1# Copyright 2008-2015 Nokia Networks 2# Copyright 2016- Robot Framework Foundation 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16from copy import copy 17 18from robot.utils import (getdoc, getshortdoc, is_java_init, is_java_method, 19 is_list_like, printable_name, split_tags_from_doc, 20 type_name) 21from robot.errors import DataError 22from robot.model import Tags 23 24from .arguments import (ArgumentSpec, DynamicArgumentParser, 25 JavaArgumentCoercer, JavaArgumentParser, 26 PythonArgumentParser) 27from .dynamicmethods import GetKeywordTypes 28from .librarykeywordrunner import (EmbeddedArgumentsRunner, 29 LibraryKeywordRunner, RunKeywordRunner) 30from .runkwregister import RUN_KW_REGISTER 31 32 33def Handler(library, name, method): 34 if RUN_KW_REGISTER.is_run_keyword(library.orig_name, name): 35 return _RunKeywordHandler(library, name, method) 36 if is_java_method(method): 37 return _JavaHandler(library, name, method) 38 else: 39 return _PythonHandler(library, name, method) 40 41 42def DynamicHandler(library, name, method, doc, argspec, tags=None): 43 if RUN_KW_REGISTER.is_run_keyword(library.orig_name, name): 44 return _DynamicRunKeywordHandler(library, name, method, doc, argspec, tags) 45 return _DynamicHandler(library, name, method, doc, argspec, tags) 46 47 48def InitHandler(library, method, docgetter=None): 49 Init = _PythonInitHandler if not is_java_init(method) else _JavaInitHandler 50 return Init(library, '__init__', method, docgetter) 51 52 53class _RunnableHandler(object): 54 55 def __init__(self, library, handler_name, handler_method, doc='', tags=None): 56 self.library = library 57 self._handler_name = handler_name 58 self.name = self._get_name(handler_name, handler_method) 59 self.arguments = self._parse_arguments(handler_method) 60 self._method = self._get_initial_handler(library, handler_name, 61 handler_method) 62 doc, tags_from_doc = split_tags_from_doc(doc or '') 63 tags_from_attr = self._get_tags_from_attribute(handler_method) 64 self._doc = doc 65 self.tags = Tags(tuple(tags_from_doc) + 66 tuple(tags_from_attr) + 67 tuple(tags or ())) 68 69 def _get_name(self, handler_name, handler_method): 70 robot_name = getattr(handler_method, 'robot_name', None) 71 name = robot_name or printable_name(handler_name, code_style=True) 72 if not name: 73 raise DataError('Keyword name cannot be empty.') 74 return name 75 76 def _parse_arguments(self, handler_method): 77 raise NotImplementedError 78 79 def _get_tags_from_attribute(self, handler_method): 80 tags = getattr(handler_method, 'robot_tags', ()) 81 if not is_list_like(tags): 82 raise DataError("Expected tags to be list-like, got %s." 83 % type_name(tags)) 84 return tags 85 86 def _get_initial_handler(self, library, name, method): 87 if library.scope.is_global: 88 return self._get_global_handler(method, name) 89 return None 90 91 def resolve_arguments(self, args, variables=None): 92 return self.arguments.resolve(args, variables) 93 94 @property 95 def doc(self): 96 return self._doc 97 98 @property 99 def longname(self): 100 return '%s.%s' % (self.library.name, self.name) 101 102 @property 103 def shortdoc(self): 104 return getshortdoc(self.doc) 105 106 @property 107 def libname(self): 108 return self.library.name 109 110 def create_runner(self, name): 111 return LibraryKeywordRunner(self) 112 113 def current_handler(self): 114 if self._method: 115 return self._method 116 return self._get_handler(self.library.get_instance(), self._handler_name) 117 118 def _get_global_handler(self, method, name): 119 return method 120 121 def _get_handler(self, lib_instance, handler_name): 122 return getattr(lib_instance, handler_name) 123 124 125class _PythonHandler(_RunnableHandler): 126 127 def __init__(self, library, handler_name, handler_method): 128 _RunnableHandler.__init__(self, library, handler_name, handler_method, 129 getdoc(handler_method)) 130 131 def _parse_arguments(self, handler_method): 132 return PythonArgumentParser().parse(handler_method, self.longname) 133 134 135class _JavaHandler(_RunnableHandler): 136 137 def __init__(self, library, handler_name, handler_method): 138 _RunnableHandler.__init__(self, library, handler_name, handler_method) 139 signatures = self._get_signatures(handler_method) 140 self._arg_coercer = JavaArgumentCoercer(signatures, self.arguments) 141 142 def _parse_arguments(self, handler_method): 143 signatures = self._get_signatures(handler_method) 144 return JavaArgumentParser().parse(signatures, self.longname) 145 146 def _get_signatures(self, handler): 147 code_object = getattr(handler, 'im_func', handler) 148 return code_object.argslist[:code_object.nargs] 149 150 def resolve_arguments(self, args, variables=None): 151 positional, named = self.arguments.resolve(args, variables, 152 dict_to_kwargs=True) 153 arguments = self._arg_coercer.coerce(positional, named, 154 dryrun=not variables) 155 return arguments, [] 156 157 158class _DynamicHandler(_RunnableHandler): 159 160 def __init__(self, library, handler_name, dynamic_method, doc='', 161 argspec=None, tags=None): 162 self._argspec = argspec 163 self._run_keyword_method_name = dynamic_method.name 164 self._supports_kwargs = dynamic_method.supports_kwargs 165 _RunnableHandler.__init__(self, library, handler_name, 166 dynamic_method.method, doc, tags) 167 168 def _parse_arguments(self, handler_method): 169 spec = DynamicArgumentParser().parse(self._argspec, self.longname) 170 if not self._supports_kwargs: 171 if spec.kwargs: 172 raise DataError("Too few '%s' method parameters for **kwargs " 173 "support." % self._run_keyword_method_name) 174 if spec.kwonlyargs: 175 raise DataError("Too few '%s' method parameters for " 176 "keyword-only arguments support." 177 % self._run_keyword_method_name) 178 spec.types = GetKeywordTypes(self.library.get_instance())(self._handler_name) 179 return spec 180 181 def resolve_arguments(self, arguments, variables=None): 182 positional, named = self.arguments.resolve(arguments, variables) 183 if not self._supports_kwargs: 184 positional, named = self.arguments.map(positional, named) 185 return positional, named 186 187 def _get_handler(self, lib_instance, handler_name): 188 runner = getattr(lib_instance, self._run_keyword_method_name) 189 return self._get_dynamic_handler(runner, handler_name) 190 191 def _get_global_handler(self, method, name): 192 return self._get_dynamic_handler(method, name) 193 194 def _get_dynamic_handler(self, runner, name): 195 def handler(*positional, **kwargs): 196 if self._supports_kwargs: 197 return runner(name, positional, kwargs) 198 else: 199 return runner(name, positional) 200 return handler 201 202 203class _RunKeywordHandler(_PythonHandler): 204 205 def create_runner(self, name): 206 default_dry_run_keywords = ('name' in self.arguments.positional and 207 self._args_to_process) 208 return RunKeywordRunner(self, default_dry_run_keywords) 209 210 @property 211 def _args_to_process(self): 212 return RUN_KW_REGISTER.get_args_to_process(self.library.orig_name, 213 self.name) 214 215 def resolve_arguments(self, args, variables=None): 216 args_to_process = self._args_to_process 217 return self.arguments.resolve(args, variables, resolve_named=False, 218 resolve_variables_until=args_to_process) 219 220 221class _DynamicRunKeywordHandler(_DynamicHandler, _RunKeywordHandler): 222 _parse_arguments = _RunKeywordHandler._parse_arguments 223 resolve_arguments = _RunKeywordHandler.resolve_arguments 224 225 226class _PythonInitHandler(_PythonHandler): 227 228 def __init__(self, library, handler_name, handler_method, docgetter): 229 _PythonHandler.__init__(self, library, handler_name, handler_method) 230 self._docgetter = docgetter 231 232 @property 233 def doc(self): 234 if self._docgetter: 235 self._doc = self._docgetter() or self._doc 236 self._docgetter = None 237 return self._doc 238 239 def _parse_arguments(self, handler_method): 240 parser = PythonArgumentParser(type='Test Library') 241 return parser.parse(handler_method, self.library.name) 242 243 244class _JavaInitHandler(_JavaHandler): 245 246 def __init__(self, library, handler_name, handler_method, docgetter): 247 _JavaHandler.__init__(self, library, handler_name, handler_method) 248 self._docgetter = docgetter 249 250 @property 251 def doc(self): 252 if self._docgetter: 253 self._doc = self._docgetter() or self._doc 254 self._docgetter = None 255 return self._doc 256 257 def _parse_arguments(self, handler_method): 258 parser = JavaArgumentParser(type='Test Library') 259 signatures = self._get_signatures(handler_method) 260 return parser.parse(signatures, self.library.name) 261 262 263class EmbeddedArgumentsHandler(object): 264 265 def __init__(self, name_regexp, orig_handler): 266 self.arguments = ArgumentSpec() # Show empty argument spec for Libdoc 267 self.name_regexp = name_regexp 268 self._orig_handler = orig_handler 269 270 def __getattr__(self, item): 271 return getattr(self._orig_handler, item) 272 273 @property 274 def library(self): 275 return self._orig_handler.library 276 277 @library.setter 278 def library(self, library): 279 self._orig_handler.library = library 280 281 def matches(self, name): 282 return self.name_regexp.match(name) is not None 283 284 def create_runner(self, name): 285 return EmbeddedArgumentsRunner(self, name) 286 287 def __copy__(self): 288 orig_handler = copy(self._orig_handler) 289 return EmbeddedArgumentsHandler(self.name_regexp, orig_handler) 290