Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/serde/utils.py: 44%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2This module defines some utility functions.
3"""
5import importlib
6from collections import OrderedDict
7from itertools import zip_longest
9from serde.exceptions import MissingDependencyError
12def dict_partition(d, keyfunc, dict=OrderedDict):
13 """
14 Partition a dictionary.
16 Args:
17 d (dict): the dictionary to operate on.
18 keyfunc (function): the function to partition with. It must accept the
19 dictionary key and value as arguments, and should return a boolean.
20 dict (type): the type of dictionaries to return.
22 Return:
23 (dict, dict): all of the elements for which the key function returned
24 True, and all of the elements for which it returned False.
25 """
26 left = dict()
27 right = dict()
28 for key, value in d.items():
29 if keyfunc(key, value):
30 left[key] = value
31 else:
32 right[key] = value
33 return left, right
36def is_subclass(cls, class_or_tuple):
37 """
38 Return whether 'cls' is a derived from another class or is the same class.
40 Does not raise `TypeError` if the given `cls` is not a class.
41 """
42 try:
43 return issubclass(cls, class_or_tuple)
44 except TypeError:
45 return False
48def subclasses(cls):
49 """
50 Returns the recursed subclasses.
52 Args:
53 cls (class): the class whose subclasses we should recurse.
55 Returns:
56 list: the subclasses.
57 """
58 subs = cls.__subclasses__()
59 variants = []
60 for sub in subs:
61 variants.extend(subclasses(sub))
62 return subs + variants
65def try_lookup(name):
66 """
67 Try lookup a fully qualified Python path, importing the module if necessary.
69 Args:
70 name (str): the fully qualifed Python path. Example: 'validators.email'.
72 Returns:
73 the object at the path.
75 Raises:
76 serde.exceptions.MissingDependencyError: if the path could not be imported.
77 """
78 module, path = name.split('.', 1)
79 try:
80 obj = importlib.import_module(module)
81 except ImportError:
82 raise MissingDependencyError(
83 f"{module!r} is missing, did you forget to install the serde 'ext' feature?"
84 )
85 for attr in path.split('.'):
86 obj = getattr(obj, attr)
87 return obj
90def zip_equal(*iterables):
91 """
92 A zip function that validates that all the iterables have the same length.
94 Args:
95 *iterables: the iterables to pass to `zip_longest`.
97 Yields:
98 each zipped element.
100 Raises:
101 ValueError: if one of the iterables is the wrong length.
102 """
103 sentinel = object()
104 for element in zip_longest(*iterables, fillvalue=sentinel):
105 if sentinel in element:
106 raise ValueError('iterables have different lengths')
107 yield element
110def zip_until_right(*iterables):
111 """
112 A zip function that validates that the right iterable is consumed.
114 Args:
115 *iterables: the iterables to pass to `zip`.
117 Yields:
118 each zipped element.
120 Raises:
121 ValueError: if the left iterable is consumed before the right.
122 """
123 lefts = iterables[:-1]
124 right = iter(iterables[-1])
125 iterables = lefts + (right,)
126 for item in zip(*iterables):
127 yield item
128 try:
129 next(right)
130 raise ValueError('the right-most iterable was not consumed')
131 except StopIteration:
132 pass