pyglove 0.4.5.dev202410100808__py3-none-any.whl → 0.4.5.dev202410160809__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.
@@ -18,12 +18,16 @@ from pyglove.core.object_utils import formatting
18
18
  from pyglove.core.object_utils import value_location
19
19
 
20
20
 
21
+ KeyPath = value_location.KeyPath
22
+ KeyPathSet = value_location.KeyPathSet
23
+
24
+
21
25
  class KeyPathTest(unittest.TestCase):
22
26
  """Tests for class KeyPath."""
23
27
 
24
28
  def test_basics(self):
25
29
  # Root element.
26
- r = value_location.KeyPath()
30
+ r = KeyPath()
27
31
  self.assertTrue(r.is_root)
28
32
  self.assertFalse(r)
29
33
  self.assertEqual(r, '')
@@ -39,7 +43,7 @@ class KeyPathTest(unittest.TestCase):
39
43
  _ = r.key
40
44
 
41
45
  # 1-level deep.
42
- a = value_location.KeyPath('a')
46
+ a = KeyPath('a')
43
47
  self.assertFalse(a.is_root)
44
48
  self.assertEqual(a.key, 'a')
45
49
  self.assertEqual(a.path, 'a')
@@ -49,7 +53,7 @@ class KeyPathTest(unittest.TestCase):
49
53
  self.assertEqual(len(a), 1)
50
54
  self.assertEqual(a.parent, r)
51
55
 
52
- a2 = value_location.KeyPath(0)
56
+ a2 = KeyPath(0)
53
57
  self.assertFalse(a2.is_root)
54
58
  self.assertEqual(a2.key, 0)
55
59
  with formatting.str_format(markdown=True):
@@ -63,7 +67,7 @@ class KeyPathTest(unittest.TestCase):
63
67
  self.assertEqual(len(a2), 1)
64
68
  self.assertEqual(a2.parent, r)
65
69
 
66
- a3 = value_location.KeyPath('x.y')
70
+ a3 = KeyPath('x.y')
67
71
  self.assertFalse(a3.is_root)
68
72
  self.assertEqual(a3.key, 'x.y')
69
73
  self.assertEqual(a3.path, '[x.y]')
@@ -75,28 +79,28 @@ class KeyPathTest(unittest.TestCase):
75
79
  self.assertEqual(a3.parent, r)
76
80
 
77
81
  # Multiple levels.
78
- b = value_location.KeyPath([1, 'b'])
82
+ b = KeyPath([1, 'b'])
79
83
  self.assertEqual(b, '[1].b')
80
84
  self.assertEqual(b.path, '[1].b')
81
85
  self.assertNotEqual(a, b)
82
86
  self.assertEqual(len(b), 2)
83
87
  self.assertEqual(b.parent, '[1]')
84
88
 
85
- c = value_location.KeyPath('c', b)
89
+ c = KeyPath('c', b)
86
90
  self.assertEqual(c.key, 'c')
87
91
  self.assertEqual(c, '[1].b.c')
88
92
  self.assertEqual(c.keys, [1, 'b', 'c'])
89
93
  self.assertEqual(len(c), 3)
90
94
  self.assertEqual(c.parent, b)
91
95
 
92
- d = value_location.KeyPath(['d', 0], c)
96
+ d = KeyPath(['d', 0], c)
93
97
  self.assertEqual(d.key, 0)
94
98
  self.assertEqual(d, '[1].b.c.d[0]')
95
99
  self.assertEqual(d.keys, [1, 'b', 'c', 'd', 0])
96
100
  self.assertEqual(d.parent, '[1].b.c.d')
97
101
  self.assertEqual(len(d), 5)
98
102
 
99
- d2 = value_location.KeyPath(('d', 0), c)
103
+ d2 = KeyPath(('d', 0), c)
100
104
  self.assertEqual(d, d2)
101
105
 
102
106
  def test_complex_key_type(self):
@@ -109,11 +113,11 @@ class KeyPathTest(unittest.TestCase):
109
113
  def __str__(self):
110
114
  return f'A({self._text})'
111
115
 
