Source code for robot.running.arguments.customconverters

#  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
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  See the License for the specific language governing permissions and
#  limitations under the License.

from robot.utils import getdoc, seq2str, type_name

[docs] class CustomArgumentConverters: def __init__(self, converters): self.converters = converters
[docs] @classmethod def from_dict(cls, converters, library=None): valid = [] for type_, conv in converters.items(): try: info = ConverterInfo.for_converter(type_, conv, library) except TypeError as err: if library is None: raise library.report_error(str(err)) else: valid.append(info) return cls(valid)
[docs] def get_converter_info(self, type_): if isinstance(type_, type): for conv in self.converters: if issubclass(type_, conv.type): return conv return None
def __iter__(self): return iter(self.converters) def __len__(self): return len(self.converters)
[docs] class ConverterInfo: def __init__(self, type, converter, value_types, library=None): self.type = type self.converter = converter self.value_types = value_types self.library = library @property def name(self): return type_name(self.type) @property def doc(self): return getdoc(self.converter) or getdoc(self.type)
[docs] @classmethod def for_converter(cls, type_, converter, library): if not isinstance(type_, type): raise TypeError(f'Custom converters must be specified using types, ' f'got {type_name(type_)} {type_!r}.') if converter is None: def converter(arg): raise TypeError(f'Only {type_.__name__} instances are accepted, ' f'got {type_name(arg)}.') if not callable(converter): raise TypeError(f'Custom converters must be callable, converter for ' f'{type_name(type_)} is {type_name(converter)}.') spec = cls._get_arg_spec(converter) type_info = spec.types.get(spec.positional[0] if spec.positional else spec.var_positional) if type_info is None: accepts = () elif type_info.is_union: accepts = type_info.nested else: accepts = (type_info,) accepts = tuple(info.type for info in accepts) pass_library = spec.minargs == 2 or spec.var_positional return cls(type_, converter, accepts, library if pass_library else None)
@classmethod def _get_arg_spec(cls, converter): # Avoid cyclic import. Yuck. from .argumentparser import PythonArgumentParser spec = PythonArgumentParser(type='Converter').parse(converter) if spec.minargs > 2: required = seq2str([a for a in spec.positional if a not in spec.defaults]) raise TypeError(f"Custom converters cannot have more than two mandatory " f"arguments, '{converter.__name__}' has {required}.") if not spec.maxargs: raise TypeError(f"Custom converters must accept one positional argument, " f"'{converter.__name__}' accepts none.") if spec.named_only and set(spec.named_only) - set(spec.defaults): required = seq2str(sorted(set(spec.named_only) - set(spec.defaults))) raise TypeError(f"Custom converters cannot have mandatory keyword-only " f"arguments, '{converter.__name__}' has {required}.") return spec
[docs] def convert(self, value): if not self.library: return self.converter(value) return self.converter(value, self.library.instance)