pyglove 0.5.0.dev202510200810__py3-none-any.whl → 0.5.0.dev202512080812__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.

Potentially problematic release.


This version of pyglove might be problematic. Click here for more details.

Files changed (34) hide show
  1. pyglove/core/geno/base.py +7 -3
  2. pyglove/core/io/file_system.py +452 -2
  3. pyglove/core/io/file_system_test.py +442 -0
  4. pyglove/core/symbolic/__init__.py +7 -0
  5. pyglove/core/symbolic/base.py +89 -35
  6. pyglove/core/symbolic/base_test.py +3 -3
  7. pyglove/core/symbolic/dict.py +31 -12
  8. pyglove/core/symbolic/dict_test.py +49 -0
  9. pyglove/core/symbolic/list.py +17 -3
  10. pyglove/core/symbolic/list_test.py +24 -2
  11. pyglove/core/symbolic/object.py +3 -1
  12. pyglove/core/symbolic/object_test.py +13 -10
  13. pyglove/core/symbolic/ref.py +19 -7
  14. pyglove/core/symbolic/ref_test.py +94 -7
  15. pyglove/core/symbolic/unknown_symbols.py +147 -0
  16. pyglove/core/symbolic/unknown_symbols_test.py +100 -0
  17. pyglove/core/typing/annotation_conversion.py +8 -1
  18. pyglove/core/typing/annotation_conversion_test.py +14 -19
  19. pyglove/core/typing/class_schema.py +4 -1
  20. pyglove/core/typing/type_conversion.py +17 -3
  21. pyglove/core/typing/type_conversion_test.py +7 -2
  22. pyglove/core/typing/value_specs.py +5 -1
  23. pyglove/core/typing/value_specs_test.py +5 -0
  24. pyglove/core/utils/__init__.py +1 -0
  25. pyglove/core/utils/json_conversion.py +360 -63
  26. pyglove/core/utils/json_conversion_test.py +146 -13
  27. pyglove/core/views/html/controls/tab.py +33 -0
  28. pyglove/core/views/html/controls/tab_test.py +37 -0
  29. pyglove/ext/evolution/base_test.py +1 -1
  30. {pyglove-0.5.0.dev202510200810.dist-info → pyglove-0.5.0.dev202512080812.dist-info}/METADATA +8 -1
  31. {pyglove-0.5.0.dev202510200810.dist-info → pyglove-0.5.0.dev202512080812.dist-info}/RECORD +34 -32
  32. {pyglove-0.5.0.dev202510200810.dist-info → pyglove-0.5.0.dev202512080812.dist-info}/WHEEL +0 -0
  33. {pyglove-0.5.0.dev202510200810.dist-info → pyglove-0.5.0.dev202512080812.dist-info}/licenses/LICENSE +0 -0
  34. {pyglove-0.5.0.dev202510200810.dist-info → pyglove-0.5.0.dev202512080812.dist-info}/top_level.txt +0 -0
@@ -12,10 +12,15 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import datetime
15
16
  import os
16
17
  import pathlib
18
+ import shutil
17
19
  import tempfile
20
+ import time
18
21
  import unittest
22
+ from unittest import mock
23
+ import fsspec
19
24
  from pyglove.core.io import file_system
20
25
 
21
26
 
@@ -82,6 +87,111 @@ class StdFileSystemTest(unittest.TestCase):
82
87
  fs.rmdirs(os.path.join(dir_a, 'b/c'))
83
88
  self.assertEqual(sorted(fs.listdir(dir_a)), ['file1']) # pylint: disable=g-generic-assert
84
89
 
