pyglove 0.4.5.dev20240319__py3-none-any.whl → 0.4.5.dev202501140808__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. pyglove/core/__init__.py +54 -20
  2. pyglove/core/coding/__init__.py +42 -0
  3. pyglove/core/coding/errors.py +111 -0
  4. pyglove/core/coding/errors_test.py +98 -0
  5. pyglove/core/coding/execution.py +309 -0
  6. pyglove/core/coding/execution_test.py +333 -0
  7. pyglove/core/{object_utils/codegen.py → coding/function_generation.py} +10 -4
  8. pyglove/core/{object_utils/codegen_test.py → coding/function_generation_test.py} +5 -7
  9. pyglove/core/coding/parsing.py +153 -0
  10. pyglove/core/coding/parsing_test.py +150 -0
  11. pyglove/core/coding/permissions.py +100 -0
  12. pyglove/core/coding/permissions_test.py +93 -0
  13. pyglove/core/geno/base.py +54 -41
  14. pyglove/core/geno/base_test.py +2 -4
  15. pyglove/core/geno/categorical.py +37 -28
  16. pyglove/core/geno/custom.py +19 -16
  17. pyglove/core/geno/numerical.py +20 -17
  18. pyglove/core/geno/space.py +4 -5
  19. pyglove/core/hyper/base.py +6 -6
  20. pyglove/core/hyper/categorical.py +94 -55
  21. pyglove/core/hyper/custom.py +7 -7
  22. pyglove/core/hyper/custom_test.py +9 -10
  23. pyglove/core/hyper/derived.py +30 -22
  24. pyglove/core/hyper/derived_test.py +2 -4
  25. pyglove/core/hyper/dynamic_evaluation.py +5 -6
  26. pyglove/core/hyper/evolvable.py +57 -46
  27. pyglove/core/hyper/numerical.py +48 -24
  28. pyglove/core/hyper/numerical_test.py +9 -9
  29. pyglove/core/hyper/object_template.py +58 -46
  30. pyglove/core/io/__init__.py +1 -0
  31. pyglove/core/io/file_system.py +17 -7
  32. pyglove/core/io/file_system_test.py +2 -0
  33. pyglove/core/io/sequence.py +299 -0
  34. pyglove/core/io/sequence_test.py +124 -0
  35. pyglove/core/logging_test.py +0 -2
  36. pyglove/core/patching/object_factory.py +4 -4
  37. pyglove/core/patching/pattern_based.py +4 -4
  38. pyglove/core/patching/rule_based.py +17 -5
  39. pyglove/core/patching/rule_based_test.py +27 -4
  40. pyglove/core/symbolic/__init__.py +2 -7
  41. pyglove/core/symbolic/base.py +320 -183
  42. pyglove/core/symbolic/base_test.py +123 -19
  43. pyglove/core/symbolic/boilerplate.py +7 -13
  44. pyglove/core/symbolic/boilerplate_test.py +25 -23
  45. pyglove/core/symbolic/class_wrapper.py +48 -45
  46. pyglove/core/symbolic/class_wrapper_test.py +2 -2
  47. pyglove/core/symbolic/compounding.py +9 -15
  48. pyglove/core/symbolic/compounding_test.py +2 -4
  49. pyglove/core/symbolic/dict.py +154 -110
  50. pyglove/core/symbolic/dict_test.py +238 -130
  51. pyglove/core/symbolic/diff.py +199 -10
  52. pyglove/core/symbolic/diff_test.py +226 -0
  53. pyglove/core/symbolic/flags.py +1 -1
  54. pyglove/core/symbolic/functor.py +29 -26
  55. pyglove/core/symbolic/functor_test.py +102 -50
  56. pyglove/core/symbolic/inferred.py +2 -2
  57. pyglove/core/symbolic/list.py +81 -50
  58. pyglove/core/symbolic/list_test.py +119 -97
  59. pyglove/core/symbolic/object.py +225 -113
  60. pyglove/core/symbolic/object_test.py +320 -108
  61. pyglove/core/symbolic/origin.py +17 -14
  62. pyglove/core/symbolic/origin_test.py +4 -2
  63. pyglove/core/symbolic/pure_symbolic.py +4 -3
  64. pyglove/core/symbolic/ref.py +108 -21
  65. pyglove/core/symbolic/ref_test.py +93 -0
  66. pyglove/core/symbolic/symbolize_test.py +10 -2
  67. pyglove/core/tuning/local_backend.py +2 -2
  68. pyglove/core/tuning/protocols.py +3 -3
  69. pyglove/core/tuning/sample_test.py +3 -3
  70. pyglove/core/typing/__init__.py +14 -5
  71. pyglove/core/typing/annotation_conversion.py +43 -27
  72. pyglove/core/typing/annotation_conversion_test.py +23 -0
  73. pyglove/core/typing/callable_ext.py +241 -3
  74. pyglove/core/typing/callable_ext_test.py +255 -0
  75. pyglove/core/typing/callable_signature.py +510 -66
  76. pyglove/core/typing/callable_signature_test.py +619 -99
  77. pyglove/core/typing/class_schema.py +229 -154
  78. pyglove/core/typing/class_schema_test.py +149 -95
  79. pyglove/core/typing/custom_typing.py +5 -4
  80. pyglove/core/typing/inspect.py +63 -0
  81. pyglove/core/typing/inspect_test.py +39 -0
  82. pyglove/core/typing/key_specs.py +10 -11
  83. pyglove/core/typing/key_specs_test.py +7 -4
  84. pyglove/core/typing/type_conversion.py +4 -5
  85. pyglove/core/typing/type_conversion_test.py +12 -12
  86. pyglove/core/typing/typed_missing.py +6 -7
  87. pyglove/core/typing/typed_missing_test.py +7 -8
  88. pyglove/core/typing/value_specs.py +604 -362
  89. pyglove/core/typing/value_specs_test.py +328 -90
  90. pyglove/core/utils/__init__.py +164 -0
  91. pyglove/core/{object_utils → utils}/common_traits.py +3 -67
  92. pyglove/core/utils/common_traits_test.py +36 -0
  93. pyglove/core/{object_utils → utils}/docstr_utils.py +23 -0
  94. pyglove/core/{object_utils → utils}/docstr_utils_test.py +36 -4
  95. pyglove/core/{object_utils → utils}/error_utils.py +78 -9
  96. pyglove/core/{object_utils → utils}/error_utils_test.py +61 -5
  97. pyglove/core/utils/formatting.py +464 -0
  98. pyglove/core/utils/formatting_test.py +453 -0
  99. pyglove/core/{object_utils → utils}/hierarchical.py +23 -25
  100. pyglove/core/{object_utils → utils}/hierarchical_test.py +3 -5
  101. pyglove/core/{object_utils → utils}/json_conversion.py +177 -52
  102. pyglove/core/{object_utils → utils}/json_conversion_test.py +97 -16
  103. pyglove/core/{object_utils → utils}/missing.py +3 -3
  104. pyglove/core/{object_utils → utils}/missing_test.py +2 -4
  105. pyglove/core/utils/text_color.py +128 -0
  106. pyglove/core/utils/text_color_test.py +94 -0
  107. pyglove/core/{object_utils → utils}/thread_local_test.py +1 -3
  108. pyglove/core/utils/timing.py +236 -0
  109. pyglove/core/utils/timing_test.py +154 -0
  110. pyglove/core/{object_utils → utils}/value_location.py +275 -6
  111. pyglove/core/utils/value_location_test.py +707 -0
  112. pyglove/core/views/__init__.py +32 -0
  113. pyglove/core/views/base.py +804 -0
  114. pyglove/core/views/base_test.py +580 -0
  115. pyglove/core/views/html/__init__.py +27 -0
  116. pyglove/core/views/html/base.py +547 -0
  117. pyglove/core/views/html/base_test.py +830 -0
  118. pyglove/core/views/html/controls/__init__.py +35 -0
  119. pyglove/core/views/html/controls/base.py +275 -0
  120. pyglove/core/views/html/controls/label.py +207 -0
  121. pyglove/core/views/html/controls/label_test.py +157 -0
  122. pyglove/core/views/html/controls/progress_bar.py +183 -0
  123. pyglove/core/views/html/controls/progress_bar_test.py +97 -0
  124. pyglove/core/views/html/controls/tab.py +320 -0
  125. pyglove/core/views/html/controls/tab_test.py +87 -0
  126. pyglove/core/views/html/controls/tooltip.py +99 -0
  127. pyglove/core/views/html/controls/tooltip_test.py +99 -0
  128. pyglove/core/views/html/tree_view.py +1517 -0
  129. pyglove/core/views/html/tree_view_test.py +1461 -0
  130. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/METADATA +18 -4
  131. pyglove-0.4.5.dev202501140808.dist-info/RECORD +214 -0
  132. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/WHEEL +1 -1
  133. pyglove/core/object_utils/__init__.py +0 -154
  134. pyglove/core/object_utils/common_traits_test.py +0 -82
  135. pyglove/core/object_utils/formatting.py +0 -234
  136. pyglove/core/object_utils/formatting_test.py +0 -223
  137. pyglove/core/object_utils/value_location_test.py +0 -385
  138. pyglove/core/symbolic/schema_utils.py +0 -327
  139. pyglove/core/symbolic/schema_utils_test.py +0 -57
  140. pyglove/core/typing/class_schema_utils.py +0 -202
  141. pyglove/core/typing/class_schema_utils_test.py +0 -194
  142. pyglove-0.4.5.dev20240319.dist-info/RECORD +0 -185
  143. /pyglove/core/{object_utils → utils}/thread_local.py +0 -0
  144. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/LICENSE +0 -0
  145. {pyglove-0.4.5.dev20240319.dist-info → pyglove-0.4.5.dev202501140808.dist-info}/top_level.txt +0 -0