112
- p = value_location.KeyPath([A('a'), A('b'), 'c'])
116
+ p = KeyPath([A('a'), A('b'), 'c'])
113
117
  self.assertEqual(p.path, '[A(a)][A(b)].c')
114
118
 
115
119
  # Key may have '.' in their string form.
116
- p = value_location.KeyPath([A('a.*'), A('$b')])
120
+ p = KeyPath([A('a.*'), A('$b')])
117
121
  self.assertEqual(p.path, '[A(a.*)][A($b)]')
118
122
 
119
123
  # NOTE: We cannot really parse KeyPath with complex types.
@@ -127,145 +131,145 @@ class KeyPathTest(unittest.TestCase):
127
131
  def __str__(self):
128
132
  return f'B({self._text})'
129
133
 
130
- p = value_location.KeyPath([B('a'), B('b'), 'c'])
134
+ p = KeyPath([B('a'), B('b'), 'c'])
131
135
  self.assertEqual(p.path, 'B(a).B(b).c')
132
136
 
133
137
  def test_parse(self):
134
138
  """Test KeyPath.parse method."""
135
- self.assertEqual(value_location.KeyPath.parse('a').keys, ['a'])
136
- self.assertEqual(len(value_location.KeyPath.parse('')), 0)
139
+ self.assertEqual(KeyPath.parse('a').keys, ['a'])
140
+ self.assertEqual(len(KeyPath.parse('')), 0)
137
141
 
138
- self.assertEqual(value_location.KeyPath.parse('a').keys, ['a'])
139
- self.assertEqual(value_location.KeyPath.parse('[a ]').keys, ['a '])
140
- self.assertEqual(value_location.KeyPath.parse('[0].a').keys, [0, 'a'])
142
+ self.assertEqual(KeyPath.parse('a').keys, ['a'])
143
+ self.assertEqual(KeyPath.parse('[a ]').keys, ['a '])
144
+ self.assertEqual(KeyPath.parse('[0].a').keys, [0, 'a'])
141
145
  self.assertEqual(
142
- value_location.KeyPath.parse('[0][1].a').keys, [0, 1, 'a'])
146
+ KeyPath.parse('[0][1].a').keys, [0, 1, 'a'])
143
147
  self.assertEqual(
144
- value_location.KeyPath.parse('a.b[1].c').keys, ['a', 'b', 1, 'c'])
148
+ KeyPath.parse('a.b[1].c').keys, ['a', 'b', 1, 'c'])
145
149
  self.assertEqual(
146
- value_location.KeyPath.parse('a[x[0]].b[y.z].c').keys,
150
+ KeyPath.parse('a[x[0]].b[y.z].c').keys,
147
151
  ['a', 'x[0]', 'b', 'y.z', 'c'])
148
152
 
149
153
  with self.assertRaisesRegex(
150
154
  ValueError, '\'path_str\' must be a string type.'):
151
- value_location.KeyPath.parse(0)
155
+ KeyPath.parse(0)
152
156
 
153
157
  with self.assertRaisesRegex(
154
158
  ValueError,
155
159
  'KeyPath parse failed: unmatched open bracket at position 0'):
156
- value_location.KeyPath.parse('[0')
160
+ KeyPath.parse('[0')
157
161
 
158
162
  with self.assertRaisesRegex(
159
163
  ValueError,
160
164
  'KeyPath parse failed: unmatched open bracket at position 0'):
161
- value_location.KeyPath.parse('[[0]')
165
+ KeyPath.parse('[[0]')
162
166
 
163
167
  with self.assertRaisesRegex(
164
168
  ValueError,
165
169
  'KeyPath parse failed: unmatched close bracket at position 3'):
166
- value_location.KeyPath.parse('[0]]')
170
+ KeyPath.parse('[0]]')
167
171
 
168
172
  def test_from_value(self):
169
173
  """Test KeyPath.from_value."""
170
174
  self.assertEqual(
171
- value_location.KeyPath.from_value('x.y'),
172
- value_location.KeyPath(['x', 'y']))
175
+ KeyPath.from_value('x.y'),
176
+ KeyPath(['x', 'y']))
173
177
 
174
178
  self.assertEqual(
175
- value_location.KeyPath.from_value(1),
176
- value_location.KeyPath([1]))
179
+ KeyPath.from_value(1),
180
+ KeyPath([1]))
177
181
 
