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 plural_or_not, seq2str 18from robot.variables import is_list_var 19 20 21class ArgumentValidator(object): 22 23 def __init__(self, argspec): 24 """:type argspec: :py:class:`robot.running.arguments.ArgumentSpec`""" 25 self._argspec = argspec 26 27 def validate(self, positional, named, dryrun=False): 28 if dryrun and any(is_list_var(arg) for arg in positional): 29 return 30 named = set(name for name, value in named) 31 self._validate_no_multiple_values(positional, named, self._argspec) 32 self._validate_positional_limits(positional, named, self._argspec) 33 self._validate_no_mandatory_missing(positional, named, self._argspec) 34 self._validate_no_named_only_missing(named, self._argspec) 35 self._validate_no_extra_named(named, self._argspec) 36 37 def _validate_positional_limits(self, positional, named, spec): 38 count = len(positional) + self._named_positionals(named, spec) 39 if not spec.minargs <= count <= spec.maxargs: 40 self._raise_wrong_count(count, spec) 41 42 def _named_positionals(self, named, spec): 43 if not spec.supports_named: 44 return 0 45 return sum(1 for n in named if n in spec.positional) 46 47 def _raise_wrong_count(self, count, spec): 48 minend = plural_or_not(spec.minargs) 49 if spec.minargs == spec.maxargs: 50 expected = '%d argument%s' % (spec.minargs, minend) 51 elif not spec.varargs: 52 expected = '%d to %d arguments' % (spec.minargs, spec.maxargs) 53 else: 54 expected = 'at least %d argument%s' % (spec.minargs, minend) 55 if spec.kwargs or spec.kwonlyargs: 56 expected = expected.replace('argument', 'non-named argument') 57 raise DataError("%s '%s' expected %s, got %d." 58 % (spec.type, spec.name, expected, count)) 59 60 def _validate_no_multiple_values(self, positional, named, spec): 61 if named and spec.supports_named: 62 for name in spec.positional[:len(positional)]: 63 if name in named: 64 raise DataError("%s '%s' got multiple values for argument " 65 "'%s'." % (spec.type, spec.name, name)) 66 67 def _validate_no_mandatory_missing(self, positional, named, spec): 68 for name in spec.positional[len(positional):spec.minargs]: 69 if name not in named: 70 raise DataError("%s '%s' missing value for argument '%s'." 71 % (spec.type, spec.name, name)) 72 73 def _validate_no_named_only_missing(self, named, spec): 74 defined = set(named) | set(spec.defaults) 75 missing = [arg for arg in spec.kwonlyargs if arg not in defined] 76 if missing: 77 raise DataError("%s '%s' missing named-only argument%s %s." 78 % (spec.type, spec.name, plural_or_not(missing), 79 seq2str(sorted(missing)))) 80 81 def _validate_no_extra_named(self, named, spec): 82 if not spec.kwargs: 83 extra = set(named) - set(spec.positional) - set(spec.kwonlyargs) 84 if extra: 85 raise DataError("%s '%s' got unexpected named argument%s %s." 86 % (spec.type, spec.name, plural_or_not(extra), 87 seq2str(sorted(extra)))) 88