Source code for robot.running.arguments.argumentspec

#  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.

import sys
from enum import Enum
from typing import Any, Callable, Iterator, Mapping, Sequence

from robot.utils import NOT_SET, safe_str, setter, SetterAwareType

from .argumentconverter import ArgumentConverter
from .argumentmapper import ArgumentMapper
from .argumentresolver import ArgumentResolver
from .typeinfo import TypeInfo
from .typevalidator import TypeValidator


[docs] class ArgumentSpec(metaclass=SetterAwareType): __slots__ = ['_name', 'type', 'positional_only', 'positional_or_named', 'var_positional', 'named_only', 'var_named', 'embedded', 'defaults'] def __init__(self, name: 'str|Callable[[], str]|None' = None, type: str = 'Keyword', positional_only: Sequence[str] = (), positional_or_named: Sequence[str] = (), var_positional: 'str|None' = None, named_only: Sequence[str] = (), var_named: 'str|None' = None, defaults: 'Mapping[str, Any]|None' = None, embedded: Sequence[str] = (), types: 'Mapping[str, TypeInfo]|None' = None, return_type: 'TypeInfo|None' = None): self.name = name self.type = type self.positional_only = tuple(positional_only) self.positional_or_named = tuple(positional_or_named) self.var_positional = var_positional self.named_only = tuple(named_only) self.var_named = var_named self.embedded = tuple(embedded) self.defaults = defaults or {} self.types = types self.return_type = return_type @property def name(self) -> 'str|None': return self._name if not callable(self._name) else self._name() @name.setter def name(self, name: 'str|Callable[[], str]|None'): self._name = name @setter def types(self, types) -> 'dict[str, TypeInfo]|None': return TypeValidator(self).validate(types) @setter def return_type(self, hint) -> 'TypeInfo|None': if hint in (None, type(None)): return None if isinstance(hint, TypeInfo): return hint return TypeInfo.from_type_hint(hint) @property def positional(self) -> 'tuple[str, ...]': return self.positional_only + self.positional_or_named @property def named(self) -> 'tuple[str, ...]': return self.named_only + self.positional_or_named @property def minargs(self) -> int: return len([arg for arg in self.positional if arg not in self.defaults]) @property def maxargs(self) -> int: return len(self.positional) if not self.var_positional else sys.maxsize @property def argument_names(self) -> 'tuple[str, ...]': var_positional = (self.var_positional,) if self.var_positional else () var_named = (self.var_named,) if self.var_named else () return (self.positional_only + self.positional_or_named + var_positional + self.named_only + var_named)
[docs] def resolve(self, arguments, variables=None, converters=None, resolve_named=True, resolve_args_until=None, dict_to_kwargs=False, languages=None) -> 'tuple[list, list]': resolver = ArgumentResolver(self, resolve_named, resolve_args_until, dict_to_kwargs) positional, named = resolver.resolve(arguments, variables) return self.convert(positional, named, converters, dry_run=not variables, languages=languages)
[docs] def convert(self, positional, named, converters=None, dry_run=False, languages=None) -> 'tuple[list, list]': if self.types or self.defaults: converter = ArgumentConverter(self, converters, dry_run, languages) positional, named = converter.convert(positional, named) return positional, named
[docs] def map(self, positional, named, replace_defaults=True) -> 'tuple[list, list]': mapper = ArgumentMapper(self) return mapper.map(positional, named, replace_defaults)
[docs] def copy(self) -> 'ArgumentSpec': types = dict(self.types) if self.types is not None else None return type(self)(self.name, self.type, self.positional_only, self.positional_or_named, self.var_positional, self.named_only, self.var_named, dict(self.defaults), self.embedded, types, self.return_type)
def __iter__(self) -> Iterator['ArgInfo']: get_type = (self.types or {}).get get_default = self.defaults.get for arg in self.positional_only: yield ArgInfo(ArgInfo.POSITIONAL_ONLY, arg, get_type(arg), get_default(arg, NOT_SET)) if self.positional_only: yield ArgInfo(ArgInfo.POSITIONAL_ONLY_MARKER) for arg in self.positional_or_named: yield ArgInfo(ArgInfo.POSITIONAL_OR_NAMED, arg, get_type(arg), get_default(arg, NOT_SET)) if self.var_positional: yield ArgInfo(ArgInfo.VAR_POSITIONAL, self.var_positional, get_type(self.var_positional)) elif self.named_only: yield ArgInfo(ArgInfo.NAMED_ONLY_MARKER) for arg in self.named_only: yield ArgInfo(ArgInfo.NAMED_ONLY, arg, get_type(arg), get_default(arg, NOT_SET)) if self.var_named: yield ArgInfo(ArgInfo.VAR_NAMED, self.var_named, get_type(self.var_named)) def __bool__(self): return any([self.positional_only, self.positional_or_named, self.var_positional, self.named_only, self.var_named, self.return_type]) def __str__(self): return ', '.join(str(arg) for arg in self)
[docs] class ArgInfo: """Contains argument information. Only used by Libdoc.""" POSITIONAL_ONLY = 'POSITIONAL_ONLY' POSITIONAL_ONLY_MARKER = 'POSITIONAL_ONLY_MARKER' POSITIONAL_OR_NAMED = 'POSITIONAL_OR_NAMED' VAR_POSITIONAL = 'VAR_POSITIONAL' NAMED_ONLY_MARKER = 'NAMED_ONLY_MARKER' NAMED_ONLY = 'NAMED_ONLY' VAR_NAMED = 'VAR_NAMED' def __init__(self, kind: str, name: str = '', type: 'TypeInfo|None' = None, default: Any = NOT_SET): self.kind = kind self.name = name self.type = type or TypeInfo() self.default = default @property def required(self) -> bool: if self.kind in (self.POSITIONAL_ONLY, self.POSITIONAL_OR_NAMED, self.NAMED_ONLY): return self.default is NOT_SET return False @property def default_repr(self) -> 'str|None': if self.default is NOT_SET: return None if isinstance(self.default, Enum): return self.default.name return safe_str(self.default) def __str__(self): if self.kind == self.POSITIONAL_ONLY_MARKER: return '/' if self.kind == self.NAMED_ONLY_MARKER: return '*' ret = self.name if self.kind == self.VAR_POSITIONAL: ret = '*' + ret elif self.kind == self.VAR_NAMED: ret = '**' + ret if self.type: ret = f'{ret}: {self.type}' default_sep = ' = ' else: default_sep = '=' if self.default is not NOT_SET: ret = f'{ret}{default_sep}{self.default_repr}' return ret