178
- path = value_location.KeyPath(['x'])
182
+ path = KeyPath(['x'])
179
183
  self.assertIs(
180
- value_location.KeyPath.from_value(path),
184
+ KeyPath.from_value(path),
181
185
  path)
182
186
 
183
187
  with self.assertRaisesRegex(
184
188
  ValueError, '.* is not a valid KeyPath equivalence'):
185
- value_location.KeyPath.from_value(0.1)
189
+ KeyPath.from_value(0.1)
186
190
 
187
191
  def test_arithmetics(self):
188
192
  """Test KeyPath arithmetics."""
189
193
 
190
194
  # Test operator +.
191
- self.assertEqual(value_location.KeyPath('a') + 'b.c', 'a.b.c')
192
- self.assertEqual(value_location.KeyPath('a') + '[0].b', 'a[0].b')
193
- self.assertEqual(value_location.KeyPath('a') + None, 'a')
194
- self.assertEqual(value_location.KeyPath('a') + 1, 'a[1]')
195
+ self.assertEqual(KeyPath('a') + 'b.c', 'a.b.c')
196
+ self.assertEqual(KeyPath('a') + '[0].b', 'a[0].b')
197
+ self.assertEqual(KeyPath('a') + None, 'a')
198
+ self.assertEqual(KeyPath('a') + 1, 'a[1]')
195
199
  self.assertEqual(
196
- value_location.KeyPath('a') + value_location.KeyPath('b'), 'a.b')
197
- self.assertEqual(value_location.KeyPath.parse('a.b') + 1.0, 'a.b[1.0]')
200
+ KeyPath('a') + KeyPath('b'), 'a.b')
201
+ self.assertEqual(KeyPath.parse('a.b') + 1.0, 'a.b[1.0]')
198
202
 
199
203
  # Test operator -.
200
204
  self.assertEqual(
201
- value_location.KeyPath('a') - value_location.KeyPath('a'), '')
202
- self.assertEqual(value_location.KeyPath('a') - 'a', '')
203
- self.assertEqual(value_location.KeyPath('a') - '', 'a')
204
- self.assertEqual(value_location.KeyPath('a') - None, 'a')
205
+ KeyPath('a') - KeyPath('a'), '')
206
+ self.assertEqual(KeyPath('a') - 'a', '')
207
+ self.assertEqual(KeyPath('a') - '', 'a')
208
+ self.assertEqual(KeyPath('a') - None, 'a')
205
209
  self.assertEqual(
206
- value_location.KeyPath('a') - value_location.KeyPath(), 'a')
207
- self.assertEqual(value_location.KeyPath.parse('a.b.c.d') - 'a.b', 'c.d')
208
- self.assertEqual(value_location.KeyPath.parse('[0].a') - 0, 'a')
210
+ KeyPath('a') - KeyPath(), 'a')
211
+ self.assertEqual(KeyPath.parse('a.b.c.d') - 'a.b', 'c.d')
212
+ self.assertEqual(KeyPath.parse('[0].a') - 0, 'a')
209
213
 
210
214
  with self.assertRaisesRegex(
211
215
  ValueError, 'KeyPath subtraction failed: .* are in different subtree.'):
212
- _ = value_location.KeyPath('a') - 'b'
216
+ _ = KeyPath('a') - 'b'
213
217
 
214
218
  with self.assertRaisesRegex(
215
219
  ValueError, 'KeyPath subtraction failed: .* are in different subtree.'):
216
- _ = value_location.KeyPath.parse('a.b') - 'a.c'
220
+ _ = KeyPath.parse('a.b') - 'a.c'
217
221
 
218
222
  with self.assertRaisesRegex(
219
223
  ValueError, 'KeyPath subtraction failed: .* are in different subtree.'):
220
- _ = value_location.KeyPath.parse('a[0]') - 'a[1]'
224
+ _ = KeyPath.parse('a[0]') - 'a[1]'
221
225
 
222
226
  with self.assertRaisesRegex(
223
227
  ValueError, 'KeyPath subtraction failed: .* is an ancestor'):
