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

1import re 

2try: 

3 from inspect import Parameter, signature 

4except ImportError: 

5 signature = None 

6 from inspect import getfullargspec 

7 

8 

9_EMPTY_SENTINEL = object() 

10 

11 

12def inc(x): 

13 """ Add one to the current value """ 

14 return x + 1 

15 

16 

17def dec(x): 

18 """ Subtract one from the current value """ 

19 return x - 1 

20 

21 

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 

28 

29 

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) 

35 

36 

37def ny(_): 

38 """ Matcher that matches any value """ 

39 return True 

40 

41 

42# Support functions 

43def _chunks(l, n): 

44 for i in range(0, len(l), n): 

45 yield l[i:i + n] 

46 

47 

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 

53 

54 

55def _do_to_path(structure, path, command): 

56 if not path: 

57 return command(structure) if callable(command) else command 

58 

59 kvs = _get_keys_and_values(structure, path[0]) 

60 return _update_structure(structure, kvs, path[1:], command) 

61 

62 

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)) 

69 

70 

71def _get(structure, key, default): 

72 try: 

73 if hasattr(structure, '__getitem__'): 

74 return structure[key] 

75 

76 return getattr(structure, key) 

77 

78 except (IndexError, KeyError): 

79 return default 

80 

81 

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 ) 

99 

100 # Non-callables are used as-is as a key. 

101 return [(key_spec, _get(structure, key_spec, _EMPTY_SENTINEL))] 

102 

103 

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 ) 

117 

118 

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() 

134 

135 result = _do_to_path(v, path, command) 

136 if result is not v or is_empty: 

137 e[k] = result 

138 

139 return e.persistent()