@@ -1,385 +0,0 @@
1
- # Copyright 2022 The PyGlove Authors
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- """Tests for pyglove.object_utils.value_location."""
15
-
16
- import unittest
17
- from pyglove.core.object_utils import value_location
18
-
19
-
20
- class KeyPathTest(unittest.TestCase):
21
- """Tests for class KeyPath."""
22
-
23
- def test_basics(self):
24
- # Root element.
25
- r = value_location.KeyPath()
26
- self.assertTrue(r.is_root)
27
- self.assertFalse(r)
28
- self.assertEqual(r, '')
29
- self.assertEqual(r.depth, 0)
30
- self.assertEqual(len(r), 0)
31
- self.assertEqual(r.path, '')
32
- with self.assertRaisesRegex(
33
- KeyError, 'Parent of a root KeyPath does not exist.'):
34
- _ = r.parent
35
-
36
- with self.assertRaisesRegex(
37
- KeyError, 'Key of root KeyPath does not exist.'):
38
- _ = r.key
39
-
40
- # 1-level deep.
41
- a = value_location.KeyPath('a')
42
- self.assertFalse(a.is_root)
43
- self.assertEqual(a.key, 'a')
44
- self.assertEqual(a.path, 'a')
45
- self.assertEqual(a, 'a') # Relative path compare.
46
- self.assertNotEqual(a, '')
47
- self.assertEqual(a.depth, 1)
48
- self.assertEqual(len(a), 1)
49
- self.assertEqual(a.parent, r)
50
-
51
- a2 = value_location.KeyPath(0)
52
- self.assertFalse(a2.is_root)
53
- self.assertEqual(a2.key, 0)
54
- self.assertEqual(str(a2), '[0]')
55
- self.assertEqual(a2.path, '[0]')
56
- self.assertEqual(a2, '[0]')
57
- self.assertNotEqual(a2, '')
58
- self.assertEqual(a2.depth, 1)
59
- self.assertEqual(len(a2), 1)
60
- self.assertEqual(a2.parent, r)
61
-
62
- a3 = value_location.KeyPath('x.y')
63
- self.assertFalse(a3.is_root)
64
- self.assertEqual(a3.key, 'x.y')
65
- self.assertEqual(a3.path, '[x.y]')
66
- self.assertEqual(a3.path_str(False), 'x.y')
67
- self.assertEqual(a3, '[x.y]') # Relative path compare.
68
- self.assertNotEqual(a3, 'x.y')
69
- self.assertEqual(a3.depth, 1)
70
- self.assertEqual(len(a3), 1)
71
- self.assertEqual(a3.parent, r)
72
-
73
- # Multiple levels.
74
- b = value_location.KeyPath([1, 'b'])
75
- self.assertEqual(b, '[1].b')
76
- self.assertEqual(b.path, '[1].b')
77
- self.assertNotEqual(a, b)
78
- self.assertEqual(len(b), 2)
79
- self.assertEqual(b.parent, '[1]')
80
-
81
- c = value_location.KeyPath('c', b)
82
- self.assertEqual(c.key, 'c')
83
- self.assertEqual(c, '[1].b.c')
84
- self.assertEqual(c.keys, [1, 'b', 'c'])
85
- self.assertEqual(len(c), 3)
86
- self.assertEqual(c.parent, b)
87
-
88
- d = value_location.KeyPath(['d', 0], c)
89
- self.assertEqual(d.key, 0)
90
- self.assertEqual(d, '[1].b.c.d[0]')
91
- self.assertEqual(d.keys, [1, 'b', 'c', 'd', 0])
92
- self.assertEqual(d.parent, '[1].b.c.d')
93
- self.assertEqual(len(d), 5)
94
-
95
- d2 = value_location.KeyPath(('d', 0), c)
96
- self.assertEqual(d, d2)
97
-
98
- def test_complex_key_type(self):
99
-
100
- class A:
101
-
102
- def __init__(self, text):
103
- self._text = text
104
-
105
- def __str__(self):
106
- return f'A({self._text})'
107
-
108
- p = value_location.KeyPath([A('a'), A('b'), 'c'])
109
- self.assertEqual(p.path, '[A(a)][A(b)].c')
110
-
111
- # Key may have '.' in their string form.
112
- p = value_location.KeyPath([A('a.*'), A('$b')])
113
- self.assertEqual(p.path, '[A(a.*)][A($b)]')
114
-
115
- # NOTE: We cannot really parse KeyPath with complex types.
116
-
117
- class B(value_location.StrKey):
118
- """Class that implements StrKey will be treated as string key."""
119
-
120
- def __init__(self, text):
121
- self._text = text
122
-
123
- def __str__(self):
124
- return f'B({self._text})'
125
-
126
- p = value_location.KeyPath([B('a'), B('b'), 'c'])
127
- self.assertEqual(p.path, 'B(a).B(b).c')
128
-
129
- def test_parse(self):
130
- """Test KeyPath.parse method."""
131
- self.assertEqual(value_location.KeyPath.parse('a').keys, ['a'])
132
- self.assertEqual(len(value_location.KeyPath.parse('')), 0)
133
-
134
- self.assertEqual(value_location.KeyPath.parse('a').keys, ['a'])
135
- self.assertEqual(value_location.KeyPath.parse('[a ]').keys, ['a '])
136
- self.assertEqual(value_location.KeyPath.parse('[0].a').keys, [0, 'a'])
137
- self.assertEqual(
138
- value_location.KeyPath.parse('[0][1].a').keys, [0, 1, 'a'])
139
- self.assertEqual(
140
- value_location.KeyPath.parse('a.b[1].c').keys, ['a', 'b', 1, 'c'])
141
- self.assertEqual(
142
- value_location.KeyPath.parse('a[x[0]].b[y.z].c').keys,
143
- ['a', 'x[0]', 'b', 'y.z', 'c'])
144
-
145
- with self.assertRaisesRegex(
146
- ValueError, '\'path_str\' must be a string type.'):
147
- value_location.KeyPath.parse(0)
148
-
149
- with self.assertRaisesRegex(
150
- ValueError,
151
- 'KeyPath parse failed: unmatched open bracket at position 0'):
152
- value_location.KeyPath.parse('[0')
153
-
154
- with self.assertRaisesRegex(
155
- ValueError,
156
- 'KeyPath parse failed: unmatched open bracket at position 0'):
157
- value_location.KeyPath.parse('[[0]')
158
-
159
- with self.assertRaisesRegex(
160
- ValueError,
161
- 'KeyPath parse failed: unmatched close bracket at position 3'):
162
- value_location.KeyPath.parse('[0]]')
163
-
164
- def test_from_value(self):
165
- """Test KeyPath.from_value."""
166
- self.assertEqual(
167
- value_location.KeyPath.from_value('x.y'),
168
- value_location.KeyPath(['x', 'y']))
169
-
170
- self.assertEqual(
171
- value_location.KeyPath.from_value(1),
172
- value_location.KeyPath([1]))
173
-
174
- path = value_location.KeyPath(['x'])
175
- self.assertIs(
176
- value_location.KeyPath.from_value(path),
177
- path)
178
-
179
- with self.assertRaisesRegex(
180
- ValueError, '.* is not a valid KeyPath equivalence'):
181
- value_location.KeyPath.from_value(0.1)
182
-
183
- def test_arithmetics(self):
184
- """Test KeyPath arithmetics."""
185
-
186
- # Test operator +.
187
- self.assertEqual(value_location.KeyPath('a') + 'b.c', 'a.b.c')
188
- self.assertEqual(value_location.KeyPath('a') + '[0].b', 'a[0].b')
189
- self.assertEqual(value_location.KeyPath('a') + None, 'a')
190
- self.assertEqual(value_location.KeyPath('a') + 1, 'a[1]')
191
- self.assertEqual(
192
- value_location.KeyPath('a') + value_location.KeyPath('b'), 'a.b')
193
- self.assertEqual(value_location.KeyPath.parse('a.b') + 1.0, 'a.b[1.0]')
194
-
195
- # Test operator -.
196
- self.assertEqual(
197
- value_location.KeyPath('a') - value_location.KeyPath('a'), '')
198
- self.assertEqual(value_location.KeyPath('a') - 'a', '')
199
- self.assertEqual(value_location.KeyPath('a') - '', 'a')
200
- self.assertEqual(value_location.KeyPath('a') - None, 'a')
201
- self.assertEqual(
202
- value_location.KeyPath('a') - value_location.KeyPath(), 'a')
203
- self.assertEqual(value_location.KeyPath.parse('a.b.c.d') - 'a.b', 'c.d')
204
- self.assertEqual(value_location.KeyPath.parse('[0].a') - 0, 'a')
205
-
206
- with self.assertRaisesRegex(
207
- ValueError, 'KeyPath subtraction failed: .* are in different subtree.'):
208
- _ = value_location.KeyPath('a') - 'b'
209
-
210
- with self.assertRaisesRegex(
211
- ValueError, 'KeyPath subtraction failed: .* are in different subtree.'):
212
- _ = value_location.KeyPath.parse('a.b') - 'a.c'
213
-
214
- with self.assertRaisesRegex(
215
- ValueError, 'KeyPath subtraction failed: .* are in different subtree.'):
216
- _ = value_location.KeyPath.parse('a[0]') - 'a[1]'
217
-
218
- with self.assertRaisesRegex(
219
- ValueError, 'KeyPath subtraction failed: .* is an ancestor'):
220
- _ = value_location.KeyPath.parse('a.b') - 'a.b.c'
221
-
222
- with self.assertRaisesRegex(TypeError, 'Cannot subtract KeyPath'):
223
- _ = value_location.KeyPath.parse('a.b') - 1.0
224
-
225
- def test_hash(self):
226
- self.assertIn(value_location.KeyPath.parse('a.b.c'), {'a.b.c': 1})
227
- self.assertNotIn(value_location.KeyPath.parse('a.b.c'), {'a.b': 1})
228
-
229
- def test_comparison(self):
230
- keypath = value_location.KeyPath.parse
231
- # Equality should only hold true for KeyPaths that are identical.
232
- self.assertEqual(
233
- value_location.KeyPath(), value_location.KeyPath.parse(''))
234
- self.assertEqual(keypath('a[1][2].b[3][4]'), keypath('a[1][2].b[3][4]'))
235
- self.assertNotEqual(keypath('a[1][2].b[3][4]'), keypath('a[1][2].a[3][4]'))
236
- self.assertNotEqual(keypath('a[1][2].b[3][4]'), keypath('a[1][2].b[4][4]'))
237
- # Earlier keys in the path should be prioritized over later ones.
238
- self.assertLess(value_location.KeyPath(), 'a')
239
- self.assertLess(value_location.KeyPath(), keypath('a'))
240
- self.assertLess(keypath('a'), keypath('a.a'))
241
- self.assertLess(keypath('a.a'), keypath('a.b'))
242
- self.assertGreater(keypath('a'), value_location.KeyPath())
243
- self.assertGreater(keypath('a[1].b'), keypath('a[1].a'))
244
- self.assertGreater(keypath('a[1].a'), keypath('a[1]'))
245
- # Numbers should be compared numerically - not lexicographically.
246
- self.assertLessEqual(keypath('a[1]'), keypath('a[2]'))
247
- self.assertLessEqual(keypath('a[2]'), keypath('a[10]'))
248
- self.assertGreaterEqual(keypath('a[10]'), keypath('a[2]'))
249
- self.assertGreaterEqual(keypath('a[2]'), keypath('a[1]'))
250
- # It should be possible to compare numeric keys with string keys.
251
- self.assertLess(keypath('a[1]'), keypath('a.b'))
252
- self.assertGreater(keypath('a.b'), keypath('a[1]'))
253
- self.assertLessEqual(keypath('a[1]'), keypath('a.b'))
254
- self.assertGreaterEqual(keypath('a.b'), keypath('a[1]'))
255
-
256
- class CustomKey(value_location.StrKey):
257
-
258
- def __init__(self, text):
259
- self.text = text
260
-
261
- def __lt__(self, other):
262
- if isinstance(other, CustomKey):
263
- return self.text < other.text
264
- return False
265
-
266
- self.assertLess(
267
- value_location.KeyPath([CustomKey('a'), 'b']),
268
- value_location.KeyPath([CustomKey('b'), 'b']))
269
-
270
- with self.assertRaisesRegex(
271
- TypeError, 'Comparison is not supported between instances'):
272
- _ = value_location.KeyPath() < 1
273
-
274
- def test_query(self):
275
-
276
- def query_shall_succeed(path_str, obj, expected_value, use_inferred=False):
277
- self.assertEqual(
278
- value_location.KeyPath.parse(path_str).query(obj, use_inferred),
279
- expected_value)
280
-
281
- def query_shall_fail(path_str,
282
- obj,
283
- error='Cannot query sub-key .* of object .*'):
284
- with self.assertRaisesRegex(KeyError, error):
285
- value_location.KeyPath.parse(path_str).query(obj)
286
-
287
- def get_shall_succeed(path_str, obj, default, expected_value):
288
- self.assertEqual(
289
- value_location.KeyPath.parse(path_str).get(obj, default),
290
- expected_value)
291
-
292
- def assert_exists(path_str, obj, should_exists):
293
- self.assertEqual(
294
- value_location.KeyPath.parse(path_str).exists(obj), should_exists)
295
-
296
- # Query at root level.
297
- query_shall_succeed('', 1, 1)
298
- query_shall_succeed('', None, None)
299
- query_shall_succeed('', [1, 2], [1, 2])
300
- query_shall_succeed('', {'a': 'foo'}, {'a': 'foo'})
301
-
302
- # Query simple types with more than 1 depth.
303
- query_shall_fail('a', 1)
304
- query_shall_fail('[0]', None)
305
-
306
- # Query complex types.
307
- class Foo:
308
- """Custom object."""
309
-
310
- def __init__(self, values):
311
- self._values = values
312
-
313
- def __getitem__(self, key):
314
- return self._values[key]
315
-
316
- # NOTE(daiyip): __len__ and __contains__ is intentional omitted.
317
-
318
- class Bar:
319
- """Custom object with sym_getattr."""
320
-
321
- def __init__(self, **kwargs):
322
- self._map = kwargs
323
-
324
- def sym_hasattr(self, name):
325
- return name in self._map
326
-
327
- def sym_getattr(self, name):
328
- return self._map[name]
329
-
330
- def sym_inferred(self, key):
331
- return Bar(z=self._map[key])
332
-
333
- def __contains__(self, key):
334
- return key in self._map
335
-
336
- def __eq__(self, other):
337
- return self._map == other._map
338
-
339
- def __ne__(self, other):
340
- return not self.__eq__(other)
341
-
342
- src = {'a': [{'c': 'foo'},
343
- {'d': [1, 2]}],
344
- 'b': True,
345
- 'e': Foo([1, 2, 3]),
346
- 'f': Bar(x=0, y=1)}
347
-
348
- query_shall_succeed('', src, src)
349
- query_shall_succeed('a', src, src['a'])
350
- query_shall_succeed('a[0]', src, src['a'][0])
351
- query_shall_succeed('a[0].c', src, src['a'][0]['c'])
352
- query_shall_succeed('a[1].d[1]', src, src['a'][1]['d'][1])
353
- query_shall_succeed('b', src, src['b'])
354
- query_shall_succeed('f.x', src, 0)
355
- query_shall_succeed('f.x.z', src, Bar(z=0), use_inferred=True)
356
-
357
- query_shall_fail('c', src, 'Path .* does not exist: key .* is absent')
358
- query_shall_fail('a.c', src, 'Path .* does not exist: key .* is absent')
359
- query_shall_fail('a[2]', src, 'Path .* does not exist: key .* is absent')
360
- query_shall_fail('a[1].e', src, 'Path .* does not exist: key .* is absent')
361
- query_shall_fail(
362
- 'e[0]', src, 'Cannot query index .* on object .*: '
363
- '\'__len__\' does not exist')
364
- query_shall_fail(
365
- 'e.f', src, 'Cannot query key .* on object .*: '
366
- '\'__contains__\' does not exist')
367
- query_shall_fail(
368
- 'f.z', src, 'Path .* does not exist: key .* is absent')
369
- query_shall_fail('f.x.z', src, 'Cannot query sub-key .* does not exist')
370
-
371
- # Test get method.
372
- get_shall_succeed('', src, None, src)
373
- get_shall_succeed('a[1].d[1]', src, None, src['a'][1]['d'][1])
374
- get_shall_succeed('c', src, None, None)
375
- get_shall_succeed('b.c', src, 1, 1)
376
-
377
- # Test exists method.
378
- assert_exists('', src, True)
379
- assert_exists('a[1].d[1]', src, True)
380
- assert_exists('c', src, False)
381
- assert_exists('b.c', src, False)
382
-
383
-
384
- if __name__ == '__main__':
385
- unittest.main()