Source code for robot.running.arguments.argumentresolver

#  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 typing import TYPE_CHECKING

from robot.errors import DataError
from robot.utils import is_dict_like, is_list_like, split_from_equals
from robot.variables import is_dict_variable

from .argumentvalidator import ArgumentValidator

if TYPE_CHECKING:
    from .argumentspec import ArgumentSpec


[docs] class ArgumentResolver: def __init__(self, spec: 'ArgumentSpec', resolve_named: bool = True, resolve_args_until: 'int|None' = None, dict_to_kwargs: bool = False): self.named_resolver = NamedArgumentResolver(spec) \ if resolve_named else NullNamedArgumentResolver() self.variable_replacer = VariableReplacer(spec, resolve_args_until) self.dict_to_kwargs = DictToKwargs(spec, dict_to_kwargs) self.argument_validator = ArgumentValidator(spec)
[docs] def resolve(self, arguments, variables=None): if len(arguments) == 2 and is_list_like(arguments[0]) and is_dict_like(arguments[1]): positional = list(arguments[0]) named = list(arguments[1].items()) else: positional, named = self.named_resolver.resolve(arguments, variables) positional, named = self.variable_replacer.replace(positional, named, variables) positional, named = self.dict_to_kwargs.handle(positional, named) self.argument_validator.validate(positional, named, dryrun=variables is None) return positional, named
[docs] class NamedArgumentResolver: def __init__(self, spec: 'ArgumentSpec'): self.spec = spec
[docs] def resolve(self, arguments, variables=None): spec = self.spec positional = list(arguments[:len(spec.embedded)]) named = [] for arg in arguments[len(spec.embedded):]: if is_dict_variable(arg): named.append(arg) else: name, value = self._split_named(arg, named, variables, spec) if name is not None: named.append((name, value)) elif named: self._raise_positional_after_named() else: positional.append(value) return positional, named
def _split_named(self, arg, previous_named, variables, spec): if isinstance(arg, tuple): if len(arg) == 2 and isinstance(arg[0], str): return arg elif len(arg) == 1: return None, arg[0] else: return None, arg name, value = split_from_equals(arg) if value is None or not self._is_named(name, previous_named, variables, spec): return None, arg return name, value def _is_named(self, name, previous_named, variables, spec): if previous_named or spec.var_named: return True if variables: try: name = variables.replace_scalar(name) except DataError: return False return name in spec.named def _raise_positional_after_named(self): raise DataError(f"{self.spec.type.capitalize()} '{self.spec.name}' " f"got positional argument after named arguments.")
[docs] class NullNamedArgumentResolver:
[docs] def resolve(self, arguments, variables=None): return arguments, []
[docs] class DictToKwargs: def __init__(self, spec: 'ArgumentSpec', enabled: bool = False): self.maxargs = spec.maxargs self.enabled = enabled and bool(spec.var_named)
[docs] def handle(self, positional, named): if self.enabled and self._extra_arg_has_kwargs(positional, named): named = positional.pop().items() return positional, named
def _extra_arg_has_kwargs(self, positional, named): if named or len(positional) != self.maxargs + 1: return False return is_dict_like(positional[-1])
[docs] class VariableReplacer: def __init__(self, spec: 'ArgumentSpec', resolve_until: 'int|None' = None): self.spec = spec self.resolve_until = resolve_until
[docs] def replace(self, positional, named, variables=None): # `variables` is None in dry-run mode and when using Libdoc. if variables: if self.spec.embedded: embedded = len(self.spec.embedded) positional = [ variables.replace_scalar(emb) for emb in positional[:embedded] ] + variables.replace_list(positional[embedded:]) else: positional = variables.replace_list(positional, self.resolve_until) named = list(self._replace_named(named, variables.replace_scalar)) else: # If `var` isn't a tuple, it's a &{dict} variables. named = [var if isinstance(var, tuple) else (var, var) for var in named] return positional, named
def _replace_named(self, named, replace_scalar): for item in named: for name, value in self._get_replaced_named(item, replace_scalar): if not isinstance(name, str): raise DataError('Argument names must be strings.') yield name, value def _get_replaced_named(self, item, replace_scalar): if not isinstance(item, tuple): return replace_scalar(item).items() name, value = item return [(replace_scalar(name), replace_scalar(value))]