# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from copy import copy
import inspect
from robot.utils import (getdoc, getshortdoc, is_java_init, is_java_method,
is_list_like, normpath, printable_name,
split_tags_from_doc, type_name, unwrap)
from robot.errors import DataError
from robot.model import Tags
from .arguments import (ArgumentSpec, DynamicArgumentParser,
JavaArgumentCoercer, JavaArgumentParser,
PythonArgumentParser)
from .dynamicmethods import GetKeywordSource, GetKeywordTypes
from .librarykeywordrunner import (EmbeddedArgumentsRunner,
LibraryKeywordRunner, RunKeywordRunner)
from .runkwregister import RUN_KW_REGISTER
[docs]def Handler(library, name, method):
if RUN_KW_REGISTER.is_run_keyword(library.orig_name, name):
return _RunKeywordHandler(library, name, method)
if is_java_method(method):
return _JavaHandler(library, name, method)
else:
return _PythonHandler(library, name, method)
[docs]def DynamicHandler(library, name, method, doc, argspec, tags=None):
if RUN_KW_REGISTER.is_run_keyword(library.orig_name, name):
return _DynamicRunKeywordHandler(library, name, method, doc, argspec, tags)
return _DynamicHandler(library, name, method, doc, argspec, tags)
[docs]def InitHandler(library, method=None, docgetter=None):
Init = _PythonInitHandler if not is_java_init(method) else _JavaInitHandler
return Init(library, '__init__', method, docgetter)
class _RunnableHandler(object):
def __init__(self, library, handler_name, handler_method, doc='', tags=None):
self.library = library
self._handler_name = handler_name
self.name = self._get_name(handler_name, handler_method)
self.arguments = self._parse_arguments(handler_method)
self._method = self._get_initial_handler(library, handler_name,
handler_method)
doc, tags_from_doc = split_tags_from_doc(doc or '')
tags_from_attr = self._get_tags_from_attribute(handler_method)
self._doc = doc
self.tags = Tags(tuple(tags_from_doc) +
tuple(tags_from_attr) +
tuple(tags or ()))
def _get_name(self, handler_name, handler_method):
robot_name = getattr(handler_method, 'robot_name', None)
name = robot_name or printable_name(handler_name, code_style=True)
if not name:
raise DataError('Keyword name cannot be empty.')
return name
def _parse_arguments(self, handler_method):
raise NotImplementedError
def _get_tags_from_attribute(self, handler_method):
tags = getattr(handler_method, 'robot_tags', ())
if not is_list_like(tags):
raise DataError("Expected tags to be list-like, got %s."
% type_name(tags))
return tags
def _get_initial_handler(self, library, name, method):
if library.scope.is_global:
return self._get_global_handler(method, name)
return None
def resolve_arguments(self, args, variables=None):
return self.arguments.resolve(args, variables)
@property
def doc(self):
return self._doc
@property
def longname(self):
return '%s.%s' % (self.library.name, self.name)
@property
def shortdoc(self):
return getshortdoc(self.doc)
@property
def libname(self):
return self.library.name
@property
def source(self):
return self.library.source
@property
def lineno(self):
return -1
def create_runner(self, name):
return LibraryKeywordRunner(self)
def current_handler(self):
if self._method:
return self._method
return self._get_handler(self.library.get_instance(), self._handler_name)
def _get_global_handler(self, method, name):
return method
def _get_handler(self, lib_instance, handler_name):
try:
return getattr(lib_instance, handler_name)
except AttributeError:
# Occurs with old-style classes.
if handler_name == '__init__':
return None
raise
class _PythonHandler(_RunnableHandler):
def __init__(self, library, handler_name, handler_method):
_RunnableHandler.__init__(self, library, handler_name, handler_method,
getdoc(handler_method))
def _parse_arguments(self, handler_method):
return PythonArgumentParser().parse(handler_method, self.longname)
@property
def source(self):
handler = self.current_handler()
# `getsourcefile` can return None and raise TypeError.
try:
source = inspect.getsourcefile(unwrap(handler))
except TypeError:
source = None
return normpath(source) if source else self.library.source
@property
def lineno(self):
handler = self.current_handler()
try:
lines, start_lineno = inspect.getsourcelines(unwrap(handler))
except (TypeError, OSError, IOError):
return -1
for increment, line in enumerate(lines):
if line.strip().startswith('def '):
return start_lineno + increment
return start_lineno
class _JavaHandler(_RunnableHandler):
def __init__(self, library, handler_name, handler_method):
_RunnableHandler.__init__(self, library, handler_name, handler_method)
signatures = self._get_signatures(handler_method)
self._arg_coercer = JavaArgumentCoercer(signatures, self.arguments)
def _parse_arguments(self, handler_method):
signatures = self._get_signatures(handler_method)
return JavaArgumentParser().parse(signatures, self.longname)
def _get_signatures(self, handler):
code_object = getattr(handler, 'im_func', handler)
return code_object.argslist[:code_object.nargs]
def resolve_arguments(self, args, variables=None):
positional, named = self.arguments.resolve(args, variables,
dict_to_kwargs=True)
arguments = self._arg_coercer.coerce(positional, named,
dryrun=not variables)
return arguments, []
class _DynamicHandler(_RunnableHandler):
def __init__(self, library, handler_name, dynamic_method, doc='',
argspec=None, tags=None):
self._argspec = argspec
self._run_keyword_method_name = dynamic_method.name
self._supports_kwargs = dynamic_method.supports_kwargs
_RunnableHandler.__init__(self, library, handler_name,
dynamic_method.method, doc, tags)
self._source_info = None
def _parse_arguments(self, handler_method):
spec = DynamicArgumentParser().parse(self._argspec, self.longname)
if not self._supports_kwargs:
if spec.kwargs:
raise DataError("Too few '%s' method parameters for **kwargs "
"support." % self._run_keyword_method_name)
if spec.kwonlyargs:
raise DataError("Too few '%s' method parameters for "
"keyword-only arguments support."
% self._run_keyword_method_name)
get_keyword_types = GetKeywordTypes(self.library.get_instance())
spec.types = get_keyword_types(self._handler_name)
return spec
@property
def source(self):
if self._source_info is None:
self._source_info = self._get_source_info()
return self._source_info[0]
def _get_source_info(self):
get_keyword_source = GetKeywordSource(self.library.get_instance())
try:
source = get_keyword_source(self._handler_name)
except DataError as err:
self.library.report_error(
"Getting source information for keyword '%s' failed: %s"
% (self.name, err.message), err.details
)
return None, -1
if not source:
return self.library.source, -1
if ':' not in source:
return source, -1
path, lineno = source.rsplit(':', 1)
try:
return path or self.library.source, int(lineno)
except ValueError:
return source, -1
@property
def lineno(self):
if self._source_info is None:
self._source_info = self._get_source_info()
return self._source_info[1]
def resolve_arguments(self, arguments, variables=None):
positional, named = self.arguments.resolve(arguments, variables)
if not self._supports_kwargs:
positional, named = self.arguments.map(positional, named)
return positional, named
def _get_handler(self, lib_instance, handler_name):
runner = getattr(lib_instance, self._run_keyword_method_name)
return self._get_dynamic_handler(runner, handler_name)
def _get_global_handler(self, method, name):
return self._get_dynamic_handler(method, name)
def _get_dynamic_handler(self, runner, name):
def handler(*positional, **kwargs):
if self._supports_kwargs:
return runner(name, positional, kwargs)
else:
return runner(name, positional)
return handler
class _RunKeywordHandler(_PythonHandler):
def create_runner(self, name):
default_dry_run_keywords = ('name' in self.arguments.positional and
self._args_to_process)
return RunKeywordRunner(self, default_dry_run_keywords)
@property
def _args_to_process(self):
return RUN_KW_REGISTER.get_args_to_process(self.library.orig_name,
self.name)
def resolve_arguments(self, args, variables=None):
args_to_process = self._args_to_process
return self.arguments.resolve(args, variables, resolve_named=False,
resolve_variables_until=args_to_process)
class _DynamicRunKeywordHandler(_DynamicHandler, _RunKeywordHandler):
_parse_arguments = _RunKeywordHandler._parse_arguments
resolve_arguments = _RunKeywordHandler.resolve_arguments
class _PythonInitHandler(_PythonHandler):
def __init__(self, library, handler_name, handler_method, docgetter):
_PythonHandler.__init__(self, library, handler_name, handler_method)
self._docgetter = docgetter
@property
def doc(self):
if self._docgetter:
self._doc = self._docgetter() or self._doc
self._docgetter = None
return self._doc
def _parse_arguments(self, init_method):
parser = PythonArgumentParser(type='Test Library')
return parser.parse(init_method or (lambda: None), self.library.name)
class _JavaInitHandler(_JavaHandler):
def __init__(self, library, handler_name, handler_method, docgetter):
_JavaHandler.__init__(self, library, handler_name, handler_method)
self._docgetter = docgetter
@property
def doc(self):
if self._docgetter:
self._doc = self._docgetter() or self._doc
self._docgetter = None
return self._doc
def _parse_arguments(self, handler_method):
parser = JavaArgumentParser(type='Test Library')
signatures = self._get_signatures(handler_method)
return parser.parse(signatures, self.library.name)
[docs]class EmbeddedArgumentsHandler(object):
def __init__(self, name_regexp, orig_handler):
self.arguments = ArgumentSpec() # Show empty argument spec for Libdoc
self.name_regexp = name_regexp
self._orig_handler = orig_handler
def __getattr__(self, item):
return getattr(self._orig_handler, item)
@property
def library(self):
return self._orig_handler.library
@library.setter
def library(self, library):
self._orig_handler.library = library
[docs] def matches(self, name):
return self.name_regexp.match(name) is not None
[docs] def create_runner(self, name):
return EmbeddedArgumentsRunner(self, name)
def __copy__(self):
orig_handler = copy(self._orig_handler)
return EmbeddedArgumentsHandler(self.name_regexp, orig_handler)