224
- _ = value_location.KeyPath.parse('a.b') - 'a.b.c'
228
+ _ = KeyPath.parse('a.b') - 'a.b.c'
225
229
 
226
230
  with self.assertRaisesRegex(TypeError, 'Cannot subtract KeyPath'):
227
- _ = value_location.KeyPath.parse('a.b') - 1.0
231
+ _ = KeyPath.parse('a.b') - 1.0
228
232
 
229
233
  def test_is_relative_to(self):
230
234
  self.assertTrue(
231
- value_location.KeyPath.parse('a.b.c').is_relative_to(
232
- value_location.KeyPath())
235
+ KeyPath.parse('a.b.c').is_relative_to(
236
+ KeyPath())
233
237
  )
234
238
  self.assertTrue(
235
- value_location.KeyPath.parse('a.b.c').is_relative_to(
236
- value_location.KeyPath.parse('a.b'))
239
+ KeyPath.parse('a.b.c').is_relative_to(
240
+ KeyPath.parse('a.b'))
237
241
  )
238
242
  self.assertTrue(
239
- value_location.KeyPath.parse('a.b.c').is_relative_to(
240
- value_location.KeyPath.parse('a.b.c'))
243
+ KeyPath.parse('a.b.c').is_relative_to(
244
+ KeyPath.parse('a.b.c'))
241
245
  )
242
246
  self.assertFalse(
243
- value_location.KeyPath.parse('a.b').is_relative_to(
244
- value_location.KeyPath.parse('a.b.c'))
247
+ KeyPath.parse('a.b').is_relative_to(
248
+ KeyPath.parse('a.b.c'))
245
249
  )
246
250
  self.assertFalse(
247
- value_location.KeyPath.parse('a.b.d').is_relative_to(
248
- value_location.KeyPath.parse('a.b.c'))
251
+ KeyPath.parse('a.b.d').is_relative_to(
252
+ KeyPath.parse('a.b.c'))
249
253
  )
250
254
 
251
255
  def test_hash(self):
252
- self.assertIn(value_location.KeyPath.parse('a.b.c'), {'a.b.c': 1})
253
- self.assertNotIn(value_location.KeyPath.parse('a.b.c'), {'a.b': 1})
256
+ self.assertIn(KeyPath.parse('a.b.c'), {'a.b.c': 1})
257
+ self.assertNotIn(KeyPath.parse('a.b.c'), {'a.b': 1})
254
258
 
255
259
  def test_comparison(self):
256
- keypath = value_location.KeyPath.parse
260
+ keypath = KeyPath.parse
257
261
  # Equality should only hold true for KeyPaths that are identical.
258
262
  self.assertEqual(
259
- value_location.KeyPath(), value_location.KeyPath.parse(''))
263
+ KeyPath(), KeyPath.parse(''))
260
264
  self.assertEqual(keypath('a[1][2].b[3][4]'), keypath('a[1][2].b[3][4]'))
261
265
  self.assertNotEqual(keypath('a[1][2].b[3][4]'), keypath('a[1][2].a[3][4]'))
262
266
  self.assertNotEqual(keypath('a[1][2].b[3][4]'), keypath('a[1][2].b[4][4]'))
263
267
  # Earlier keys in the path should be prioritized over later ones.
264
- self.assertLess(value_location.KeyPath(), 'a')
265
- self.assertLess(value_location.KeyPath(), keypath('a'))
268
+ self.assertLess(KeyPath(), 'a')
269
+ self.assertLess(KeyPath(), keypath('a'))
266
270
  self.assertLess(keypath('a'), keypath('a.a'))
267
271
  self.assertLess(keypath('a.a'), keypath('a.b'))
268
- self.assertGreater(keypath('a'), value_location.KeyPath())
272
+ self.assertGreater(keypath('a'), KeyPath())
269
273
  self.assertGreater(keypath('a[1].b'), keypath('a[1].a'))
270
274
  self.assertGreater(keypath('a[1].a'), keypath('a[1]'))
271
275
  # Numbers should be compared numerically - not lexicographically.
@@ -290,34 +294,34 @@ class KeyPathTest(unittest.TestCase):
290
294
  return False
291
295
 
