fastcore 2.1.1__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 (42) hide show
  1. {fastcore-2.1.1/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.1 → fastcore-2.1.2}/fastcore/_modidx.py +25 -1
  4. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/nbio.py +159 -10
  5. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/net.py +39 -2
  6. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/xtras.py +19 -8
  7. {fastcore-2.1.1 → fastcore-2.1.2/fastcore.egg-info}/PKG-INFO +1 -1
  8. {fastcore-2.1.1 → fastcore-2.1.2}/pyproject.toml +0 -1
  9. fastcore-2.1.1/fastcore/__init__.py +0 -1
  10. {fastcore-2.1.1 → fastcore-2.1.2}/CONTRIBUTING.md +0 -0
  11. {fastcore-2.1.1 → fastcore-2.1.2}/LICENSE +0 -0
  12. {fastcore-2.1.1 → fastcore-2.1.2}/MANIFEST.in +0 -0
  13. {fastcore-2.1.1 → fastcore-2.1.2}/README.md +0 -0
  14. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/aio.py +0 -0
  15. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/all.py +0 -0
  16. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/ansi.py +0 -0
  17. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/basics.py +0 -0
  18. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/dispatch.py +0 -0
  19. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/docments.py +0 -0
  20. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/docscrape.py +0 -0
  21. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/foundation.py +0 -0
  22. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/imghdr.py +0 -0
  23. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/imports.py +0 -0
  24. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/meta.py +0 -0
  25. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/nb_imports.py +0 -0
  26. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/parallel.py +0 -0
  27. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/py2pyi.py +0 -0
  28. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/script.py +0 -0
  29. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/shutil.py +0 -0
  30. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/style.py +0 -0
  31. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/test.py +0 -0
  32. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/tools.py +0 -0
  33. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/transform.py +0 -0
  34. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/utils.py +0 -0
  35. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/xdg.py +0 -0
  36. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore/xml.py +0 -0
  37. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore.egg-info/SOURCES.txt +0 -0
  38. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore.egg-info/dependency_links.txt +0 -0
  39. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore.egg-info/entry_points.txt +0 -0
  40. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore.egg-info/requires.txt +0 -0
  41. {fastcore-2.1.1 → fastcore-2.1.2}/fastcore.egg-info/top_level.txt +0 -0
  42. {fastcore-2.1.1 → 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.1
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"
@@ -483,7 +483,13 @@ d = { 'settings': { 'branch': 'main',
483
483
  'fastcore.nbio.NbCell.__eq__': ('nbio.html#nbcell.__eq__', 'fastcore/nbio.py'),
484
484
  'fastcore.nbio.NbCell.__hash__': ('nbio.html#nbcell.__hash__', 'fastcore/nbio.py'),
485
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'),
486
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'),
487
493
  'fastcore.nbio.NbCell.set_source': ('nbio.html#nbcell.set_source', 'fastcore/nbio.py'),
488
494
  'fastcore.nbio.Notebook': ('nbio.html#notebook', 'fastcore/nbio.py'),
489
495
  'fastcore.nbio.Notebook.__contains__': ('nbio.html#notebook.__contains__', 'fastcore/nbio.py'),
@@ -506,8 +512,14 @@ d = { 'settings': { 'branch': 'main',
506
512
  'fastcore.nbio.Notebook.save': ('nbio.html#notebook.save', 'fastcore/nbio.py'),
507
513
  'fastcore.nbio.Notebook.view': ('nbio.html#notebook.view', 'fastcore/nbio.py'),
508
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'),
509
520
  'fastcore.nbio._is_json_mime': ('nbio.html#_is_json_mime', 'fastcore/nbio.py'),
510
521
  'fastcore.nbio._join': ('nbio.html#_join', 'fastcore/nbio.py'),
522
+ 'fastcore.nbio._meta_directives': ('nbio.html#_meta_directives', 'fastcore/nbio.py'),
511
523
  'fastcore.nbio._mkout': ('nbio.html#_mkout', 'fastcore/nbio.py'),
512
524
  'fastcore.nbio._read_json': ('nbio.html#_read_json', 'fastcore/nbio.py'),
513
525
  'fastcore.nbio._rejoin_cell': ('nbio.html#_rejoin_cell', 'fastcore/nbio.py'),
@@ -515,10 +527,12 @@ d = { 'settings': { 'branch': 'main',
515
527
  'fastcore.nbio._render_text': ('nbio.html#_render_text', 'fastcore/nbio.py'),
516
528
  'fastcore.nbio._split_cell': ('nbio.html#_split_cell', 'fastcore/nbio.py'),
517
529
  'fastcore.nbio._split_mime': ('nbio.html#_split_mime', 'fastcore/nbio.py'),
530
+ 'fastcore.nbio._unparse_dir': ('nbio.html#_unparse_dir', 'fastcore/nbio.py'),
518
531
  'fastcore.nbio.cell2xml': ('nbio.html#cell2xml', 'fastcore/nbio.py'),
519
532
  'fastcore.nbio.cells2xml': ('nbio.html#cells2xml', 'fastcore/nbio.py'),
520
533
  'fastcore.nbio.concat_streams': ('nbio.html#concat_streams', 'fastcore/nbio.py'),
521
534
  'fastcore.nbio.dict2nb': ('nbio.html#dict2nb', 'fastcore/nbio.py'),
535
+ 'fastcore.nbio.first_code_ln': ('nbio.html#first_code_ln', 'fastcore/nbio.py'),
522
536
  'fastcore.nbio.mk_cell': ('nbio.html#mk_cell', 'fastcore/nbio.py'),
523
537
  'fastcore.nbio.mk_display': ('nbio.html#mk_display', 'fastcore/nbio.py'),
524
538
  'fastcore.nbio.mk_error': ('nbio.html#mk_error', 'fastcore/nbio.py'),
@@ -526,6 +540,7 @@ d = { 'settings': { 'branch': 'main',
526
540
  'fastcore.nbio.mk_stream': ('nbio.html#mk_stream', 'fastcore/nbio.py'),
527
541
  'fastcore.nbio.nb2dict': ('nbio.html#nb2dict', 'fastcore/nbio.py'),
528
542
  'fastcore.nbio.nb2str': ('nbio.html#nb2str', 'fastcore/nbio.py'),
543
+ 'fastcore.nbio.nb_lang': ('nbio.html#nb_lang', 'fastcore/nbio.py'),
529
544
  'fastcore.nbio.new_nb': ('nbio.html#new_nb', 'fastcore/nbio.py'),
530
545
  'fastcore.nbio.preferred_msg_out': ('nbio.html#preferred_msg_out', 'fastcore/nbio.py'),
531
546
  'fastcore.nbio.preferred_out': ('nbio.html#preferred_out', 'fastcore/nbio.py'),
@@ -533,6 +548,8 @@ d = { 'settings': { 'branch': 'main',
533
548
  'fastcore.nbio.render_output': ('nbio.html#render_output', 'fastcore/nbio.py'),
534
549
  'fastcore.nbio.render_outputs': ('nbio.html#render_outputs', 'fastcore/nbio.py'),
535
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'),
536
553
  'fastcore.nbio.validate_cell': ('nbio.html#validate_cell', 'fastcore/nbio.py'),
537
554
  'fastcore.nbio.validate_nb': ('nbio.html#validate_nb', 'fastcore/nbio.py'),
538
555
  'fastcore.nbio.write_nb': ('nbio.html#write_nb', 'fastcore/nbio.py')},
@@ -542,7 +559,9 @@ d = { 'settings': { 'branch': 'main',
542
559
  'fastcore.net._SafeRedirectHandler.redirect_request': ( 'net.html#_saferedirecthandler.redirect_request',
543
560
  'fastcore/net.py'),
544
561
  'fastcore.net._socket_det': ('net.html#_socket_det', 'fastcore/net.py'),
562
+ 'fastcore.net._waitfor': ('net.html#_waitfor', 'fastcore/net.py'),
545
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'),
546
565
  'fastcore.net.recv_once': ('net.html#recv_once', 'fastcore/net.py'),
547
566
  'fastcore.net.start_client': ('net.html#start_client', 'fastcore/net.py'),
548
567
  'fastcore.net.start_server': ('net.html#start_server', 'fastcore/net.py'),
@@ -557,7 +576,11 @@ d = { 'settings': { 'branch': 'main',
557
576
  'fastcore.net.urlretrieve': ('net.html#urlretrieve', 'fastcore/net.py'),
558
577
  'fastcore.net.urlsave': ('net.html#urlsave', 'fastcore/net.py'),
559
578
  'fastcore.net.urlvalid': ('net.html#urlvalid', 'fastcore/net.py'),
560
- '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')},
561
584
  'fastcore.parallel': { 'fastcore.parallel.ProcessPoolExecutor': ('parallel.html#processpoolexecutor', 'fastcore/parallel.py'),
562
585
  'fastcore.parallel.ProcessPoolExecutor.__init__': ( 'parallel.html#processpoolexecutor.__init__',
563
586
  'fastcore/parallel.py'),
@@ -852,6 +875,7 @@ d = { 'settings': { 'branch': 'main',
852
875
  'fastcore.xtras.ssh': ('xtras.html#ssh', 'fastcore/xtras.py'),
853
876
  'fastcore.xtras.str_diff': ('xtras.html#str_diff', 'fastcore/xtras.py'),
854
877
  'fastcore.xtras.stringfmt_names': ('xtras.html#stringfmt_names', 'fastcore/xtras.py'),
878
+ 'fastcore.xtras.take_lines': ('xtras.html#take_lines', 'fastcore/xtras.py'),
855
879
  'fastcore.xtras.time_policy': ('xtras.html#time_policy', 'fastcore/xtras.py'),
856
880
  'fastcore.xtras.timed_cache': ('xtras.html#timed_cache', 'fastcore/xtras.py'),
857
881
  'fastcore.xtras.trace': ('xtras.html#trace', 'fastcore/xtras.py'),
@@ -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', 'validate_cell', 'validate_nb',
9
- 'preferred_out', 'mk_stream', 'mk_result', 'mk_display', 'mk_error', 'concat_streams', 'preferred_msg_out',
10
- 'render_output', 'render_outputs', 'render_text', 'cell2xml', 'cells2xml', 'Notebook']
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',
10
+ 'mk_result', 'mk_display', 'mk_error', 'concat_streams', 'preferred_msg_out', 'render_output',
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`"
@@ -171,6 +279,47 @@ def validate_nb(nb):
171
279
  if dups := {o for o in ids if ids.count(o)>1}: raise ValueError(f"duplicate cell id(s): {', '.join(sorted(dups))}")
172
280
  return nb
173
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
+
174
323
  # %% ../nbs/13_nbio.ipynb #530b9cd1
175
324
  from .xml import NB,to_xml,ft,item2xml
176
325
 
@@ -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")
@@ -9,14 +9,14 @@ __all__ = ['UNSET', 'spark_chars', 'walk_join', 'walk', 'exttypes', 'globtastic'
9
9
  'img_bytes', 'detect_mime', 'bunzip', 'loads', 'loads_multi', 'dumps', 'untar_dir', 'repo_details', 'shell',
10
10
  'ssh', 'rsync_multi', 'run', 'open_file', 'save_pickle', 'load_pickle', 'parse_env', 'expand_wildcards',
11
11
  'atomic_save', 'load_mod', 'import_no_init', 'save_config_file', 'read_config_file', 'find_file_parents',
12
- 'Config', 'Unset', 'dict2obj', 'obj2dict', 'repr_dict', 'is_listy', 'mapped', 'IterLen', 'ReindexCollection',
13
- 'SaveReturn', 'trim_wraps', 'save_iter', 'asave_iter', 'frontmatter', 'clean_cli_output', 'unqid',
14
- 'rtoken_hex', 'friendly_name', 'n_friendly_names', 'exec_eval', 'get_source_link', 'sparkline',
15
- 'modify_exception', 'round_multiple', 'set_num_threads', 'join_path_file', 'autostart', 'EventTimer',
16
- 'stringfmt_names', 'PartialFormatter', 'partial_format', 'truncstr', 'str_diff', 'utc2local', 'local2utc',
17
- 'trace', 'modified_env', 'ContextManagers', 'shufflish', 'console_help', 'hl_md', 'type2str',
18
- 'dataclass_src', 'nullable_dc', 'make_nullable', 'flexiclass', 'asdict', 'vars_pub', 'is_typeddict',
19
- 'is_namedtuple', 'CachedIter', 'flexicache', 'time_policy', 'mtime_policy', 'timed_cache']
12
+ 'Config', 'Unset', 'dict2obj', 'obj2dict', 'repr_dict', 'is_listy', 'mapped', 'take_lines', 'IterLen',
13
+ 'ReindexCollection', 'SaveReturn', 'trim_wraps', 'save_iter', 'asave_iter', 'frontmatter',
14
+ 'clean_cli_output', 'unqid', 'rtoken_hex', 'friendly_name', 'n_friendly_names', 'exec_eval',
15
+ 'get_source_link', 'sparkline', 'modify_exception', 'round_multiple', 'set_num_threads', 'join_path_file',
16
+ 'autostart', 'EventTimer', 'stringfmt_names', 'PartialFormatter', 'partial_format', 'truncstr', 'str_diff',
17
+ 'utc2local', 'local2utc', 'trace', 'modified_env', 'ContextManagers', 'shufflish', 'console_help', 'hl_md',
18
+ 'type2str', 'dataclass_src', 'nullable_dc', 'make_nullable', 'flexiclass', 'asdict', 'vars_pub',
19
+ 'is_typeddict', 'is_namedtuple', 'CachedIter', 'flexicache', 'time_policy', 'mtime_policy', 'timed_cache']
20
20
 
21
21
  # %% ../nbs/03_xtras.ipynb #3401d507
22
22
  from .imports import *
@@ -562,6 +562,17 @@ def mapped(f, it):
562
562
  "map `f` over `it`, unless it's not listy, in which case return `f(it)`"
563
563
  return L(it).map(f) if is_listy(it) else f(it)
564
564
 
565
+ # %% ../nbs/03_xtras.ipynb #062ca713
566
+ def take_lines(s, pat, atstart=True, atend=False, drop=False):
567
+ "Take contiguous lines matching `pat` from either end of `s`"
568
+ ls,pred = s.splitlines(keepends=True),re.compile(pat).search
569
+ i,j = 0,len(ls)
570
+ if atstart:
571
+ while i<j and pred(ls[i]): i += 1
572
+ if atend:
573
+ while i<j and pred(ls[j-1]): j -= 1
574
+ return ''.join(ls[i:j] if drop else ls[:i]+ls[j:])
575
+
565
576
  # %% ../nbs/03_xtras.ipynb #ab0758e6
566
577
  @patch
567
578
  def readlines(self:Path, hint=-1, encoding='utf8'):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.1.1
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
@@ -44,6 +44,5 @@ tst_flags = ''
44
44
  custom_sidebar = false
45
45
  allowed_cell_metadata_keys = ['solveit_ai']
46
46
 
47
-
48
47
  [tool.chkstyle]
49
48
  skip_paths = ["_modidx.py"]
@@ -1 +0,0 @@
1
- __version__ = "2.1.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes