Robot Framework
customconverters.py
Go to the documentation of this file.
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 
16 from robot.utils import getdoc, is_union, seq2str, type_name
17 
18 from .argumentparser import PythonArgumentParser
19 
20 
22 
23  def __init__(self, converters):
24  self.convertersconverters = converters
25 
26  @classmethod
27  def from_dict(cls, converters, error_reporter):
28  valid = []
29  for type_, conv in converters.items():
30  try:
31  info = ConverterInfo.for_converter(type_, conv)
32  except TypeError as err:
33  error_reporter(str(err))
34  else:
35  valid.append(info)
36  return cls(valid)
37 
38  def get_converter_info(self, type_):
39  if isinstance(type_, type):
40  for conv in self.convertersconverters:
41  if issubclass(type_, conv.type):
42  return conv
43  return None
44 
45  def __iter__(self):
46  return iter(self.convertersconverters)
47 
48  def __len__(self):
49  return len(self.convertersconverters)
50 
51 
53 
54  def __init__(self, type, converter, value_types):
55  self.typetype = type
56  self.converterconverter = converter
57  self.value_typesvalue_types = value_types
58 
59  @property
60  name = property
61 
62  def name(self):
63  return type_name(self.typetype)
64 
65  @property
66  doc = property
67 
68  def doc(self):
69  return getdoc(self.converterconverter) or getdoc(self.typetype)
70 
71  @classmethod
72  def for_converter(cls, type_, converter):
73  if not isinstance(type_, type):
74  raise TypeError(f'Custom converters must be specified using types, '
75  f'got {type_name(type_)} {type_!r}.')
76  if converter is None:
77  def converter(arg):
78  raise TypeError(f'Only {type_.__name__} instances are accepted, '
79  f'got {type_name(arg)}.')
80  if not callable(converter):
81  raise TypeError(f'Custom converters must be callable, converter for '
82  f'{type_name(type_)} is {type_name(converter)}.')
83  arg_type = cls._get_arg_type_get_arg_type(converter)
84  if arg_type is None:
85  accepts = ()
86  elif is_union(arg_type):
87  accepts = arg_type.__args__
88  elif hasattr(arg_type, '__origin__'):
89  accepts = (arg_type.__origin__,)
90  else:
91  accepts = (arg_type,)
92  return cls(type_, converter, accepts)
93 
94  @classmethod
95  def _get_arg_type(cls, converter):
96  spec = PythonArgumentParser(type='Converter').parse(converter)
97  if spec.minargs > 1:
98  required = seq2str([a for a in spec.positional if a not in spec.defaults])
99  raise TypeError(f"Custom converters cannot have more than one mandatory "
100  f"argument, '{converter.__name__}' has {required}.")
101  if not spec.positional:
102  raise TypeError(f"Custom converters must accept one positional argument, "
103  f"'{converter.__name__}' accepts none.")
104  if spec.named_only and set(spec.named_only) - set(spec.defaults):
105  required = seq2str(sorted(set(spec.named_only) - set(spec.defaults)))
106  raise TypeError(f"Custom converters cannot have mandatory keyword-only "
107  f"arguments, '{converter.__name__}' has {required}.")
108  return spec.types.get(spec.positional[0])
def __init__(self, type, converter, value_types)
def seq2str(sequence, quote="'", sep=', ', lastsep=' and ')
Returns sequence in format ‘'item 1’, 'item 2' and 'item 3'`.
Definition: misc.py:79
def type_name(item, capitalize=False)
Return "non-technical" type name for objects and types.
Definition: robottypes.py:86
def is_union(item, allow_tuple=False)
Definition: robottypes.py:76
def getdoc(item)
Definition: text.py:180