fastcore 2.1.0__tar.gz → 2.1.2__tar.gz
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.
- {fastcore-2.1.0/fastcore.egg-info → fastcore-2.1.2}/PKG-INFO +1 -1
- fastcore-2.1.2/fastcore/__init__.py +1 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/_modidx.py +41 -15
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/basics.py +16 -3
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/foundation.py +5 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/nbio.py +184 -16
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/net.py +39 -2
- fastcore-2.1.2/fastcore/tools.py +221 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/xml.py +20 -8
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/xtras.py +31 -9
- {fastcore-2.1.0 → fastcore-2.1.2/fastcore.egg-info}/PKG-INFO +1 -1
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/entry_points.txt +3 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/pyproject.toml +3 -1
- fastcore-2.1.0/fastcore/__init__.py +0 -1
- fastcore-2.1.0/fastcore/tools.py +0 -247
- {fastcore-2.1.0 → fastcore-2.1.2}/CONTRIBUTING.md +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/LICENSE +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/MANIFEST.in +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/README.md +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/aio.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/all.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/ansi.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/dispatch.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/docments.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/docscrape.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/imghdr.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/imports.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/meta.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/nb_imports.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/parallel.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/py2pyi.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/script.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/shutil.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/style.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/test.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/transform.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/utils.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/xdg.py +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/SOURCES.txt +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/dependency_links.txt +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/requires.txt +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/top_level.txt +0 -0
- {fastcore-2.1.0 → fastcore-2.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.2"
|
|
@@ -119,6 +119,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
119
119
|
'fastcore.basics._eval_type': ('basics.html#_eval_type', 'fastcore/basics.py'),
|
|
120
120
|
'fastcore.basics._get_op': ('basics.html#_get_op', 'fastcore/basics.py'),
|
|
121
121
|
'fastcore.basics._ispy3_10': ('basics.html#_ispy3_10', 'fastcore/basics.py'),
|
|
122
|
+
'fastcore.basics._json_default': ('basics.html#_json_default', 'fastcore/basics.py'),
|
|
122
123
|
'fastcore.basics._mk_op': ('basics.html#_mk_op', 'fastcore/basics.py'),
|
|
123
124
|
'fastcore.basics._nested_idx': ('basics.html#_nested_idx', 'fastcore/basics.py'),
|
|
124
125
|
'fastcore.basics._oper': ('basics.html#_oper', 'fastcore/basics.py'),
|
|
@@ -258,6 +259,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
258
259
|
'fastcore.basics.ver2tuple': ('basics.html#ver2tuple', 'fastcore/basics.py'),
|
|
259
260
|
'fastcore.basics.with_cast': ('basics.html#with_cast', 'fastcore/basics.py'),
|
|
260
261
|
'fastcore.basics.wrap_class': ('basics.html#wrap_class', 'fastcore/basics.py'),
|
|
262
|
+
'fastcore.basics.xdumps': ('basics.html#xdumps', 'fastcore/basics.py'),
|
|
261
263
|
'fastcore.basics.zip_cycle': ('basics.html#zip_cycle', 'fastcore/basics.py')},
|
|
262
264
|
'fastcore.dispatch': {},
|
|
263
265
|
'fastcore.docments': { 'fastcore.docments.DocmentList': ('docments.html#docmentlist', 'fastcore/docments.py'),
|
|
@@ -370,11 +372,13 @@ d = { 'settings': { 'branch': 'main',
|
|
|
370
372
|
'fastcore.foundation.L.__add__': ('foundation.html#l.__add__', 'fastcore/foundation.py'),
|
|
371
373
|
'fastcore.foundation.L.__addi__': ('foundation.html#l.__addi__', 'fastcore/foundation.py'),
|
|
372
374
|
'fastcore.foundation.L.__contains__': ('foundation.html#l.__contains__', 'fastcore/foundation.py'),
|
|
375
|
+
'fastcore.foundation.L.__copy__': ('foundation.html#l.__copy__', 'fastcore/foundation.py'),
|
|
373
376
|
'fastcore.foundation.L.__eq__': ('foundation.html#l.__eq__', 'fastcore/foundation.py'),
|
|
374
377
|
'fastcore.foundation.L.__getitem__': ('foundation.html#l.__getitem__', 'fastcore/foundation.py'),
|
|
375
378
|
'fastcore.foundation.L.__init__': ('foundation.html#l.__init__', 'fastcore/foundation.py'),
|
|
376
379
|
'fastcore.foundation.L.__invert__': ('foundation.html#l.__invert__', 'fastcore/foundation.py'),
|
|
377
380
|
'fastcore.foundation.L.__iter__': ('foundation.html#l.__iter__', 'fastcore/foundation.py'),
|
|
381
|
+
'fastcore.foundation.L.__json__': ('foundation.html#l.__json__', 'fastcore/foundation.py'),
|
|
378
382
|
'fastcore.foundation.L.__mul__': ('foundation.html#l.__mul__', 'fastcore/foundation.py'),
|
|
379
383
|
'fastcore.foundation.L.__radd__': ('foundation.html#l.__radd__', 'fastcore/foundation.py'),
|
|
380
384
|
'fastcore.foundation.L.__repr__': ('foundation.html#l.__repr__', 'fastcore/foundation.py'),
|
|
@@ -479,7 +483,13 @@ d = { 'settings': { 'branch': 'main',
|
|
|
479
483
|
'fastcore.nbio.NbCell.__eq__': ('nbio.html#nbcell.__eq__', 'fastcore/nbio.py'),
|
|
480
484
|
'fastcore.nbio.NbCell.__hash__': ('nbio.html#nbcell.__hash__', 'fastcore/nbio.py'),
|
|
481
485
|
'fastcore.nbio.NbCell.__init__': ('nbio.html#nbcell.__init__', 'fastcore/nbio.py'),
|
|
486
|
+
'fastcore.nbio.NbCell.__setattr__': ('nbio.html#nbcell.__setattr__', 'fastcore/nbio.py'),
|
|
487
|
+
'fastcore.nbio.NbCell.__setitem__': ('nbio.html#nbcell.__setitem__', 'fastcore/nbio.py'),
|
|
488
|
+
'fastcore.nbio.NbCell._partition': ('nbio.html#nbcell._partition', 'fastcore/nbio.py'),
|
|
489
|
+
'fastcore.nbio.NbCell.directive': ('nbio.html#nbcell.directive', 'fastcore/nbio.py'),
|
|
490
|
+
'fastcore.nbio.NbCell.has_directive': ('nbio.html#nbcell.has_directive', 'fastcore/nbio.py'),
|
|
482
491
|
'fastcore.nbio.NbCell.parsed_': ('nbio.html#nbcell.parsed_', 'fastcore/nbio.py'),
|
|
492
|
+
'fastcore.nbio.NbCell.remove_directives': ('nbio.html#nbcell.remove_directives', 'fastcore/nbio.py'),
|
|
483
493
|
'fastcore.nbio.NbCell.set_source': ('nbio.html#nbcell.set_source', 'fastcore/nbio.py'),
|
|
484
494
|
'fastcore.nbio.Notebook': ('nbio.html#notebook', 'fastcore/nbio.py'),
|
|
485
495
|
'fastcore.nbio.Notebook.__contains__': ('nbio.html#notebook.__contains__', 'fastcore/nbio.py'),
|
|
@@ -502,8 +512,14 @@ d = { 'settings': { 'branch': 'main',
|
|
|
502
512
|
'fastcore.nbio.Notebook.save': ('nbio.html#notebook.save', 'fastcore/nbio.py'),
|
|
503
513
|
'fastcore.nbio.Notebook.view': ('nbio.html#notebook.view', 'fastcore/nbio.py'),
|
|
504
514
|
'fastcore.nbio._dict2obj': ('nbio.html#_dict2obj', 'fastcore/nbio.py'),
|
|
515
|
+
'fastcore.nbio._dir_line': ('nbio.html#_dir_line', 'fastcore/nbio.py'),
|
|
516
|
+
'fastcore.nbio._dir_pre': ('nbio.html#_dir_pre', 'fastcore/nbio.py'),
|
|
517
|
+
'fastcore.nbio._directive': ('nbio.html#_directive', 'fastcore/nbio.py'),
|
|
518
|
+
'fastcore.nbio._directives_get': ('nbio.html#_directives_get', 'fastcore/nbio.py'),
|
|
519
|
+
'fastcore.nbio._directives_set': ('nbio.html#_directives_set', 'fastcore/nbio.py'),
|
|
505
520
|
'fastcore.nbio._is_json_mime': ('nbio.html#_is_json_mime', 'fastcore/nbio.py'),
|
|
506
521
|
'fastcore.nbio._join': ('nbio.html#_join', 'fastcore/nbio.py'),
|
|
522
|
+
'fastcore.nbio._meta_directives': ('nbio.html#_meta_directives', 'fastcore/nbio.py'),
|
|
507
523
|
'fastcore.nbio._mkout': ('nbio.html#_mkout', 'fastcore/nbio.py'),
|
|
508
524
|
'fastcore.nbio._read_json': ('nbio.html#_read_json', 'fastcore/nbio.py'),
|
|
509
525
|
'fastcore.nbio._rejoin_cell': ('nbio.html#_rejoin_cell', 'fastcore/nbio.py'),
|
|
@@ -511,10 +527,12 @@ d = { 'settings': { 'branch': 'main',
|
|
|
511
527
|
'fastcore.nbio._render_text': ('nbio.html#_render_text', 'fastcore/nbio.py'),
|
|
512
528
|
'fastcore.nbio._split_cell': ('nbio.html#_split_cell', 'fastcore/nbio.py'),
|
|
513
529
|
'fastcore.nbio._split_mime': ('nbio.html#_split_mime', 'fastcore/nbio.py'),
|
|
530
|
+
'fastcore.nbio._unparse_dir': ('nbio.html#_unparse_dir', 'fastcore/nbio.py'),
|
|
514
531
|
'fastcore.nbio.cell2xml': ('nbio.html#cell2xml', 'fastcore/nbio.py'),
|
|
515
532
|
'fastcore.nbio.cells2xml': ('nbio.html#cells2xml', 'fastcore/nbio.py'),
|
|
516
533
|
'fastcore.nbio.concat_streams': ('nbio.html#concat_streams', 'fastcore/nbio.py'),
|
|
517
534
|
'fastcore.nbio.dict2nb': ('nbio.html#dict2nb', 'fastcore/nbio.py'),
|
|
535
|
+
'fastcore.nbio.first_code_ln': ('nbio.html#first_code_ln', 'fastcore/nbio.py'),
|
|
518
536
|
'fastcore.nbio.mk_cell': ('nbio.html#mk_cell', 'fastcore/nbio.py'),
|
|
519
537
|
'fastcore.nbio.mk_display': ('nbio.html#mk_display', 'fastcore/nbio.py'),
|
|
520
538
|
'fastcore.nbio.mk_error': ('nbio.html#mk_error', 'fastcore/nbio.py'),
|
|
@@ -522,6 +540,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
522
540
|
'fastcore.nbio.mk_stream': ('nbio.html#mk_stream', 'fastcore/nbio.py'),
|
|
523
541
|
'fastcore.nbio.nb2dict': ('nbio.html#nb2dict', 'fastcore/nbio.py'),
|
|
524
542
|
'fastcore.nbio.nb2str': ('nbio.html#nb2str', 'fastcore/nbio.py'),
|
|
543
|
+
'fastcore.nbio.nb_lang': ('nbio.html#nb_lang', 'fastcore/nbio.py'),
|
|
525
544
|
'fastcore.nbio.new_nb': ('nbio.html#new_nb', 'fastcore/nbio.py'),
|
|
526
545
|
'fastcore.nbio.preferred_msg_out': ('nbio.html#preferred_msg_out', 'fastcore/nbio.py'),
|
|
527
546
|
'fastcore.nbio.preferred_out': ('nbio.html#preferred_out', 'fastcore/nbio.py'),
|
|
@@ -529,6 +548,10 @@ d = { 'settings': { 'branch': 'main',
|
|
|
529
548
|
'fastcore.nbio.render_output': ('nbio.html#render_output', 'fastcore/nbio.py'),
|
|
530
549
|
'fastcore.nbio.render_outputs': ('nbio.html#render_outputs', 'fastcore/nbio.py'),
|
|
531
550
|
'fastcore.nbio.render_text': ('nbio.html#render_text', 'fastcore/nbio.py'),
|
|
551
|
+
'fastcore.nbio.repair_cell': ('nbio.html#repair_cell', 'fastcore/nbio.py'),
|
|
552
|
+
'fastcore.nbio.repair_nb': ('nbio.html#repair_nb', 'fastcore/nbio.py'),
|
|
553
|
+
'fastcore.nbio.validate_cell': ('nbio.html#validate_cell', 'fastcore/nbio.py'),
|
|
554
|
+
'fastcore.nbio.validate_nb': ('nbio.html#validate_nb', 'fastcore/nbio.py'),
|
|
532
555
|
'fastcore.nbio.write_nb': ('nbio.html#write_nb', 'fastcore/nbio.py')},
|
|
533
556
|
'fastcore.net': { 'fastcore.net.HTTP4xxClientError': ('net.html#http4xxclienterror', 'fastcore/net.py'),
|
|
534
557
|
'fastcore.net.HTTP5xxServerError': ('net.html#http5xxservererror', 'fastcore/net.py'),
|
|
@@ -536,7 +559,9 @@ d = { 'settings': { 'branch': 'main',
|
|
|
536
559
|
'fastcore.net._SafeRedirectHandler.redirect_request': ( 'net.html#_saferedirecthandler.redirect_request',
|
|
537
560
|
'fastcore/net.py'),
|
|
538
561
|
'fastcore.net._socket_det': ('net.html#_socket_det', 'fastcore/net.py'),
|
|
562
|
+
'fastcore.net._waitfor': ('net.html#_waitfor', 'fastcore/net.py'),
|
|
539
563
|
'fastcore.net.http_response': ('net.html#http_response', 'fastcore/net.py'),
|
|
564
|
+
'fastcore.net.is_port_free': ('net.html#is_port_free', 'fastcore/net.py'),
|
|
540
565
|
'fastcore.net.recv_once': ('net.html#recv_once', 'fastcore/net.py'),
|
|
541
566
|
'fastcore.net.start_client': ('net.html#start_client', 'fastcore/net.py'),
|
|
542
567
|
'fastcore.net.start_server': ('net.html#start_server', 'fastcore/net.py'),
|
|
@@ -551,7 +576,11 @@ d = { 'settings': { 'branch': 'main',
|
|
|
551
576
|
'fastcore.net.urlretrieve': ('net.html#urlretrieve', 'fastcore/net.py'),
|
|
552
577
|
'fastcore.net.urlsave': ('net.html#urlsave', 'fastcore/net.py'),
|
|
553
578
|
'fastcore.net.urlvalid': ('net.html#urlvalid', 'fastcore/net.py'),
|
|
554
|
-
'fastcore.net.urlwrap': ('net.html#urlwrap', 'fastcore/net.py')
|
|
579
|
+
'fastcore.net.urlwrap': ('net.html#urlwrap', 'fastcore/net.py'),
|
|
580
|
+
'fastcore.net.wait_port_free': ('net.html#wait_port_free', 'fastcore/net.py'),
|
|
581
|
+
'fastcore.net.wait_port_free_async': ('net.html#wait_port_free_async', 'fastcore/net.py'),
|
|
582
|
+
'fastcore.net.waitfor': ('net.html#waitfor', 'fastcore/net.py'),
|
|
583
|
+
'fastcore.net.waitfor_async': ('net.html#waitfor_async', 'fastcore/net.py')},
|
|
555
584
|
'fastcore.parallel': { 'fastcore.parallel.ProcessPoolExecutor': ('parallel.html#processpoolexecutor', 'fastcore/parallel.py'),
|
|
556
585
|
'fastcore.parallel.ProcessPoolExecutor.__init__': ( 'parallel.html#processpoolexecutor.__init__',
|
|
557
586
|
'fastcore/parallel.py'),
|
|
@@ -646,22 +675,16 @@ d = { 'settings': { 'branch': 'main',
|
|
|
646
675
|
'fastcore.test.test_shuffled': ('test.html#test_shuffled', 'fastcore/test.py'),
|
|
647
676
|
'fastcore.test.test_stdout': ('test.html#test_stdout', 'fastcore/test.py'),
|
|
648
677
|
'fastcore.test.test_warns': ('test.html#test_warns', 'fastcore/test.py')},
|
|
649
|
-
'fastcore.tools': { 'fastcore.tools.
|
|
650
|
-
'fastcore.tools.
|
|
651
|
-
'fastcore.tools.
|
|
652
|
-
'fastcore.tools.
|
|
653
|
-
'fastcore.tools.
|
|
654
|
-
'fastcore.tools.
|
|
655
|
-
'fastcore.tools.
|
|
656
|
-
'fastcore.tools.move_lines': ('tools.html#move_lines', 'fastcore/tools.py'),
|
|
678
|
+
'fastcore.tools': { 'fastcore.tools._norm_lines': ('tools.html#_norm_lines', 'fastcore/tools.py'),
|
|
679
|
+
'fastcore.tools.ast_replace': ('tools.html#ast_replace', 'fastcore/tools.py'),
|
|
680
|
+
'fastcore.tools.del_lines': ('tools.html#del_lines', 'fastcore/tools.py'),
|
|
681
|
+
'fastcore.tools.file_create': ('tools.html#file_create', 'fastcore/tools.py'),
|
|
682
|
+
'fastcore.tools.file_edit': ('tools.html#file_edit', 'fastcore/tools.py'),
|
|
683
|
+
'fastcore.tools.file_view': ('tools.html#file_view', 'fastcore/tools.py'),
|
|
684
|
+
'fastcore.tools.insert_line': ('tools.html#insert_line', 'fastcore/tools.py'),
|
|
657
685
|
'fastcore.tools.replace_lines': ('tools.html#replace_lines', 'fastcore/tools.py'),
|
|
658
|
-
'fastcore.tools.rg': ('tools.html#rg', 'fastcore/tools.py'),
|
|
659
|
-
'fastcore.tools.run_cmd': ('tools.html#run_cmd', 'fastcore/tools.py'),
|
|
660
|
-
'fastcore.tools.sed': ('tools.html#sed', 'fastcore/tools.py'),
|
|
661
686
|
'fastcore.tools.str_replace': ('tools.html#str_replace', 'fastcore/tools.py'),
|
|
662
|
-
'fastcore.tools.strs_replace': ('tools.html#strs_replace', 'fastcore/tools.py'),
|
|
663
|
-
'fastcore.tools.valid_path': ('tools.html#valid_path', 'fastcore/tools.py'),
|
|
664
|
-
'fastcore.tools.view': ('tools.html#view', 'fastcore/tools.py')},
|
|
687
|
+
'fastcore.tools.strs_replace': ('tools.html#strs_replace', 'fastcore/tools.py')},
|
|
665
688
|
'fastcore.transform': {},
|
|
666
689
|
'fastcore.utils': {},
|
|
667
690
|
'fastcore.xdg': { 'fastcore.xdg._path_from_env': ('xdg.html#_path_from_env', 'fastcore/xdg.py'),
|
|
@@ -706,6 +729,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
706
729
|
'fastcore.xml.dict2xml': ('xml.html#dict2xml', 'fastcore/xml.py'),
|
|
707
730
|
'fastcore.xml.ft': ('xml.html#ft', 'fastcore/xml.py'),
|
|
708
731
|
'fastcore.xml.highlight': ('xml.html#highlight', 'fastcore/xml.py'),
|
|
732
|
+
'fastcore.xml.item2xml': ('xml.html#item2xml', 'fastcore/xml.py'),
|
|
709
733
|
'fastcore.xml.mk_getattr': ('xml.html#mk_getattr', 'fastcore/xml.py'),
|
|
710
734
|
'fastcore.xml.showtags': ('xml.html#showtags', 'fastcore/xml.py'),
|
|
711
735
|
'fastcore.xml.to_xml': ('xml.html#to_xml', 'fastcore/xml.py'),
|
|
@@ -849,7 +873,9 @@ d = { 'settings': { 'branch': 'main',
|
|
|
849
873
|
'fastcore.xtras.shufflish': ('xtras.html#shufflish', 'fastcore/xtras.py'),
|
|
850
874
|
'fastcore.xtras.sparkline': ('xtras.html#sparkline', 'fastcore/xtras.py'),
|
|
851
875
|
'fastcore.xtras.ssh': ('xtras.html#ssh', 'fastcore/xtras.py'),
|
|
876
|
+
'fastcore.xtras.str_diff': ('xtras.html#str_diff', 'fastcore/xtras.py'),
|
|
852
877
|
'fastcore.xtras.stringfmt_names': ('xtras.html#stringfmt_names', 'fastcore/xtras.py'),
|
|
878
|
+
'fastcore.xtras.take_lines': ('xtras.html#take_lines', 'fastcore/xtras.py'),
|
|
853
879
|
'fastcore.xtras.time_policy': ('xtras.html#time_policy', 'fastcore/xtras.py'),
|
|
854
880
|
'fastcore.xtras.timed_cache': ('xtras.html#timed_cache', 'fastcore/xtras.py'),
|
|
855
881
|
'fastcore.xtras.trace': ('xtras.html#trace', 'fastcore/xtras.py'),
|
|
@@ -22,12 +22,12 @@ __all__ = ['defaults', 'null', 'num_methods', 'rnum_methods', 'inum_methods', 'a
|
|
|
22
22
|
'fail_clean', 'dstar', 'copy_func', 'patch_to', 'patch', 'extend_enum', 'compile_re', 'ImportEnum',
|
|
23
23
|
'StrEnum', 'str_enum', 'ValEnum', 'Stateful', 'NotStr', 'PrettyString', 'even_mults', 'num_cpus',
|
|
24
24
|
'add_props', 'str2bool', 'str2int', 'str2float', 'str2list', 'str2date', 'to_bool', 'to_int', 'to_float',
|
|
25
|
-
'to_list', 'to_date', 'typed', 'exec_new', 'exec_import', 'sig_with_params', 'fdelegates', '
|
|
26
|
-
'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'mod']
|
|
25
|
+
'to_list', 'to_date', 'typed', 'exec_new', 'exec_import', 'sig_with_params', 'fdelegates', 'xdumps', 'lt',
|
|
26
|
+
'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv', 'is_', 'is_not', 'mod']
|
|
27
27
|
|
|
28
28
|
# %% ../nbs/01_basics.ipynb #0e91ed82
|
|
29
29
|
from .imports import *
|
|
30
|
-
import builtins,types,typing
|
|
30
|
+
import builtins,types,typing,json
|
|
31
31
|
from functools import cmp_to_key,wraps
|
|
32
32
|
from copy import copy
|
|
33
33
|
from datetime import date
|
|
@@ -1394,3 +1394,16 @@ def fdelegates(to):
|
|
|
1394
1394
|
f.__signature__ = sig_with_params(sig, remove=['kwargs'], **new_params)
|
|
1395
1395
|
return f
|
|
1396
1396
|
return _f
|
|
1397
|
+
|
|
1398
|
+
# %% ../nbs/01_basics.ipynb #3e6f50e5
|
|
1399
|
+
def _json_default(o, default=None):
|
|
1400
|
+
f = getattr(o, '__json__', None)
|
|
1401
|
+
if f: return f()
|
|
1402
|
+
if default: return default(o)
|
|
1403
|
+
raise TypeError(f'Object of type {type(o).__name__} is not JSON serializable')
|
|
1404
|
+
|
|
1405
|
+
def xdumps(o, **kwargs):
|
|
1406
|
+
"`json.dumps`, using `__json__` methods when available"
|
|
1407
|
+
default = kwargs.pop('default', None)
|
|
1408
|
+
return json.dumps(o, default=partial(_json_default, default=default), **kwargs)
|
|
1409
|
+
|
|
@@ -153,6 +153,7 @@ class L(GetAttr, CollBase, metaclass=_L_Meta):
|
|
|
153
153
|
return all_equal(b,self)
|
|
154
154
|
|
|
155
155
|
def __iter__(self): return iter(self.items.itertuples() if hasattr(self.items,'iloc') else self.items)
|
|
156
|
+
def __json__(self): return list(self)
|
|
156
157
|
def __contains__(self,b): return b in self.items
|
|
157
158
|
def __reversed__(self): return self._new(reversed(self.items))
|
|
158
159
|
def __invert__(self): return self._new(not i for i in self)
|
|
@@ -362,6 +363,10 @@ def copy(self:L):
|
|
|
362
363
|
"Same as `list.copy`, but returns an `L`"
|
|
363
364
|
return self._new(self.items.copy())
|
|
364
365
|
|
|
366
|
+
# %% ../nbs/02_foundation.ipynb #0becb556
|
|
367
|
+
@patch
|
|
368
|
+
def __copy__(self:L): return self.copy()
|
|
369
|
+
|
|
365
370
|
# %% ../nbs/02_foundation.ipynb #7b2691d6
|
|
366
371
|
@patch
|
|
367
372
|
def shuffle(self:L):
|
|
@@ -5,18 +5,20 @@ Docs: https://fastcore.fast.ai/nbio.html.md"""
|
|
|
5
5
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/13_nbio.ipynb.
|
|
6
6
|
|
|
7
7
|
# %% auto #0
|
|
8
|
-
__all__ = ['
|
|
8
|
+
__all__ = ['langs', 'nb_lang', 'NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'first_code_ln', 'nb2dict', 'nb2str',
|
|
9
|
+
'write_nb', 'validate_cell', 'validate_nb', 'repair_cell', 'repair_nb', 'preferred_out', 'mk_stream',
|
|
9
10
|
'mk_result', 'mk_display', 'mk_error', 'concat_streams', 'preferred_msg_out', 'render_output',
|
|
10
11
|
'render_outputs', 'render_text', 'cell2xml', 'cells2xml', 'Notebook']
|
|
11
12
|
|
|
12
13
|
# %% ../nbs/13_nbio.ipynb #954ca1aa
|
|
13
14
|
from .basics import *
|
|
14
|
-
from .xtras import rtoken_hex,clean_cli_output
|
|
15
|
+
from .xtras import rtoken_hex,clean_cli_output,take_lines
|
|
15
16
|
from .imports import *
|
|
16
17
|
from .ansi import ansi2html
|
|
17
18
|
from .meta import delegates
|
|
18
19
|
|
|
19
20
|
import ast,functools
|
|
21
|
+
from collections import defaultdict
|
|
20
22
|
from pprint import pformat,pprint
|
|
21
23
|
from json import loads,dumps
|
|
22
24
|
|
|
@@ -42,18 +44,39 @@ def _rejoin_cell(c):
|
|
|
42
44
|
if o.get('output_type') in ('execute_result','display_data'): _rejoin_mime(o.get('data', {}))
|
|
43
45
|
elif o.get('output_type')=='stream' and isinstance(o.get('text'), list): o['text'] = ''.join(o['text'])
|
|
44
46
|
|
|
47
|
+
# %% ../nbs/13_nbio.ipynb #e13f61b8
|
|
48
|
+
# from https://github.com/quarto-dev/quarto-cli/blob/main/src/resources/jupyter/notebook.py
|
|
49
|
+
langs = defaultdict(
|
|
50
|
+
lambda: '#', r = "#", python = "#", julia = "#", scala = "//", matlab = "%", csharp = "//", fsharp = "//",
|
|
51
|
+
c = ["/*","*/"], css = ["/*","*/"], sas = ["*",";"], powershell = "#", bash = "#", sql = "--", mysql = "--", psql = "--",
|
|
52
|
+
lua = "--", cpp = "//", cc = "//", stan = "#", octave = "#", fortran = "!", fortran95 = "!", awk = "#", gawk = "#", stata = "*",
|
|
53
|
+
java = "//", groovy = "//", sed = "#", perl = "#", ruby = "#", tikz = "%", javascript = "//", js = "//", d3 = "//", node = "//",
|
|
54
|
+
sass = "//", coffee = "#", go = "//", asy = "//", haskell = "--", dot = "//", apl = "⍝")
|
|
55
|
+
|
|
56
|
+
def nb_lang(nb): return nested_attr(nb, 'metadata.kernelspec.language', 'python')
|
|
57
|
+
|
|
45
58
|
# %% ../nbs/13_nbio.ipynb #49d7bf89
|
|
46
59
|
class NbCell(AttrDict):
|
|
47
|
-
def __init__(self, idx, cell):
|
|
60
|
+
def __init__(self, idx, cell, lang='python'):
|
|
48
61
|
super().__init__(cell)
|
|
49
62
|
self.idx_ = idx
|
|
63
|
+
self.lang_ = nested_attr(self, 'metadata.language', lang)
|
|
50
64
|
if 'id' not in self: self.id = rtoken_hex(4)
|
|
51
65
|
if 'source' in self: self.set_source(self.source)
|
|
52
66
|
_rejoin_cell(self)
|
|
53
67
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
if
|
|
68
|
+
def __setattr__(self, k, v):
|
|
69
|
+
prop = getattr(type(self), k, None)
|
|
70
|
+
if isinstance(prop, property) and prop.fset: prop.fset(self, v)
|
|
71
|
+
else: super().__setattr__(k, v)
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, k, v):
|
|
74
|
+
super().__setitem__(k, v)
|
|
75
|
+
if k in ('source','metadata'):
|
|
76
|
+
for c in ('_parsed_','_directives_','_meta_names_'):
|
|
77
|
+
if c in self: del(self[c])
|
|
78
|
+
|
|
79
|
+
def set_source(self, source): self.source = ''.join(source)
|
|
57
80
|
|
|
58
81
|
def parsed_(self):
|
|
59
82
|
if self.cell_type!='code' or self.source.strip()[:1] in ['%', '!']: return
|
|
@@ -77,7 +100,7 @@ def _dict2obj(d, list_func=list, dict_func=AttrDict):
|
|
|
77
100
|
def dict2nb(js=None, **kwargs):
|
|
78
101
|
"Convert dict `js` to an `AttrDict`, "
|
|
79
102
|
nb = _dict2obj(js or kwargs)
|
|
80
|
-
nb.cells = [NbCell(
|
|
103
|
+
nb.cells = [NbCell(i, o, nb_lang(nb)) for i,o in enumerate(nb.cells)]
|
|
81
104
|
return nb
|
|
82
105
|
|
|
83
106
|
# %% ../nbs/13_nbio.ipynb #0626f06e
|
|
@@ -104,9 +127,94 @@ def mk_cell(
|
|
|
104
127
|
# %% ../nbs/13_nbio.ipynb #e7af3290
|
|
105
128
|
def new_nb(cells=None, meta=None, nbformat=4, nbformat_minor=5):
|
|
106
129
|
"Returns an empty new notebook"
|
|
107
|
-
cells = [o if isinstance(o,dict) else mk_cell(o) for o in cells]
|
|
130
|
+
cells = [o if isinstance(o,dict) else mk_cell(o) for o in cells or []]
|
|
108
131
|
return dict2nb(cells=cells or [],metadata=meta or {},nbformat=nbformat,nbformat_minor=nbformat_minor)
|
|
109
132
|
|
|
133
|
+
# %% ../nbs/13_nbio.ipynb #c2ed0d5e
|
|
134
|
+
def _dir_pre(lang=None): return fr"\s*{langs[lang]}\s*\|"
|
|
135
|
+
_cell_mgc = re.compile(r"^\s*%%\w+")
|
|
136
|
+
|
|
137
|
+
def first_code_ln(code_list, re_pattern=None, lang='python'):
|
|
138
|
+
"get first line number where code occurs, where `code_list` is a list of code"
|
|
139
|
+
if re_pattern is None: re_pattern = _dir_pre(lang)
|
|
140
|
+
return first(i for i,o in enumerate(code_list) if o.strip() != '' and not re.match(re_pattern, o) and not _cell_mgc.match(o))
|
|
141
|
+
|
|
142
|
+
# %% ../nbs/13_nbio.ipynb #b0cc33c0
|
|
143
|
+
def _directive(s, lang='python'):
|
|
144
|
+
"Parse a directive line into `(name, value)`; None if `s` is not a directive line"
|
|
145
|
+
if not re.match(_dir_pre(lang), s): return None
|
|
146
|
+
s = re.sub('^'+_dir_pre(lang), '', s).strip()
|
|
147
|
+
m = re.match(r'([^\s:]+)\s*:?\s?(.*?)\s*$', s, flags=re.S)
|
|
148
|
+
if not m: return None
|
|
149
|
+
name,v = m.groups()
|
|
150
|
+
return name, ('' if v=='true' else v)
|
|
151
|
+
|
|
152
|
+
# %% ../nbs/13_nbio.ipynb #16c08410
|
|
153
|
+
def _meta_directives(cell):
|
|
154
|
+
"Directives from the `nbdev` dict in cell metadata, as `{name: value}`"
|
|
155
|
+
def _v(v):
|
|
156
|
+
if isinstance(v,bool): return '' if v else 'false'
|
|
157
|
+
if isinstance(v,(list,tuple)): return ' '.join(map(str,v))
|
|
158
|
+
return '' if v=='true' else str(v)
|
|
159
|
+
return {k:_v(v) for k,v in cell.get('metadata',{}).get('nbdev',{}).items()}
|
|
160
|
+
|
|
161
|
+
def _unparse_dir(v):
|
|
162
|
+
"Inverse of `_meta_directives` value parsing: value string back to a metadata value"
|
|
163
|
+
if v=='': return True
|
|
164
|
+
if v=='false': return False
|
|
165
|
+
return v
|
|
166
|
+
|
|
167
|
+
def _dir_line(k, v, lang='python', quarto=False):
|
|
168
|
+
"A canonical directive comment line; with `quarto`, bare directives render as `: true`"
|
|
169
|
+
if quarto and not v: v = 'true'
|
|
170
|
+
return f"{langs[lang]}| {k}" + (f": {v}" if v else '') + '\n'
|
|
171
|
+
|
|
172
|
+
# %% ../nbs/13_nbio.ipynb #428ecb6a
|
|
173
|
+
@patch
|
|
174
|
+
def _partition(self:NbCell):
|
|
175
|
+
"Split source into leading directive lines (with magics and blanks) and the rest"
|
|
176
|
+
dirs = take_lines(self.source, fr'^({_dir_pre(self.lang_)}|\s*%%\w+|\s*$)')
|
|
177
|
+
return dirs.splitlines(True), self.source[len(dirs):].splitlines(True)
|
|
178
|
+
|
|
179
|
+
def _directives_get(self):
|
|
180
|
+
"Directives from leading comments and the metadata `nbdev` key; comments win. Assign a modified copy back to update the cell."
|
|
181
|
+
if '_directives_' not in self:
|
|
182
|
+
dirs,_ = self._partition()
|
|
183
|
+
cmts = dict(t for t in (_directive(s, self.lang_) for s in dirs) if t)
|
|
184
|
+
meta = _meta_directives(self)
|
|
185
|
+
self['_meta_names_'] = set(meta) - set(cmts)
|
|
186
|
+
self['_directives_'] = cmts | {k:v for k,v in meta.items() if k not in cmts}
|
|
187
|
+
return dict(self['_directives_'])
|
|
188
|
+
|
|
189
|
+
def _directives_set(self, d):
|
|
190
|
+
if d == self.directives: return
|
|
191
|
+
mnames = self['_meta_names_']
|
|
192
|
+
meta = {k:_unparse_dir(v) for k,v in d.items() if k in mnames}
|
|
193
|
+
lines = [_dir_line(k, v, self.lang_) for k,v in d.items() if k not in mnames]
|
|
194
|
+
if meta: self.setdefault('metadata', AttrDict())['nbdev'] = meta
|
|
195
|
+
elif 'nbdev' in self.get('metadata', {}): del self.metadata['nbdev']
|
|
196
|
+
dirs,code = self._partition()
|
|
197
|
+
self.set_source(''.join([o for o in dirs if _cell_mgc.match(o)] + lines + code))
|
|
198
|
+
|
|
199
|
+
NbCell.directives = property(_directives_get, _directives_set)
|
|
200
|
+
|
|
201
|
+
# %% ../nbs/13_nbio.ipynb #281f7f8a
|
|
202
|
+
@patch
|
|
203
|
+
def directive(self:NbCell, name, default=None):
|
|
204
|
+
"Value of directive `name` (`''` if bare), or `default` if absent"
|
|
205
|
+
return self.directives.get(name, default)
|
|
206
|
+
|
|
207
|
+
@patch
|
|
208
|
+
def has_directive(self:NbCell, name): return self.directive(name) is not None
|
|
209
|
+
|
|
210
|
+
@patch
|
|
211
|
+
def remove_directives(self:NbCell, quarto=False):
|
|
212
|
+
"Strip directives from source, keeping cell magics; with `quarto`, instead materialize every directive (metadata included) as a Quarto option line"
|
|
213
|
+
dirs,code = self._partition()
|
|
214
|
+
keep = [o for o in dirs if _cell_mgc.match(o)]
|
|
215
|
+
if quarto: keep += [_dir_line(k, v, self.lang_, quarto=True) for k,v in self.directives.items()]
|
|
216
|
+
self.set_source(''.join(keep + code))
|
|
217
|
+
|
|
110
218
|
# %% ../nbs/13_nbio.ipynb #7c7bab22
|
|
111
219
|
def _split_mime(data):
|
|
112
220
|
"Copy of mimebundle `data` with multiline text split to lists of lines, like nbformat's `split_lines`"
|
|
@@ -147,8 +255,73 @@ def write_nb(nb, path):
|
|
|
147
255
|
if new!=old:
|
|
148
256
|
with open(path, 'w', encoding='utf-8') as f: f.write(new)
|
|
149
257
|
|
|
258
|
+
# %% ../nbs/13_nbio.ipynb #86453c0f
|
|
259
|
+
def validate_cell(cell, idx=None):
|
|
260
|
+
"Raise `ValueError` for structural problems in notebook cell dict `cell`; returns it unchanged if fine"
|
|
261
|
+
where = f"cell {cell.get('id', idx)}"
|
|
262
|
+
ct = cell.get('cell_type')
|
|
263
|
+
if ct not in ('code','markdown','raw'): raise ValueError(f"{where}: unknown cell_type {ct!r}")
|
|
264
|
+
src = cell.get('source', '')
|
|
265
|
+
if not (isinstance(src,str) or (isinstance(src,list) and all(isinstance(o,str) for o in src))):
|
|
266
|
+
raise ValueError(f"{where}: source must be str or list of str")
|
|
267
|
+
if not isinstance(cell.get('metadata', {}), dict): raise ValueError(f"{where}: metadata must be a dict")
|
|
268
|
+
if ct=='code':
|
|
269
|
+
if not isinstance(cell.get('outputs'), list): raise ValueError(f"{where}: code cell requires an outputs list")
|
|
270
|
+
if 'execution_count' not in cell: raise ValueError(f"{where}: code cell requires execution_count")
|
|
271
|
+
elif k := first(k for k in ('outputs','execution_count') if k in cell): raise ValueError(f"{where}: {k} not allowed in a {ct} cell")
|
|
272
|
+
return cell
|
|
273
|
+
|
|
274
|
+
def validate_nb(nb):
|
|
275
|
+
"Raise `ValueError` for structural problems in notebook `nb`; returns it unchanged if fine"
|
|
276
|
+
if k := first(k for k in ('cells','metadata','nbformat') if k not in nb): raise ValueError(f"notebook requires {k!r}")
|
|
277
|
+
for i,c in enumerate(nb['cells']): validate_cell(c, i)
|
|
278
|
+
ids = [c.get('id') for c in nb['cells'] if c.get('id')]
|
|
279
|
+
if dups := {o for o in ids if ids.count(o)>1}: raise ValueError(f"duplicate cell id(s): {', '.join(sorted(dups))}")
|
|
280
|
+
return nb
|
|
281
|
+
|
|
282
|
+
# %% ../nbs/13_nbio.ipynb #d38902eb
|
|
283
|
+
def repair_cell(cell, idx=None):
|
|
284
|
+
"Fix deterministic structural problems in `cell`, returning a list of repairs made"
|
|
285
|
+
res,where = [],f"cell {cell.get('id', idx)}"
|
|
286
|
+
src = cell.get('source', '')
|
|
287
|
+
if not (isinstance(src,str) or (isinstance(src,list) and all(isinstance(o,str) for o in src))):
|
|
288
|
+
cell['source'] = ''.join(map(str, listify(src)))
|
|
289
|
+
res.append(f"{where}: coerced source to text")
|
|
290
|
+
if not isinstance(cell.get('metadata', {}), dict):
|
|
291
|
+
cell['metadata'] = {}
|
|
292
|
+
res.append(f"{where}: reset non-dict metadata")
|
|
293
|
+
if cell.get('cell_type')=='code':
|
|
294
|
+
if not isinstance(cell.get('outputs'), list):
|
|
295
|
+
cell['outputs'] = []
|
|
296
|
+
res.append(f"{where}: added outputs list")
|
|
297
|
+
if 'execution_count' not in cell:
|
|
298
|
+
cell['execution_count'] = None
|
|
299
|
+
res.append(f"{where}: added execution_count")
|
|
300
|
+
else:
|
|
301
|
+
for k in ('outputs','execution_count'):
|
|
302
|
+
if k in cell:
|
|
303
|
+
del cell[k]
|
|
304
|
+
res.append(f"{where}: removed {k}")
|
|
305
|
+
return res
|
|
306
|
+
|
|
307
|
+
def repair_nb(nb):
|
|
308
|
+
"Fix deterministic structural problems in `nb`, returning a list of repairs made"
|
|
309
|
+
res = []
|
|
310
|
+
for k,v in dict(cells=[], metadata={}, nbformat=4).items():
|
|
311
|
+
if k not in nb:
|
|
312
|
+
nb[k] = v
|
|
313
|
+
res.append(f"added notebook {k}")
|
|
314
|
+
for i,c in enumerate(nb['cells']): res += repair_cell(c, i)
|
|
315
|
+
seen = set()
|
|
316
|
+
for c in nb['cells']:
|
|
317
|
+
if (i := c.get('id')) in seen:
|
|
318
|
+
c['id'] = rtoken_hex(4)
|
|
319
|
+
res.append(f"cell {i}: duplicate id, regenerated as {c['id']}")
|
|
320
|
+
seen.add(c.get('id'))
|
|
321
|
+
return res
|
|
322
|
+
|
|
150
323
|
# %% ../nbs/13_nbio.ipynb #530b9cd1
|
|
151
|
-
from .xml import
|
|
324
|
+
from .xml import NB,to_xml,ft,item2xml
|
|
152
325
|
|
|
153
326
|
# %% ../nbs/13_nbio.ipynb #9f22b923
|
|
154
327
|
def preferred_out(data, html1st=True, include_imgs=False):
|
|
@@ -252,15 +425,10 @@ def render_text(outputs, html1st=False):
|
|
|
252
425
|
return items[0][0] if len(items)==1 else '\n'.join(o[1] for o in items)
|
|
253
426
|
|
|
254
427
|
# %% ../nbs/13_nbio.ipynb #ca73be1c
|
|
255
|
-
_ctfuns_nb = dict(code=Code, markdown=Markdown, raw=Raw)
|
|
256
|
-
|
|
257
428
|
def cell2xml(cell, ids=True, incl_out=True):
|
|
258
429
|
"Convert NbCell to concise XML format"
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
output = getattr(cell, 'outputs', None) if incl_out else None
|
|
262
|
-
if not output: return f(cell.source, **kw)
|
|
263
|
-
return f(Source(cell.source), Out(render_text(output)), **kw)
|
|
430
|
+
outputs = getattr(cell, 'outputs', None) if incl_out else None
|
|
431
|
+
return item2xml(cell.cell_type, cell.source, render_text(outputs) if outputs else '', id=cell.id if ids else None)
|
|
264
432
|
|
|
265
433
|
def cells2xml(cells, wrap=NB, ids=True, incl_out=True, **kw):
|
|
266
434
|
"Convert notebook cells to XML format"
|
|
@@ -7,7 +7,8 @@ Docs: https://fastcore.fast.ai/net.html.md"""
|
|
|
7
7
|
# %% auto #0
|
|
8
8
|
__all__ = ['url_default_headers', 'ExceptionsHTTP', 'urlquote', 'urlwrap', 'HTTP4xxClientError', 'HTTP5xxServerError',
|
|
9
9
|
'urlopener', 'urlopen', 'urlread', 'urljson', 'urlclean', 'urlretrieve', 'urldest', 'urlsave', 'urlvalid',
|
|
10
|
-
'start_server', 'start_client', 'tobytes', 'http_response', 'recv_once', '
|
|
10
|
+
'start_server', 'start_client', 'tobytes', 'http_response', 'recv_once', 'waitfor', 'waitfor_async',
|
|
11
|
+
'is_port_free', 'wait_port_free', 'wait_port_free_async', 'HTTP400BadRequestError',
|
|
11
12
|
'HTTP401UnauthorizedError', 'HTTP402PaymentRequiredError', 'HTTP403ForbiddenError', 'HTTP404NotFoundError',
|
|
12
13
|
'HTTP405MethodNotAllowedError', 'HTTP406NotAcceptableError', 'HTTP407ProxyAuthRequiredError',
|
|
13
14
|
'HTTP408RequestTimeoutError', 'HTTP409ConflictError', 'HTTP410GoneError', 'HTTP411LengthRequiredError',
|
|
@@ -23,7 +24,7 @@ from .utils import *
|
|
|
23
24
|
from .parallel import *
|
|
24
25
|
|
|
25
26
|
import json,urllib,contextlib,tempfile
|
|
26
|
-
import socket,urllib.request,urllib,asyncio,threading
|
|
27
|
+
import socket,urllib.request,urllib,asyncio,threading,time
|
|
27
28
|
from contextlib import contextmanager
|
|
28
29
|
from urllib.request import Request,urlretrieve
|
|
29
30
|
from urllib.error import HTTPError,URLError
|
|
@@ -240,3 +241,39 @@ def recv_once(host:str='localhost', port:int=8000):
|
|
|
240
241
|
res = conn.recv(1024)
|
|
241
242
|
conn.sendall(http_response(res))
|
|
242
243
|
return res
|
|
244
|
+
|
|
245
|
+
# %% ../nbs/03b_net.ipynb #8896fe36
|
|
246
|
+
def _waitfor(f, timeout, msg):
|
|
247
|
+
end = time.time()+timeout
|
|
248
|
+
while not f():
|
|
249
|
+
if time.time()>end: raise TimeoutError(msg or f"Timeout after {timeout}s")
|
|
250
|
+
yield 0.1
|
|
251
|
+
|
|
252
|
+
def waitfor(f, timeout=20, msg=None):
|
|
253
|
+
"Call `f` every 0.1s until it returns truthy, raising `TimeoutError` with `msg` after `timeout` secs"
|
|
254
|
+
for d in _waitfor(f, timeout, msg): time.sleep(d)
|
|
255
|
+
|
|
256
|
+
async def waitfor_async(f, timeout=20, msg=None):
|
|
257
|
+
"Async version of `waitfor`"
|
|
258
|
+
for d in _waitfor(f, timeout, msg): await asyncio.sleep(d)
|
|
259
|
+
|
|
260
|
+
# %% ../nbs/03b_net.ipynb #38a94895
|
|
261
|
+
def is_port_free(port, host='localhost'):
|
|
262
|
+
"Is `port` free on `host`?"
|
|
263
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
264
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
265
|
+
try:
|
|
266
|
+
sock.bind((host,port))
|
|
267
|
+
sock.listen(1)
|
|
268
|
+
return True
|
|
269
|
+
except OSError: return False
|
|
270
|
+
finally: sock.close()
|
|
271
|
+
|
|
272
|
+
# %% ../nbs/03b_net.ipynb #e0a7369a
|
|
273
|
+
def wait_port_free(port, host='localhost', max_wait=20):
|
|
274
|
+
"Wait for `port` to be free on `host`"
|
|
275
|
+
waitfor(partial(is_port_free, port, host), max_wait, f"Port {host}:{port} not free after {max_wait}s")
|
|
276
|
+
|
|
277
|
+
async def wait_port_free_async(port, host='localhost', max_wait=20):
|
|
278
|
+
"Async wait for `port` to be free on `host`"
|
|
279
|
+
await waitfor_async(partial(is_port_free, port, host), max_wait, f"Port {host}:{port} not free after {max_wait}s")
|