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 robot.errors import DataError 17from robot.utils import JYTHON, PY_VERSION, PY2 18from robot.variables import is_dict_var, is_list_var, is_scalar_var 19 20from .argumentspec import ArgumentSpec 21 22 23if PY2: 24 from inspect import getargspec, ismethod 25 26 def getfullargspec(func): 27 return getargspec(func) + ([], None, {}) 28else: 29 from inspect import getfullargspec, ismethod 30 31if PY_VERSION >= (3, 5): 32 import typing 33else: 34 typing = None 35 36if JYTHON: 37 from java.lang import Class 38 from java.util import List, Map 39 40 41class _ArgumentParser(object): 42 43 def __init__(self, type='Keyword'): 44 self._type = type 45 46 def parse(self, source, name=None): 47 raise NotImplementedError 48 49 50class PythonArgumentParser(_ArgumentParser): 51 52 def parse(self, handler, name=None): 53 args, varargs, kwargs, defaults, kwonly, kwonlydefaults, annotations \ 54 = getfullargspec(handler) 55 if ismethod(handler) or handler.__name__ == '__init__': 56 args = args[1:] # drop 'self' 57 spec = ArgumentSpec( 58 name, 59 self._type, 60 positional=args, 61 varargs=varargs, 62 kwargs=kwargs, 63 kwonlyargs=kwonly, 64 defaults=self._get_defaults(args, defaults, kwonlydefaults) 65 ) 66 spec.types = self._get_types(handler, annotations, spec) 67 return spec 68 69 def _get_defaults(self, args, default_values, kwonlydefaults): 70 if default_values: 71 defaults = dict(zip(args[-len(default_values):], default_values)) 72 else: 73 defaults = {} 74 if kwonlydefaults: 75 defaults.update(kwonlydefaults) 76 return defaults 77 78 def _get_types(self, handler, annotations, spec): 79 types = getattr(handler, 'robot_types', ()) 80 if types is None: 81 return None 82 if types: 83 return types 84 return self._get_type_hints(handler, annotations, spec) 85 86 def _get_type_hints(self, handler, annotations, spec): 87 if not typing: 88 return annotations 89 try: 90 type_hints = typing.get_type_hints(handler) 91 except Exception: # Can raise pretty much anything 92 return annotations 93 self._remove_mismatching_type_hints(type_hints, spec.argument_names) 94 self._remove_optional_none_type_hints(type_hints, spec.defaults) 95 return type_hints 96 97 def _remove_mismatching_type_hints(self, type_hints, argument_names): 98 # typing.get_type_hints returns info from the original function even 99 # if it is decorated. Argument names are got from the wrapping 100 # decorator and thus there is a mismatch that needs to be resolved. 101 mismatch = set(type_hints) - set(argument_names) 102 for name in mismatch: 103 type_hints.pop(name) 104 105 def _remove_optional_none_type_hints(self, type_hints, defaults): 106 # If argument has None as a default, typing.get_type_hints adds 107 # optional None to the information it returns. We don't want that. 108 for arg in defaults: 109 if defaults[arg] is None and arg in type_hints: 110 type_ = type_hints[arg] 111 if self._is_union(type_): 112 types = type_.__args__ 113 if len(types) == 2 and types[1] is type(None): 114 type_hints[arg] = types[0] 115 116 def _is_union(self, type_): 117 if PY_VERSION >= (3, 7) and hasattr(type_, '__origin__'): 118 type_ = type_.__origin__ 119 return isinstance(type_, type(typing.Union)) 120 121 122class JavaArgumentParser(_ArgumentParser): 123 124 def parse(self, signatures, name=None): 125 if not signatures: 126 return self._no_signatures_arg_spec(name) 127 elif len(signatures) == 1: 128 return self._single_signature_arg_spec(signatures[0], name) 129 else: 130 return self._multi_signature_arg_spec(signatures, name) 131 132 def _no_signatures_arg_spec(self, name): 133 # Happens when a class has no public constructors 134 return self._format_arg_spec(name) 135 136 def _single_signature_arg_spec(self, signature, name): 137 varargs, kwargs = self._get_varargs_and_kwargs_support(signature.args) 138 positional = len(signature.args) - int(varargs) - int(kwargs) 139 return self._format_arg_spec(name, positional, varargs=varargs, 140 kwargs=kwargs) 141 142 def _get_varargs_and_kwargs_support(self, args): 143 if not args: 144 return False, False 145 if self._is_varargs_type(args[-1]): 146 return True, False 147 if not self._is_kwargs_type(args[-1]): 148 return False, False 149 if len(args) > 1 and self._is_varargs_type(args[-2]): 150 return True, True 151 return False, True 152 153 def _is_varargs_type(self, arg): 154 return arg is List or isinstance(arg, Class) and arg.isArray() 155 156 def _is_kwargs_type(self, arg): 157 return arg is Map 158 159 def _multi_signature_arg_spec(self, signatures, name): 160 mina = maxa = len(signatures[0].args) 161 for sig in signatures[1:]: 162 argc = len(sig.args) 163 mina = min(argc, mina) 164 maxa = max(argc, maxa) 165 return self._format_arg_spec(name, maxa, maxa-mina) 166 167 def _format_arg_spec(self, name, positional=0, defaults=0, varargs=False, 168 kwargs=False): 169 positional = ['arg%d' % (i+1) for i in range(positional)] 170 if defaults: 171 defaults = {name: '' for name in positional[-defaults:]} 172 else: 173 defaults = {} 174 return ArgumentSpec(name, self._type, 175 positional=positional, 176 varargs='varargs' if varargs else None, 177 kwargs='kwargs' if kwargs else None, 178 defaults=defaults, 179 supports_named=False) 180 181 182class _ArgumentSpecParser(_ArgumentParser): 183 184 def parse(self, argspec, name=None): 185 spec = ArgumentSpec(name, self._type) 186 kw_only_args = False 187 for arg in argspec: 188 if spec.kwargs: 189 self._raise_invalid_spec('Only last argument can be kwargs.') 190 elif self._is_kwargs(arg): 191 self._add_kwargs(spec, arg) 192 elif self._is_kw_only_separator(arg): 193 if spec.varargs or kw_only_args: 194 self._raise_invalid_spec('Cannot have multiple varargs.') 195 kw_only_args = True 196 elif self._is_varargs(arg): 197 if spec.varargs or kw_only_args: 198 self._raise_invalid_spec('Cannot have multiple varargs.') 199 self._add_varargs(spec, arg) 200 kw_only_args = True 201 elif '=' in arg: 202 self._add_arg_with_default(spec, arg, kw_only_args) 203 elif spec.defaults and not kw_only_args: 204 self._raise_invalid_spec('Non-default argument after default ' 205 'arguments.') 206 else: 207 self._add_arg(spec, arg, kw_only_args) 208 return spec 209 210 def _raise_invalid_spec(self, error): 211 raise DataError('Invalid argument specification: %s' % error) 212 213 def _is_kwargs(self, arg): 214 raise NotImplementedError 215 216 def _add_kwargs(self, spec, kwargs): 217 spec.kwargs = self._format_kwargs(kwargs) 218 219 def _format_kwargs(self, kwargs): 220 raise NotImplementedError 221 222 def _is_kw_only_separator(self, arg): 223 raise NotImplementedError 224 225 def _is_varargs(self, arg): 226 raise NotImplementedError 227 228 def _add_varargs(self, spec, varargs): 229 spec.varargs = self._format_varargs(varargs) 230 231 def _format_varargs(self, varargs): 232 raise NotImplementedError 233 234 def _add_arg_with_default(self, spec, arg, kw_only_arg=False): 235 arg, default = arg.split('=', 1) 236 arg = self._add_arg(spec, arg, kw_only_arg) 237 spec.defaults[arg] = default 238 239 def _format_arg(self, arg): 240 return arg 241 242 def _add_arg(self, spec, arg, kw_only_arg=False): 243 arg = self._format_arg(arg) 244 target = spec.positional if not kw_only_arg else spec.kwonlyargs 245 target.append(arg) 246 return arg 247 248 249class DynamicArgumentParser(_ArgumentSpecParser): 250 251 def _is_kwargs(self, arg): 252 return arg.startswith('**') 253 254 def _format_kwargs(self, kwargs): 255 return kwargs[2:] 256 257 def _is_kw_only_separator(self, arg): 258 return arg == '*' 259 260 def _is_varargs(self, arg): 261 return arg.startswith('*') and not self._is_kwargs(arg) 262 263 def _format_varargs(self, varargs): 264 return varargs[1:] 265 266 267class UserKeywordArgumentParser(_ArgumentSpecParser): 268 269 def _is_kwargs(self, arg): 270 return is_dict_var(arg) 271 272 def _format_kwargs(self, kwargs): 273 return kwargs[2:-1] 274 275 def _is_varargs(self, arg): 276 return is_list_var(arg) 277 278 def _format_varargs(self, varargs): 279 return varargs[2:-1] 280 281 def _is_kw_only_separator(self, arg): 282 return arg == '@{}' 283 284 def _format_arg(self, arg): 285 if not is_scalar_var(arg): 286 self._raise_invalid_spec("Invalid argument syntax '%s'." % arg) 287 return arg[2:-1] 288