292
296
  self.assertLess(
293
- value_location.KeyPath([CustomKey('a'), 'b']),
294
- value_location.KeyPath([CustomKey('b'), 'b']))
297
+ KeyPath([CustomKey('a'), 'b']),
298
+ KeyPath([CustomKey('b'), 'b']))
295
299
 
296
300
  with self.assertRaisesRegex(
297
301
  TypeError, 'Comparison is not supported between instances'):
298
- _ = value_location.KeyPath() < 1
302
+ _ = KeyPath() < 1
299
303
 
300
304
  def test_query(self):
301
305
 
302
306
  def query_shall_succeed(path_str, obj, expected_value, use_inferred=False):
303
307
  self.assertEqual(
304
- value_location.KeyPath.parse(path_str).query(obj, use_inferred),
308
+ KeyPath.parse(path_str).query(obj, use_inferred),
305
309
  expected_value)
306
310
 
307
311
  def query_shall_fail(path_str,
308
312
  obj,
309
313
  error='Cannot query sub-key .* of object .*'):
310
314
  with self.assertRaisesRegex(KeyError, error):
311
- value_location.KeyPath.parse(path_str).query(obj)
315
+ KeyPath.parse(path_str).query(obj)
312
316
 
313
317
  def get_shall_succeed(path_str, obj, default, expected_value):
314
318
  self.assertEqual(
315
- value_location.KeyPath.parse(path_str).get(obj, default),
319
+ KeyPath.parse(path_str).get(obj, default),
316
320
  expected_value)
317
321
 
318
322
  def assert_exists(path_str, obj, should_exists):
319
323
  self.assertEqual(
320
- value_location.KeyPath.parse(path_str).exists(obj), should_exists)
324
+ KeyPath.parse(path_str).exists(obj), should_exists)
321
325
 
322
326
  # Query at root level.
323
327
  query_shall_succeed('', 1, 1)
@@ -409,12 +413,297 @@ class KeyPathTest(unittest.TestCase):
409
413
  def test_message_on_path(self):
410
414
  self.assertEqual(value_location.message_on_path('hi.', None), 'hi.')
411
415
  self.assertEqual(
412
- value_location.message_on_path('hi.', value_location.KeyPath()),
416
+ value_location.message_on_path('hi.', KeyPath()),
413
417
  'hi. (path=)')
414
418
  self.assertEqual(
415
- value_location.message_on_path('hi.', value_location.KeyPath(['a'])),
419
+ value_location.message_on_path('hi.', KeyPath(['a'])),
416
420
  'hi. (path=a)')
417
421
 
418
422
 
