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|Sequence|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: "Mapping|Sequence|None") -> "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, args, named_args=None, 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(args, named_args, 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) 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