Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyrsistent/_transformations.py: 24%
76 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1import re
2try:
3 from inspect import Parameter, signature
4except ImportError:
5 signature = None
6 from inspect import getfullargspec
9_EMPTY_SENTINEL = object()
12def inc(x):
13 """ Add one to the current value """
14 return x + 1
17def dec(x):
18 """ Subtract one from the current value """
19 return x - 1
22def discard(evolver, key):
23 """ Discard the element and returns a structure without the discarded elements """
24 try:
25 del evolver[key]
26 except KeyError:
27 pass
30# Matchers
31def rex(expr):
32 """ Regular expression matcher to use together with transform functions """
33 r = re.compile(expr)
34 return lambda key: isinstance(key, str) and r.match(key)
37def ny(_):
38 """ Matcher that matches any value """
39 return True
42# Support functions
43def _chunks(l, n):
44 for i in range(0, len(l), n):
45 yield l[i:i + n]
48def transform(structure, transformations):
49 r = structure
50 for path, command in _chunks(transformations, 2):
51 r = _do_to_path(r, path, command)
52 return r
55def _do_to_path(structure, path, command):
56 if not path:
57 return command(structure) if callable(command) else command
59 kvs = _get_keys_and_values(structure, path[0])
60 return _update_structure(structure, kvs, path[1:], command)
63def _items(structure):
64 try:
65 return structure.items()
66 except AttributeError:
67 # Support wider range of structures by adding a transform_items() or similar?
68 return list(enumerate(structure))
71def _get(structure, key, default):
72 try:
73 if hasattr(structure, '__getitem__'):
74 return structure[key]
76 return getattr(structure, key)
78 except (IndexError, KeyError):
79 return default
82def _get_keys_and_values(structure, key_spec):
83 if callable(key_spec):
84 # Support predicates as callable objects in the path
85 arity = _get_arity(key_spec)
86 if arity == 1:
87 # Unary predicates are called with the "key" of the path
88 # - eg a key in a mapping, an index in a sequence.
89 return [(k, v) for k, v in _items(structure) if key_spec(k)]
90 elif arity == 2:
91 # Binary predicates are called with the key and the corresponding
92 # value.
93 return [(k, v) for k, v in _items(structure) if key_spec(k, v)]
94 else:
95 # Other arities are an error.
96 raise ValueError(
97 "callable in transform path must take 1 or 2 arguments"
98 )
100 # Non-callables are used as-is as a key.
101 return [(key_spec, _get(structure, key_spec, _EMPTY_SENTINEL))]
104if signature is None:
105 def _get_arity(f):
106 argspec = getfullargspec(f)
107 return len(argspec.args) - len(argspec.defaults or ())
108else:
109 def _get_arity(f):
110 return sum(
111 1
112 for p
113 in signature(f).parameters.values()
114 if p.default is Parameter.empty
115 and p.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
116 )
119def _update_structure(structure, kvs, path, command):
120 from pyrsistent._pmap import pmap
121 e = structure.evolver()
122 if not path and command is discard:
123 # Do this in reverse to avoid index problems with vectors. See #92.
124 for k, v in reversed(kvs):
125 discard(e, k)
126 else:
127 for k, v in kvs:
128 is_empty = False
129 if v is _EMPTY_SENTINEL:
130 # Allow expansion of structure but make sure to cover the case
131 # when an empty pmap is added as leaf node. See #154.
132 is_empty = True
133 v = pmap()
135 result = _do_to_path(v, path, command)
136 if result is not v or is_empty:
137 e[k] = result
139 return e.persistent()