423
+ class KeyPathSetTest(unittest.TestCase):
424
+ """Tests for class KeyPathSet."""
425
+
426
+ def test_empty_set(self):
427
+ s1 = KeyPathSet()
428
+ self.assertFalse(s1)
429
+ self.assertNotIn('', s1)
430
+ self.assertNotIn(KeyPath(), s1)
431
+ self.assertNotIn('abc', s1)
432
+ self.assertNotIn(1, s1)
433
+ self.assertEqual(list(s1), [])
434
+ self.assertIs(s1.subtree(KeyPath()), s1)
435
+ self.assertFalse(s1.subtree('a.b.c'))
436
+ self.assertEqual(s1, KeyPathSet())
437
+ self.assertNotEqual(s1, 1)
438
+ self.assertNotEqual(s1, KeyPathSet([1]))
439
+
440
+ def test_add(self):
441
+ s1 = KeyPathSet(
442
+ ['a.b.c', 1, KeyPath([1, 'x']), 'a.b']
443
+ )
444
+ self.assertEqual(
445
+ s1._trie,
446
+ {
447
+ 1: {
448
+ 'x': {
449
+ '$': True,
450
+ },
451
+ '$': True
452
+ },
453
+ 'a': {
454
+ 'b': {
455
+ 'c': {
456
+ '$': True
457
+ },
458
+ '$': True,
459
+ },
460
+ }
461
+ }
462
+ )
463
+ self.assertNotIn('', s1)
464
+ self.assertNotIn('a', s1)
465
+ self.assertIn(KeyPath(['a', 'b']), s1)
466
+ self.assertIn('a.b.c', s1)
467
+ self.assertIn(1, s1)
468
+ self.assertIn('[1]', s1)
469
+ self.assertIn('[1].x', s1)
470
+ self.assertIn(KeyPath([1, 'x']), s1)
471
+
472
+ self.assertTrue(s1.add(''))
473
+ self.assertIn('', s1)
474
+ self.assertFalse(s1.add('a.b.c'))
475
+
476
+ # Test include_intermediate.
477
+ s1 = KeyPathSet()
478
+ self.assertTrue(s1.add('a.b.c', include_intermediate=True))
479
+ self.assertIn('a', s1)
480
+ self.assertIn('a.b', s1)
481
+ self.assertIn('a.b.c', s1)
482
+
483
+ def test_remove(self):
484
+ s1 = KeyPathSet(
485
+ ['a.b.c', 1, KeyPath([1, 'x']), 'a.b', 'c.d']
486
+ )
487
+ self.assertFalse(s1.remove('b'))
488
+ self.assertFalse(s1.remove('c'))
489
+ self.assertTrue(s1.remove('a.b.c'))
490
+ self.assertTrue(s1.remove('a.b'))
491
+ self.assertTrue(s1.remove(1))
492
+ self.assertEqual(
493
+ s1._trie,
494
+ {
495
+ 1: {
496
+ 'x': {
497
+ '$': True,
498
+ },
499
+ },
500
+ 'c': {
501
+ 'd': {
502
+ '$': True,
503
+ },
504
+ },
505
+ }
506
+ )
507
+ self.assertNotIn(1, s1)
508
+ self.assertTrue(s1.has_prefix(1))
509
+
510
+ def test_iter(self):
511
+ self.assertEqual(
512
+ list(KeyPathSet(['', 'a.b.c', 1, KeyPath([1, 'x']), 'a.b'])),
513
+ [
514
+ KeyPath(), KeyPath.parse('a.b.c'), KeyPath.parse('a.b'),
515
+ KeyPath([1]), KeyPath([1, 'x'])
516
+ ]
517
+ )
518
+
519
+ def test_has_prefix(self):
520
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b'])
521
+ self.assertTrue(s1.has_prefix('a'))
522
+ self.assertTrue(s1.has_prefix('a.b'))
523
+ self.assertTrue(s1.has_prefix('a.b.c'))
524
+ self.assertTrue(s1.has_prefix(KeyPath(['a'])))
525
+ self.assertTrue(s1.has_prefix(1))
526
+ self.assertFalse(s1.has_prefix(2))
527
+ self.assertFalse(s1.has_prefix('a.b.c.d'))
528
+
529
+ def test_subpaths(self):
530
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b'])
531
+ self.assertIs(s1.subtree(''), s1)
532
+ self.assertEqual(
533
+ s1.subtree('a'), KeyPathSet(['b.c', 'b'])
534
+ )
535
+ self.assertEqual(s1.subtree(1), KeyPathSet(['', 'x']))
536
+ self.assertEqual(
537
+ s1.subtree(1), KeyPathSet(['', 'x'])
538
+ )
539
+
540
+ def test_clear(self):
541
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b'])
542
+ s1.clear()
543
+ self.assertEqual(s1, KeyPathSet())
544
+
545
+ def test_copy(self):
546
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b'])
547
+ s2 = s1.copy()
548
+ self.assertIsNot(s1, s2)
549
+ self.assertIsNot(s1._trie, s2._trie)
550
+ self.assertIsNot(s1._trie['a'], s2._trie['a'])
551
+ self.assertIsNot(s1._trie['a']['b'], s2._trie['a']['b'])
552
+
553
+ def test_update(self):
554
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b'])
555
+ s1.update(KeyPathSet(['a.b.d', 'a.c', '']))
556
+ self.assertEqual(
557
+ s1._trie,
558
+ {
559
+ 1: {
560
+ 'x': {
561
+ '$': True,
562
+ },
563
+ '$': True,
564
+ },
565
+ 'a': {
566
+ 'b': {
567
+ 'c': {
568
+ '$': True
569
+ },
570
+ 'd': {
571
+ '$': True
572
+ },
573
+ '$': True,
574
+ },
575
+ 'c': {
576
+ '$': True
577
+ },
578
+ },
579
+ '$': True,
580
+ }
581
+ )
582
+
583
+ def test_union(self):
584
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b'])
585
+ s2 = s1.union(KeyPathSet(['a.b.d', 'a.c', '']))
586
+ self.assertEqual(
587
+ list(s2),
588
+ [
589
+ KeyPath.parse('a.b.c'),
590
+ KeyPath.parse('a.b'),
591
+ KeyPath.parse('a.b.d'),
592
+ KeyPath.parse('a.c'),
593
+ KeyPath([1]),
594
+ KeyPath([1, 'x']),
595
+ KeyPath(),
596
+ ]
597
+ )
598
+ self.assertIsNot(s2._trie['a'], s1._trie['a'])
599
+ self.assertIsNot(s2._trie['a']['b'], s1._trie['a']['b'])
600
+ self.assertIsNot(
601
+ s2._trie['a']['b']['c'], s1._trie['a']['b']['c']
602
+ )
603
+
604
+ def test_difference(self):
605
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b', ''])
606
+ s2 = s1.difference(
607
+ KeyPathSet(['a.b', 'a.c.b', '[1].x', ''])
608
+ )
609
+ self.assertEqual(
610
+ s2._trie,
611
+ {
612
+ 1: {
613
+ '$': True
614
+ },
615
+ 'a': {
616
+ 'b': {
617
+ 'c': {
618
+ '$': True
619
+ }
620
+ }
621
+ }
622
+ }
623
+ )
624
+ self.assertIsNot(s2._trie['a'], s1._trie['a'])
625
+ self.assertIsNot(s2._trie['a']['b'], s1._trie['a']['b'])
626
+ self.assertIsNot(s2._trie[1], s1._trie[1])
627
+
628
+ s1.difference_update(KeyPathSet(['a.b', 'a.c.b', '[1].x', '']))
629
+ self.assertEqual(list(s1), ['a.b.c', '[1]'])
630
+
631
+ def test_intersection(self):
632
+ s1 = KeyPathSet(['a.b.c', 1, KeyPath([1, 'x']), 'a.b', 'a.c.d', ''])
633
+ s2 = s1.intersection(
634
+ KeyPathSet(['a.b', 'a.b.d', 'a.c.b', '[1].x', ''])
635
+ )
636
+ self.assertEqual(
637
+ s2._trie,
638
+ {
639
+ 1: {
640
+ 'x': {
641
+ '$': True,
642
+ },
643
+ },
644
+ 'a': {
645
+ 'b': {
646
+ '$': True,
647
+ },
648
+ },
649
+ '$': True,
650
+ }
651
+ )
652
+ self.assertIsNot(s2._trie['a'], s1._trie['a'])
653
+ self.assertIsNot(s2._trie['a']['b'], s1._trie['a']['b'])
654
+ self.assertIsNot(s2._trie[1], s1._trie[1])
655
+
656
+ s1.intersection_update(
657
+ KeyPathSet(['a.b', 'a.c.b', '[1].x', ''])
658
+ )
659
+ self.assertEqual(list(s1), ['a.b', '[1].x', ''])
660
+
661
+ def test_rebase(self):
662
+ s1 = KeyPathSet(['x.y', 'y', 'y.z.w'])
663
+ s1.rebase('a.b.')
664
+ self.assertEqual(
665
+ list(s1),
666
+ [
667
+ KeyPath.parse('a.b.x.y'),
668
+ KeyPath.parse('a.b.y'),
669
+ KeyPath.parse('a.b.y.z.w'),
670
+ ]
671
+ )
672
+
673
+ def test_operator_add(self):
674
+ self.assertEqual(
675
+ KeyPathSet(['a.b.c', 'a.b']) + KeyPathSet(['a.b', '[1].a', '']),
676
+ KeyPathSet(['a.b.c', 'a.b', '[1].a', ''])
677
+ )
678
+ self.assertEqual(
679
+ KeyPath.parse('x[0]') + KeyPathSet(['a.b.c', 'a.b']),
680
+ KeyPathSet(['x[0].a.b.c', 'x[0].a.b'])
681
+ )
682
+
683
+ def test_format(self):
684
+ self.assertEqual(
685
+ KeyPathSet(['a.b.c', 'a.b']).format(),
686
+ 'KeyPathSet([a.b.c, a.b])'
687
+ )
688
+
689
+ def test_from_value(self):
690
+ """Test KeyPathSet.from_value."""
691
+ s = KeyPathSet(['a.b.c'])
692
+ self.assertIs(
693
+ KeyPathSet.from_value(s), s
694
+ )
695
+ self.assertEqual(
696
+ KeyPathSet.from_value(['a.b']),
697
+ KeyPathSet(['a.b'])
698
+ )
699
+ self.assertEqual(
700
+ KeyPathSet.from_value(['a.b'], include_intermediate=True),
701
+ KeyPathSet(['a', 'a.b', ''])
702
+ )
703
+ with self.assertRaisesRegex(
704
+ ValueError, 'Cannot convert .* to KeyPathSet'
705
+ ):
706
+ KeyPathSet.from_value(1)
707
+
419
708
  if __name__ == '__main__':