90
+ def test_rename(self):
91
+ tmp_dir = tempfile.mkdtemp()
92
+ fs = file_system.StdFileSystem()
93
+
94
+ _ = fs.mkdirs(os.path.join(tmp_dir, 'a/b'))
95
+ file_foo = os.path.join(tmp_dir, 'a/foo.txt')
96
+ file_bar = os.path.join(tmp_dir, 'a/bar.txt')
97
+
98
+ with fs.open(file_foo, 'w') as f:
99
+ f.write('foo')
100
+ with fs.open(file_bar, 'w') as f:
101
+ f.write('bar')
102
+
103
+ # Rename file to a new name.
104
+ file_foo_new = os.path.join(tmp_dir, 'a/foo-new.txt')
105
+ fs.rename(file_foo, file_foo_new)
106
+ self.assertFalse(fs.exists(file_foo))
107
+ self.assertTrue(fs.exists(file_foo_new))
108
+
109
+ # Rename file to an existing file name.
110
+ fs.rename(file_foo_new, file_bar)
111
+ self.assertFalse(fs.exists(file_foo_new))
112
+ with fs.open(file_bar, 'r') as f:
113
+ self.assertEqual(f.read(), 'foo')
114
+
115
+ # Rename directory to a new name.
116
+ dir_b = os.path.join(tmp_dir, 'a/b')
117
+ dir_c = os.path.join(tmp_dir, 'a/c')
118
+ fs.rename(dir_b, dir_c)
119
+ self.assertFalse(fs.exists(dir_b))
120
+ self.assertTrue(fs.exists(dir_c))
121
+ self.assertTrue(fs.isdir(dir_c))
122
+
123
+ # Rename directory to an existing empty directory.
124
+ dir_d = os.path.join(tmp_dir, 'a/d')
125
+ fs.mkdirs(dir_d)
126
+ fs.rename(dir_c, dir_d)
127
+ self.assertFalse(fs.exists(dir_c))
128
+ self.assertTrue(fs.exists(dir_d))
129
+
130
+ # Rename directory to a non-empty directory.
131
+ dir_x = os.path.join(tmp_dir, 'x')
132
+ dir_a = os.path.join(tmp_dir, 'a')
133
+ fs.mkdirs(os.path.join(dir_x, 'y'))
134
+ with self.assertRaises(OSError):
135
+ fs.rename(dir_a, dir_x)
136
+ self.assertTrue(fs.exists(dir_a))
137
+ self.assertTrue(fs.exists(os.path.join(dir_x, 'y')))
138
+
139
+ # Errors
140
+ dir_u = os.path.join(tmp_dir, 'u')
141
+ dir_u_v = os.path.join(dir_u, 'v')
142
+ file_u_a = os.path.join(dir_u, 'a.txt')
143
+ fs.mkdirs(dir_u_v)
144
+ with fs.open(file_u_a, 'w') as f:
145
+ f.write('a')
146
+
147
+ with self.assertRaises((OSError, NotADirectoryError)):
148
+ fs.rename(dir_u, file_u_a)
149
+
150
+ with self.assertRaises(IsADirectoryError):
151
+ fs.rename(file_u_a, dir_u_v)
152
+
153
+ with self.assertRaises(FileNotFoundError):
154
+ fs.rename(
155
+ os.path.join(tmp_dir, 'non-existent'),
156
+ os.path.join(tmp_dir, 'y')
157
+ )
158
+
159
+ def test_copy(self):
160
+ tmp_dir = tempfile.mkdtemp()
161
+ fs = file_system.StdFileSystem()
162
+ foo_txt = os.path.join(tmp_dir, 'foo.txt')
163
+ bar_txt = os.path.join(tmp_dir, 'bar.txt')
164
+ sub_dir = os.path.join(tmp_dir, 'sub')
165
+ sub_foo_txt = os.path.join(sub_dir, 'foo.txt')
166
+
167
+ with fs.open(foo_txt, 'w') as f:
168
+ f.write('hello')
169
+ fs.copy(foo_txt, bar_txt)
170
+ with fs.open(bar_txt) as f:
171
+ self.assertEqual(f.read(), 'hello')
172
+ fs.mkdir(sub_dir)
173
+ fs.copy(foo_txt, sub_dir)
174
+ with fs.open(sub_foo_txt) as f:
175
+ self.assertEqual(f.read(), 'hello')
176
+ with fs.open(foo_txt, 'w') as f:
177
+ f.write('overwrite')
178
+ fs.copy(foo_txt, bar_txt)
179
+ with fs.open(bar_txt) as f:
180
+ self.assertEqual(f.read(), 'overwrite')
181
+ shutil.rmtree(tmp_dir)
182
+
183
+ def test_getctime_getmtime(self):
184
+ tmp_dir = tempfile.mkdtemp()
185
+ fs = file_system.StdFileSystem()
186
+ file1 = os.path.join(tmp_dir, 'file1')
187
+ with fs.open(file1, 'w') as f:
188
+ f.write('hello')
189
+ ctime = fs.getctime(file1)
190
+ mtime = fs.getmtime(file1)
191
+ self.assertLess(0, ctime)
192
+ self.assertLessEqual(ctime, mtime)
193
+ shutil.rmtree(tmp_dir)
194
+
85
195
 
