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.
Files changed (43) hide show
  1. {fastcore-2.1.0/fastcore.egg-info → fastcore-2.1.2}/PKG-INFO +1 -1
  2. fastcore-2.1.2/fastcore/__init__.py +1 -0
  3. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/_modidx.py +41 -15
  4. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/basics.py +16 -3
  5. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/foundation.py +5 -0
  6. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/nbio.py +184 -16
  7. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/net.py +39 -2
  8. fastcore-2.1.2/fastcore/tools.py +221 -0
  9. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/xml.py +20 -8
  10. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/xtras.py +31 -9
  11. {fastcore-2.1.0 → fastcore-2.1.2/fastcore.egg-info}/PKG-INFO +1 -1
  12. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/entry_points.txt +3 -0
  13. {fastcore-2.1.0 → fastcore-2.1.2}/pyproject.toml +3 -1
  14. fastcore-2.1.0/fastcore/__init__.py +0 -1
  15. fastcore-2.1.0/fastcore/tools.py +0 -247
  16. {fastcore-2.1.0 → fastcore-2.1.2}/CONTRIBUTING.md +0 -0
  17. {fastcore-2.1.0 → fastcore-2.1.2}/LICENSE +0 -0
  18. {fastcore-2.1.0 → fastcore-2.1.2}/MANIFEST.in +0 -0
  19. {fastcore-2.1.0 → fastcore-2.1.2}/README.md +0 -0
  20. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/aio.py +0 -0
  21. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/all.py +0 -0
  22. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/ansi.py +0 -0
  23. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/dispatch.py +0 -0
  24. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/docments.py +0 -0
  25. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/docscrape.py +0 -0
  26. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/imghdr.py +0 -0
  27. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/imports.py +0 -0
  28. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/meta.py +0 -0
  29. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/nb_imports.py +0 -0
  30. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/parallel.py +0 -0
  31. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/py2pyi.py +0 -0
  32. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/script.py +0 -0
  33. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/shutil.py +0 -0
  34. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/style.py +0 -0
  35. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/test.py +0 -0
  36. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/transform.py +0 -0
  37. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/utils.py +0 -0
  38. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore/xdg.py +0 -0
  39. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/SOURCES.txt +0 -0
  40. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/dependency_links.txt +0 -0
  41. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/requires.txt +0 -0
  42. {fastcore-2.1.0 → fastcore-2.1.2}/fastcore.egg-info/top_level.txt +0 -0
  43. {fastcore-2.1.0 → fastcore-2.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Python supercharged for fastai development
5
5
  Author-email: Jeremy Howard and Sylvain Gugger <infos@fast.ai>
6
6
  License: Apache-2.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._fmt_path': ('tools.html#_fmt_path', 'fastcore/tools.py'),
650
- 'fastcore.tools._load_valid_paths': ('tools.html#_load_valid_paths', 'fastcore/tools.py'),
651
- 'fastcore.tools.create': ('tools.html#create', 'fastcore/tools.py'),
652
- 'fastcore.tools.ensure': ('tools.html#ensure', 'fastcore/tools.py'),
653
- 'fastcore.tools.explain_exc': ('tools.html#explain_exc', 'fastcore/tools.py'),
654
- 'fastcore.tools.get_callable': ('tools.html#get_callable', 'fastcore/tools.py'),
655
- 'fastcore.tools.insert': ('tools.html#insert', 'fastcore/tools.py'),
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', 'lt', 'gt', 'le',
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__ = ['NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'nb2dict', 'nb2str', 'write_nb', 'preferred_out', 'mk_stream',
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 set_source(self, source):
55
- self.source = ''.join(source)
56
- if '_parsed_' in self: del(self['_parsed_'])
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(*o) for o in enumerate(nb.cells)]
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 Code,Markdown,Raw,NB,Source,Out,to_xml,ft
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
- f = _ctfuns_nb[cell.cell_type]
260
- kw = dict(id=cell.id) if ids else {}
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', 'HTTP400BadRequestError',
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")