420
709
  unittest.main()
@@ -89,7 +89,7 @@ class FieldUpdateTest(unittest.TestCase):
89
89
  )
90
90
 
91
91
 
92
- class HtmlFormattableTest(unittest.TestCase):
92
+ class HtmlTreeViewExtensionTest(unittest.TestCase):
93
93
 
94
94
  def assert_content(self, html, expected):
95
95
  expected = inspect.cleandoc(expected).strip()
@@ -112,7 +112,7 @@ class HtmlFormattableTest(unittest.TestCase):
112
112
  enable_key_tooltip=False
113
113
  ),
114
114
  """
115
- <details open class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr><tr><td><span class="object_key">y</span></td><td><span class="simple_value str">&#x27;foo&#x27;</span></td></tr></table></div></details>
115
+ <details open class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key str">x</span></td><td><div><span class="simple_value int">1</span></div></td></tr><tr><td><span class="object_key str">y</span></td><td><div><span class="simple_value str">&#x27;foo&#x27;</span></div></td></tr></table></div></details>
116
116
  """
117
117
  )
118
118
  # Hide frozen and default values.
@@ -125,7 +125,7 @@ class HtmlFormattableTest(unittest.TestCase):
125
125
  hide_default_values=True
126
126
  ),
127
127
  """
128
- <details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key">x</span></td><td><span class="simple_value int">1</span></td></tr></table></div></details>
128
+ <details class="pyglove foo"><summary><div class="summary_title">Foo(...)</div></summary><div class="complex_value foo"><table><tr><td><span class="object_key str">x</span></td><td><div><span class="simple_value int">1</span></div></td></tr></table></div></details>
129
129
  """