86
196
  class MemoryFileSystemTest(unittest.TestCase):
87
197
 
@@ -180,6 +290,145 @@ class MemoryFileSystemTest(unittest.TestCase):
180
290
  fs.rmdirs(os.path.join(dir_a, 'b/c'))
181
291
  self.assertEqual(fs.listdir(dir_a), ['file1']) # pylint: disable=g-generic-assert
182
292
 
293
+ def test_glob(self):
294
+ fs = file_system.MemoryFileSystem()
295
+ fs.mkdirs('/mem/a/b/c')
296
+ with fs.open('/mem/a/foo.txt', 'w') as f:
297
+ f.write('foo')
298
+ with fs.open('/mem/a/bar.json', 'w') as f:
299
+ f.write('bar')
300
+ with fs.open('/mem/a/b/baz.txt', 'w') as f:
301
+ f.write('baz')
302
+
303
+ self.assertEqual(
304
+ sorted(fs.glob('/mem/a/*')),
305
+ ['/mem/a/b', '/mem/a/b/baz.txt', '/mem/a/b/c',
306
+ '/mem/a/bar.json', '/mem/a/foo.txt'])
307
+ self.assertEqual(
308
+ sorted(fs.glob('/mem/a/*.txt')),
309
+ ['/mem/a/b/baz.txt', '/mem/a/foo.txt'])
310
+ self.assertEqual(
311
+ sorted(fs.glob('/mem/a/b/*')),
312
+ ['/mem/a/b/baz.txt', '/mem/a/b/c'])
313
+ self.assertEqual(fs.glob('/mem/a/b/*.txt'), ['/mem/a/b/baz.txt'])
314
+ self.assertEqual(fs.glob('/mem/a/b/c/*'), [])
315
+ self.assertEqual(fs.glob('/mem/a/???.txt'), ['/mem/a/foo.txt'])
316
+ self.assertEqual(fs.glob('/mem/a/bar.*'), ['/mem/a/bar.json'])
317
+ self.assertEqual(
318
+ sorted(fs.glob('/mem/a/*.*')),
319
+ ['/mem/a/b/baz.txt', '/mem/a/bar.json', '/mem/a/foo.txt'])
320
+
321
+ def test_rename(self):
322
+ fs = file_system.MemoryFileSystem()
323
+ fs.mkdirs('/mem/a/b')
324
+ with fs.open('/mem/a/foo.txt', 'w') as f:
325
+ f.write('foo')
326
+ with fs.open('/mem/a/bar.txt', 'w') as f:
327
+ f.write('bar')
328
+
329
+ # Rename file to a new name.
330
+ fs.rename('/mem/a/foo.txt', '/mem/a/foo-new.txt')
331
+ self.assertFalse(fs.exists('/mem/a/foo.txt'))
332
+ self.assertTrue(fs.exists('/mem/a/foo-new.txt'))
333
+
334
+ # Rename file to an existing file name.
335
+ fs.rename('/mem/a/foo-new.txt', '/mem/a/bar.txt')
336
+ self.assertFalse(fs.exists('/mem/a/foo-new.txt'))
337
+ with fs.open('/mem/a/bar.txt', 'r') as f:
338
+ self.assertEqual(f.read(), 'foo')
339
+
340
+ # Rename directory to a new name.
341
+ fs.rename('/mem/a/b', '/mem/a/c')
342
+ self.assertFalse(fs.exists('/mem/a/b'))
343
+ self.assertTrue(fs.exists('/mem/a/c'))
344
+ self.assertTrue(fs.isdir('/mem/a/c'))
345
+
346
+ # Rename directory to an existing empty directory.
347
+ fs.mkdirs('/mem/a/d')
348
+ fs.rename('/mem/a/c', '/mem/a/d')
349
+ self.assertFalse(fs.exists('/mem/a/c'))
350
+ self.assertTrue(fs.exists('/mem/a/d'))
351
+
352
+ # Rename directory to a non-empty directory.
353
+ fs.mkdirs('/mem/x/y')
354
+ with self.assertRaisesRegex(OSError, "Directory not empty: '/mem/x'"):
355
+ fs.rename('/mem/a', '/mem/x')
356
+ self.assertTrue(fs.exists('/mem/a'))
357
+ self.assertTrue(fs.exists('/mem/x/y'))
358
+
359
+ # Errors
360
+ fs.mkdirs('/mem/u/v')
361
+ with fs.open('/mem/u/a.txt', 'w') as f:
362
+ f.write('a')
363
+
364
+ with self.assertRaisesRegex(
365
+ OSError, "Cannot move directory '/mem/u' to a subdirectory of itself"):
366
+ fs.rename('/mem/u', '/mem/u/v/w')
367
+
368
+ with self.assertRaisesRegex(
369
+ NotADirectoryError,
370
+ "Cannot rename directory '/mem/u' to non-directory '/mem/u/a.txt'"):
371
+ fs.rename('/mem/u', '/mem/u/a.txt')
372
+
373
+ with self.assertRaisesRegex(
374
+ IsADirectoryError,
375
+ "Cannot rename non-directory '/mem/u/a.txt' to directory '/mem/u/v'"):
376
+ fs.rename('/mem/u/a.txt', '/mem/u/v')
377
+
378
+ with self.assertRaises(FileNotFoundError):
379
+ fs.rename('/mem/non-existent', '/mem/y')
380
+
381
+ def test_copy(self):
382
+ fs = file_system.MemoryFileSystem()
383
+ fs.mkdirs('/mem/a')
384
+ with fs.open('/mem/a/foo.txt', 'w') as f:
385
+ f.write('hello')
386
+ fs.copy('/mem/a/foo.txt', '/mem/a/bar.txt')
387
+ self.assertEqual(fs.open('/mem/a/bar.txt').read(), 'hello')
388
+ fs.mkdir('/mem/b')
389
+ fs.copy('/mem/a/foo.txt', '/mem/b')
390
+ self.assertEqual(fs.open('/mem/b/foo.txt').read(), 'hello')
391
+ with fs.open('/mem/a/foo.txt', 'w') as f:
392
+ f.write('overwrite')
393
+ fs.copy('/mem/a/foo.txt', '/mem/a/bar.txt')
394
+ self.assertEqual(fs.open('/mem/a/bar.txt').read(), 'overwrite')
395
+
396
+ # Test exceptions
397
+ with self.assertRaises(FileNotFoundError):
398
+ fs.copy('/mem/non-existent', '/mem/y')
399
+ with self.assertRaisesRegex(IsADirectoryError, '/mem/a'):
400
+ fs.copy('/mem/a', '/mem/y')
401
+
402
+ fs.mkdirs('/mem/c/foo.txt')
403
+ with self.assertRaisesRegex(IsADirectoryError, '/mem/c/foo.txt'):
404
+ fs.copy('/mem/a/foo.txt', '/mem/c')
405
+
406
+ def test_getctime_getmtime(self):
407
+ fs = file_system.MemoryFileSystem()
408
+ fs.mkdirs('/mem/a')
409
+ file1 = '/mem/file1_times'
410
+ with fs.open(file1, 'w') as f:
411
+ f.write('hello')
412
+ ctime = fs.getctime(file1)
413
+ mtime = fs.getmtime(file1)
414
+ self.assertLess(0, ctime)
415
+ self.assertLess(ctime, mtime)
416
+ time.sleep(0.01)
417
+ with fs.open(file1, 'w') as f:
418
+ f.write('world')
419
+ self.assertEqual(fs.getctime(file1), ctime)
420
+ self.assertLess(mtime, fs.getmtime(file1))
421
+
422
+ with self.assertRaises(IsADirectoryError):
423
+ fs.getctime('/mem/a')
424
+ with self.assertRaises(FileNotFoundError):
425
+ fs.getctime('/mem/non_existent')
426
+
427
+ with self.assertRaises(IsADirectoryError):
428
+ fs.getmtime('/mem/a')
429
+ with self.assertRaises(FileNotFoundError):
430
+ fs.getmtime('/mem/non_existent')
431
+
183
432
 
