Robot Framework Integrated Development Environment (RIDE)
argumentparser.py
Go to the documentation of this file.
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 
16 from robotide.lib.robot.errors import DataError
17 from robotide.lib.robot.utils import JYTHON, PY_VERSION, PY2
18 from robotide.lib.robot.variables import is_dict_var, is_list_var, is_scalar_var
19 
20 from .argumentspec import ArgumentSpec
21 
22 
23 if PY2:
24  from inspect import getargspec, ismethod
25 
26  def getfullargspec(func):
27  return getargspec(func) + ([], None, {})
28 else:
29  from inspect import getfullargspec, ismethod
30 
31 if PY_VERSION >= (3, 5):
32  import typing
33 else:
34  typing = None
35 
36 if JYTHON:
37  from java.lang import Class
38  from java.util import List, Map
39 
40 
42 
43  def __init__(self, type='Keyword'):
44  self._type_type = type
45 
46  def parse(self, source, name=None):
47  raise NotImplementedError
48 
49 
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_type,
60  positional=args,
61  varargs=varargs,
62  kwargs=kwargs,
63  kwonlyargs=kwonly,
64  defaults=self._get_defaults_get_defaults(args, defaults, kwonlydefaults)
65  )
66  spec.types = self._get_types_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_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_remove_mismatching_type_hints(type_hints, spec.argument_names)
94  self._remove_optional_none_type_hints_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_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 
123 
124  def parse(self, signatures, name=None):
125  if not signatures:
126  return self._no_signatures_arg_spec_no_signatures_arg_spec(name)
127  elif len(signatures) == 1:
128  return self._single_signature_arg_spec_single_signature_arg_spec(signatures[0], name)
129  else:
130  return self._multi_signature_arg_spec_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_format_arg_spec(name)
135 
136  def _single_signature_arg_spec(self, signature, name):
137  varargs, kwargs = self._get_varargs_and_kwargs_support_get_varargs_and_kwargs_support(signature.args)
138  positional = len(signature.args) - int(varargs) - int(kwargs)
139  return self._format_arg_spec_format_arg_spec(name, positional, varargs=varargs,
140  kwargs=kwargs)
141 
143  if not args:
144  return False, False
145  if self._is_varargs_type_is_varargs_type(args[-1]):
146  return True, False
147  if not self._is_kwargs_type_is_kwargs_type(args[-1]):
148  return False, False
149  if len(args) > 1 and self._is_varargs_type_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_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_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 
183 
184  def parse(self, argspec, name=None):
185  spec = ArgumentSpec(name, self._type_type)
186  kw_only_args = False
187  for arg in argspec:
188  if spec.kwargs:
189  self._raise_invalid_spec_raise_invalid_spec('Only last argument can be kwargs.')
190  elif self._is_kwargs_is_kwargs(arg):
191  self._add_kwargs_add_kwargs(spec, arg)
192  elif self._is_kw_only_separator_is_kw_only_separator(arg):
193  if spec.varargs or kw_only_args:
194  self._raise_invalid_spec_raise_invalid_spec('Cannot have multiple varargs.')
195  kw_only_args = True
196  elif self._is_varargs_is_varargs(arg):
197  if spec.varargs or kw_only_args:
198  self._raise_invalid_spec_raise_invalid_spec('Cannot have multiple varargs.')
199  self._add_varargs_add_varargs(spec, arg)
200  kw_only_args = True
201  elif '=' in arg:
202  self._add_arg_with_default_add_arg_with_default(spec, arg, kw_only_args)
203  elif spec.defaults and not kw_only_args:
204  self._raise_invalid_spec_raise_invalid_spec('Non-default argument after default '
205  'arguments.')
206  else:
207  self._add_arg_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_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_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_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_format_arg(arg)
244  target = spec.positional if not kw_only_arg else spec.kwonlyargs
245  target.append(arg)
246  return arg
247 
248 
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_is_kwargs_is_kwargs(arg)
262 
263  def _format_varargs(self, varargs):
264  return varargs[1:]
265 
266 
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_raise_invalid_spec("Invalid argument syntax '%s'." % arg)
287  return arg[2:-1]
Used when variable does not exist.
Definition: errors.py:67
def _format_arg_spec(self, name, positional=0, defaults=0, varargs=False, kwargs=False)
def _get_defaults(self, args, default_values, kwonlydefaults)