130
130
  )
131
131
  # Use inferred values.
@@ -137,7 +137,7 @@ class HtmlFormattableTest(unittest.TestCase):
137
137
  use_inferred=False
138
138
  ),
139
139
  """
140
- <details open class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">y</span></td><td><details class="pyglove value-from-parent-chain"><summary><div class="summary_title">ValueFromParentChain(...)</div></summary><div class="complex_value value-from-parent-chain"><span class="empty_container"></span></div></details></td></tr></table></div></details>
140
+ <details open class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key str">y</span></td><td><div><details class="pyglove value-from-parent-chain"><summary><div class="summary_title">ValueFromParentChain(...)</div></summary><div class="complex_value value-from-parent-chain"><span class="empty_container"></span></div></details></div></td></tr></table></div></details>
141
141
  """
142
142
  )
143
143
  self.assert_content(
@@ -147,7 +147,7 @@ class HtmlFormattableTest(unittest.TestCase):
147
147
  use_inferred=True
148
148
  ),
149
149
  """
150
- <details open class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key">y</span></td><td><span class="simple_value int">2</span></td></tr></table></div></details>
150
+ <details open class="pyglove dict"><summary><div class="summary_title">Dict(...)</div></summary><div class="complex_value dict"><table><tr><td><span class="object_key str">y</span></td><td><div><span class="simple_value int">2</span></div></td></tr></table></div></details>
151
151
  """
152
152
  )
153
153