184
433
  class FileIoApiTest(unittest.TestCase):
185
434
 
@@ -217,6 +466,14 @@ class FileIoApiTest(unittest.TestCase):
217
466
  file_system.rm(file2)
218
467
  self.assertFalse(file_system.path_exists(file2))
219
468
 
469
+ # Test glob with standard file system.
470
+ glob_dir = os.path.join(tempfile.mkdtemp(), 'glob')
471
+ file_system.mkdirs(os.path.join(glob_dir, 'a/b'))
472
+ file_system.writefile(os.path.join(glob_dir, 'a/foo.txt'), 'foo')
473
+ self.assertEqual(
474
+ sorted(file_system.glob(os.path.join(glob_dir, 'a/*'))),
475
+ [os.path.join(glob_dir, 'a/b'), os.path.join(glob_dir, 'a/foo.txt')])
476
+
220
477
  def test_memory_filesystem(self):
221
478
  file1 = pathlib.Path('/mem/file1')
222
479
  with self.assertRaises(FileNotFoundError):
@@ -248,6 +505,191 @@ class FileIoApiTest(unittest.TestCase):
248
505
  file_system.rm(file2)
249
506
  self.assertFalse(file_system.path_exists(file2))
250
507
 
508
+ # Test glob with memory file system.
509
+ file_system.mkdirs('/mem/g/a/b')
510
+ file_system.writefile('/mem/g/a/foo.txt', 'foo')
511
+ file_system.rename('/mem/g/a/foo.txt', '/mem/g/a/foo2.txt')
512
+ file_system.writefile('/mem/g/a/b/bar.txt', 'bar')
513
+ self.assertEqual(
514
+ sorted(file_system.glob('/mem/g/a/*')),
515
+ ['/mem/g/a/b', '/mem/g/a/b/bar.txt', '/mem/g/a/foo2.txt'])
516
+
517
+ def test_getctime_getmtime(self):
518
+ # Test with standard file system.
519
+ std_file = os.path.join(tempfile.mkdtemp(), 'file_ctime_mtime')
520
+ file_system.writefile(std_file, 'foo')
521
+ self.assertLess(0, file_system.getctime(std_file))
522
+ self.assertLess(0, file_system.getmtime(std_file))
523
+
524
+ # Test with memory file system.
525
+ mem_file = '/mem/file_ctime_mtime'
526
+ file_system.writefile(mem_file, 'foo')
527
+ self.assertLess(0, file_system.getctime(mem_file))
528
+ self.assertLess(0, file_system.getmtime(mem_file))
529
+
530
+
531
+ class FsspecFileSystemTest(unittest.TestCase):
532
+
533
+ def setUp(self):
534
+ super().setUp()
535
+ self.fs = fsspec.filesystem('memory')
536
+ self.fs.pipe('memory:///a/b/c', b'abc')
537
+ self.fs.pipe('memory:///a/b/d', b'abd')
538
+ self.fs.mkdir('memory:///a/e')
539
+ self.tmp_dir = tempfile.mkdtemp()
540
+
541
+ def tearDown(self):
542
+ super().tearDown()
543
+ fsspec.filesystem('memory').rm('/', recursive=True)
544
+ shutil.rmtree(self.tmp_dir)
545
+
546
+ def test_read_file(self):
547
+ self.assertEqual(file_system.readfile('memory:///a/b/c', mode='rb'), b'abc')
548
+ with file_system.open('memory:///a/b/d', 'rb') as f:
549
+ self.assertEqual(f.read(), b'abd')
550
+
551
+ def test_fsspec_file_ops(self):
552
+ file_system.writefile('memory:///f', b'hello\nworld\n', mode='wb')
553
+ with file_system.open('memory:///f', 'rb') as f:
554
+ self.assertIsInstance(f, file_system.FsspecFile)
555
+ self.assertEqual(f.readline(), b'hello\n')
556
+ self.assertEqual(f.tell(), 6)
557
+ self.assertEqual(f.seek(8), 8)
558
+ self.assertEqual(f.read(), b'rld\n')
559
+ f.flush()
560
+
561
+ def test_write_file(self):
562
+ file_system.writefile('memory:///a/b/e', b'abe', mode='wb')
563
+ self.assertTrue(self.fs.exists('memory:///a/b/e'))
564
+ self.assertEqual(self.fs.cat('memory:///a/b/e'), b'abe')
565
+
566
+ def test_exists(self):
567
+ self.assertTrue(file_system.path_exists('memory:///a/b/c'))
568
+ self.assertFalse(file_system.path_exists('memory:///a/b/nonexist'))
569
+
570
+ def test_isdir(self):
571
+ self.assertTrue(file_system.isdir('memory:///a/b'))
572
+ self.assertTrue(file_system.isdir('memory:///a/e'))
573
+ self.assertFalse(file_system.isdir('memory:///a/b/c'))
574
+
575
+ def test_listdir(self):
576
+ self.assertCountEqual(file_system.listdir('memory:///a'), ['b', 'e'])
577
+ self.assertCountEqual(file_system.listdir('memory:///a/b'), ['c', 'd'])
578
+
579
+ def test_glob(self):
580
+ self.assertCountEqual(
581
+ file_system.glob('memory:///a/b/*'),
582
+ ['memory:///a/b/c', 'memory:///a/b/d']
583
+ )
584
+
585
+ def test_mkdir(self):
586
+ file_system.mkdir('memory:///a/f')
587
+ self.assertTrue(self.fs.isdir('memory:///a/f'))
588
+
589
+ def test_mkdirs(self):
590
+ file_system.mkdirs('memory:///g/h/i')
591
+ self.assertTrue(self.fs.isdir('memory:///g/h/i'))
592
+
593
+ def test_rm(self):
594
+ file_system.rm('memory:///a/b/c')
595
+ self.assertFalse(self.fs.exists('memory:///a/b/c'))
596
+
597
+ def test_rename(self):
598
+ file_system.rename('memory:///a/b/c', 'memory:///a/b/c_new')
599
+ self.assertFalse(self.fs.exists('memory:///a/b/c'))
600
+ self.assertTrue(self.fs.exists('memory:///a/b/c_new'))
601
+ with self.assertRaisesRegex(ValueError, 'Rename across different'):
602
+ file_system.rename('memory:///a/b/c_new', 'file:///a/b/c_d')
603
+
604
+ def test_chmod(self):
605
+ mock_fs = mock.Mock()
606
+ mock_fs.chmod = mock.Mock()
607
+ with mock.patch('fsspec.core.url_to_fs', return_value=(mock_fs, 'path')):
608
+ file_system.chmod('protocol:///path', 0o777)
609
+ mock_fs.chmod.assert_called_once_with('path', 0o777)
610
+
611
+ def test_rmdir(self):
612
+ file_system.rmdir('memory:///a/e')
613
+ self.assertFalse(self.fs.exists('memory:///a/e'))
614
+
615
+ def test_rmdirs(self):
616
+ file_system.mkdirs('memory:///x/y/z')
617
+ self.assertTrue(file_system.isdir('memory:///x/y/z'))
618
+ file_system.rmdirs('memory:///x')
619
+ self.assertFalse(file_system.path_exists('memory:///x'))
620
+
621
+ def test_copy(self):
622
+ # same FS copy
623
+ file_system.copy('memory:///a/b/c', 'memory:///a/f')
624
+ self.assertEqual(file_system.readfile('memory:///a/f', mode='rb'), b'abc')
625
+
626
+ # same FS copy to dir
627
+ file_system.copy('memory:///a/b/d', 'memory:///a/e')
628
+ self.assertEqual(file_system.readfile('memory:///a/e/d', mode='rb'), b'abd')
629
+
630
+ # cross FS copy: memory to local
631
+ local_path = os.path.join(self.tmp_dir, 'test.txt')
632
+ file_system.copy('memory:///a/b/c', f'file://{local_path}')
633
+ self.assertEqual(file_system.readfile(local_path, mode='rb'), b'abc')
634
+
635
+ # cross FS copy: local to memory
636
+ file_system.copy(f'file://{local_path}', 'memory:///a/g')
637
+ self.assertEqual(file_system.readfile('memory:///a/g', mode='rb'), b'abc')
638
+
639
+ def test_getctime_getmtime(self):
640
+ self.fs.touch('memory:///a/b/c_time')
641
+ now = datetime.datetime.now()
642
+ with mock.patch.object(self.fs, 'created', return_value=now):
643
+ self.assertEqual(
644
+ file_system.getctime('memory:///a/b/c_time'), now.timestamp()
645
+ )
646
+ with mock.patch.object(self.fs, 'modified', return_value=now):
647
+ self.assertEqual(
648
+ file_system.getmtime('memory:///a/b/c_time'), now.timestamp()
649
+ )
650
+
651
+ with mock.patch.object(self.fs, 'created', return_value=None):
652
+ with self.assertRaisesRegex(OSError, 'c-time is not available'):
653
+ file_system.getctime('memory:///a/b/c_time')
654
+
655
+ with mock.patch.object(self.fs, 'modified', return_value=None):
656
+ with self.assertRaisesRegex(OSError, 'm-time is not available'):
657
+ file_system.getmtime('memory:///a/b/c_time')
658
+
659
+ def test_fsspec_uri_catcher(self):
660
+ with mock.patch.object(
661
+ file_system.FsspecFileSystem, 'exists', return_value=True
662
+ ) as mock_fsspec_exists:
663
+ # We use a protocol that is not registered in
664
+ # fsspec.available_protocols() to make sure _FsspecUriCatcher is used.
665
+ self.assertTrue(file_system.path_exists('some-proto://foo'))
666
+ mock_fsspec_exists.assert_called_once_with('some-proto://foo')
667
+
668
+ # For full coverage of _FsspecUriCatcher.get_fs returning StdFileSystem,
669
+ # we need to test a non-URI path that doesn't match other prefixes.
670
+ # We mock StdFileSystem.exists to check if it's called.
671
+ with mock.patch.object(
672
+ file_system.StdFileSystem, 'exists', return_value=True
673
+ ) as mock_std_exists:
674
+ self.assertTrue(file_system.path_exists('/foo/bar/baz'))
675
+ mock_std_exists.assert_called_once_with('/foo/bar/baz')
676
+
677
+ with mock.patch.object(
678
+ file_system.FsspecFileSystem, 'rename', return_value=None
679
+ ) as mock_fsspec_rename:
680
+ file_system.rename('some-proto://foo', 'some-proto://bar')
681
+ mock_fsspec_rename.assert_called_once_with(
682
+ 'some-proto://foo', 'some-proto://bar'
683
+ )
684
+
685
+ with mock.patch.object(
686
+ file_system.FsspecFileSystem, 'copy', return_value=None
687
+ ) as mock_fsspec_copy:
688
+ file_system.copy('some-proto://foo', 'some-proto://bar')
689
+ mock_fsspec_copy.assert_called_once_with(
690
+ 'some-proto://foo', 'some-proto://bar'
691
+ )
692
+
251
693
 
252
694
  if __name__ == '__main__':
253
695
  unittest.main()
@@ -147,4 +147,11 @@ from pyglove.core.symbolic.list import mark_as_insertion
147
147
  from pyglove.core.symbolic.base import WritePermissionError
148
148
  from pyglove.core.symbolic.error_info import ErrorInfo
149
149
 
150
+ # Unknown symbols.
151
+ from pyglove.core.symbolic.unknown_symbols import UnknownSymbol
152
+ from pyglove.core.symbolic.unknown_symbols import UnknownType
153
+ from pyglove.core.symbolic.unknown_symbols import UnknownFunction
154
+ from pyglove.core.symbolic.unknown_symbols import UnknownMethod
155
+ from pyglove.core.symbolic.unknown_symbols import UnknownTypedObject
156
+
150
157
  # pylint: enable=g-bad-import-order