nbdev 3.3.9__tar.gz → 3.3.11__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 (40) hide show
  1. {nbdev-3.3.9/nbdev.egg-info → nbdev-3.3.11}/PKG-INFO +2 -2
  2. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/__init__.py +1 -1
  3. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/_modidx.py +3 -2
  4. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/processors.py +1 -3
  5. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/serve.py +1 -0
  6. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/test.py +48 -10
  7. {nbdev-3.3.9 → nbdev-3.3.11/nbdev.egg-info}/PKG-INFO +2 -2
  8. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev.egg-info/requires.txt +1 -1
  9. {nbdev-3.3.9 → nbdev-3.3.11}/pyproject.toml +1 -1
  10. {nbdev-3.3.9 → nbdev-3.3.11}/CONTRIBUTING.md +0 -0
  11. {nbdev-3.3.9 → nbdev-3.3.11}/LICENSE +0 -0
  12. {nbdev-3.3.9 → nbdev-3.3.11}/MANIFEST.in +0 -0
  13. {nbdev-3.3.9 → nbdev-3.3.11}/README.md +0 -0
  14. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/clean.py +0 -0
  15. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/cli.py +0 -0
  16. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/config.py +0 -0
  17. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/diff.py +0 -0
  18. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/doclinks.py +0 -0
  19. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/export.py +0 -0
  20. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/extract_attachments.py +0 -0
  21. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/frontmatter.py +0 -0
  22. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/imports.py +0 -0
  23. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/maker.py +0 -0
  24. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/merge.py +0 -0
  25. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/migrate.py +0 -0
  26. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/moddocs.py +0 -0
  27. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/process.py +0 -0
  28. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/qmd.py +0 -0
  29. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/quarto.py +1 -1
  30. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/release.py +0 -0
  31. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/scrubmagics.py +0 -0
  32. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/serve_drv.py +0 -0
  33. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/showdoc.py +0 -0
  34. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/skill.py +0 -0
  35. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev/sync.py +0 -0
  36. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev.egg-info/SOURCES.txt +0 -0
  37. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev.egg-info/dependency_links.txt +0 -0
  38. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev.egg-info/entry_points.txt +0 -0
  39. {nbdev-3.3.9 → nbdev-3.3.11}/nbdev.egg-info/top_level.txt +0 -0
  40. {nbdev-3.3.9 → nbdev-3.3.11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbdev
3
- Version: 3.3.9
3
+ Version: 3.3.11
4
4
  Summary: Create delightful software with Jupyter Notebooks
5
5
  Author-email: "Jeremy Howard and the fast.ai community" <j@fast.ai>
6
6
  License: Apache-2.0
@@ -17,7 +17,7 @@ Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: fastcore>=2.2.7
20
- Requires-Dist: execnb>=0.2.11
20
+ Requires-Dist: execnb>=0.3.2
21
21
  Requires-Dist: astunparse
22
22
  Requires-Dist: ghapi>=2.0.2
23
23
  Requires-Dist: watchdog
@@ -6,7 +6,7 @@ Modules:
6
6
  - `nbdev.moddocs`: # Module docs: creating them from existing notebooks
7
7
  - `nbdev.skill`: Author clear, executable nbdev notebooks where code, prose, examples, outputs, and tests form one coherent narrative."""
8
8
 
9
- __version__ = "3.3.9"
9
+ __version__ = "3.3.11"
10
10
 
11
11
  from .doclinks import nbdev_export
12
12
  from .showdoc import show_doc
@@ -378,7 +378,8 @@ d = { 'settings': { 'branch': 'main',
378
378
  'nbdev.sync._update_nb': ('api/sync.html#_update_nb', 'nbdev/sync.py'),
379
379
  'nbdev.sync.absolute_import': ('api/sync.html#absolute_import', 'nbdev/sync.py'),
380
380
  'nbdev.sync.nbdev_update': ('api/sync.html#nbdev_update', 'nbdev/sync.py')},
381
- 'nbdev.test': { 'nbdev.test._keep_file': ('api/test.html#_keep_file', 'nbdev/test.py'),
382
- 'nbdev.test._test_nb_sync': ('api/test.html#_test_nb_sync', 'nbdev/test.py'),
381
+ 'nbdev.test': { 'nbdev.test._await_chain': ('api/test.html#_await_chain', 'nbdev/test.py'),
382
+ 'nbdev.test._int_handler': ('api/test.html#_int_handler', 'nbdev/test.py'),
383
+ 'nbdev.test._keep_file': ('api/test.html#_keep_file', 'nbdev/test.py'),
383
384
  'nbdev.test.nbdev_test': ('api/test.html#nbdev_test', 'nbdev/test.py'),
384
385
  'nbdev.test.test_nb': ('api/test.html#test_nb', 'nbdev/test.py')}}}
@@ -12,8 +12,6 @@ __all__ = ['populate_language', 'insert_warning', 'cell_lang', 'add_show_docs',
12
12
  # %% ../nbs/api/10_processors.ipynb #2398f5ef-06d3-4890-8a54-7cf4f81f3894
13
13
  import ast
14
14
 
15
- from fastcore.aio import run_sync
16
-
17
15
  from .config import *
18
16
  from .imports import *
19
17
  from .process import *
@@ -257,7 +255,7 @@ class exec_show_docs(Processor):
257
255
  if not self.nb.has_docs_ or not hasattr(self, 'k'): return
258
256
  fm = getattr(self.nb, 'frontmatter_', {})
259
257
  if str2bool(fm.get('skip_showdoc', False)): return
260
- if _do_eval(cell): run_sync(self.k.cell(cell))
258
+ if _do_eval(cell): self.k.cell(cell)
261
259
  title = fm.get('title', '')
262
260
  if self.k.exc:
263
261
  raise Exception(f"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\n{cell.source}") from self.k.exc
@@ -84,6 +84,7 @@ def proc_nbs(
84
84
  cache.mkdir(parents=True, exist_ok=True)
85
85
  cache_mtime = cache.stat().st_mtime
86
86
  if force or (cache.exists() and cache_mtime<chk_mtime): rmtree(cache)
87
+ (cache/'_quarto.yml').unlink(missing_ok=True) # single-doc renders strip the staged copy in place (`_strip_sidebar`), so always restage it
87
88
 
88
89
  files = files.map(_proc_file, mtime=cache_mtime, cache=cache, path=path).filter()
89
90
  kw = {} if IN_NOTEBOOK else {'method':'spawn'}
@@ -8,7 +8,7 @@ Docs: https://nbdev.fast.ai/api/test.html.md"""
8
8
  __all__ = ['test_nb', 'nbdev_test']
9
9
 
10
10
  # %% ../nbs/api/12_test.ipynb #45e10c3f
11
- import time,os,sys,traceback,contextlib,inspect,faulthandler,signal,asyncio
11
+ import time,os,sys,io,traceback,contextlib,inspect,signal,asyncio,threading
12
12
  from fastcore.basics import *
13
13
  from fastcore.imports import *
14
14
  from fastcore.foundation import *
@@ -24,8 +24,38 @@ from .frontmatter import nb_frontmatter
24
24
  from fastcore.nbio import *
25
25
  from execnb.shell import *
26
26
 
27
+ # %% ../nbs/api/12_test.ipynb #dc8994ac
28
+ _cur_nb = [None]
29
+ _cur_shell = [None]
30
+
31
+ def _await_chain(t):
32
+ "One frame per coroutine in task `t`'s await chain, deepest last: where a suspended hang actually sits"
33
+ co = t.get_coro()
34
+ while co is not None:
35
+ f = getattr(co, 'cr_frame', None) or getattr(co, 'ag_frame', None) or getattr(co, 'gi_frame', None)
36
+ if f is not None: yield f, f.f_lineno
37
+ co = getattr(co, 'cr_await', None) or getattr(co, 'ag_await', None) or getattr(co, 'gi_yieldfrom', None)
38
+
39
+ def _int_handler(signum, frame):
40
+ "Dump the running notebook's stack and exit; installed on SIGINT by `test_nb`"
41
+ if _cur_nb[0] is not None:
42
+ buf = io.StringIO()
43
+ traceback.print_stack(frame, file=buf)
44
+ k = _cur_shell[0]
45
+ if k is not None:
46
+ f = sys._current_frames().get(k._thread.ident)
47
+ if f is not None:
48
+ print('\n--- shell loop thread ---', file=buf)
49
+ traceback.print_stack(f, file=buf)
50
+ with contextlib.suppress(RuntimeError):
51
+ for t in asyncio.all_tasks(k.loop):
52
+ print(f'\n{t}', file=buf)
53
+ buf.writelines(traceback.StackSummary.extract(_await_chain(t)).format())
54
+ os.write(2, f'\n=== nbdev-test interrupted: {_cur_nb[0]} ===\n{buf.getvalue()}'.encode())
55
+ os._exit(130)
56
+
27
57
  # %% ../nbs/api/12_test.ipynb #3f4fa1ad
28
- async def test_nb(
58
+ def test_nb(
29
59
  fn, # file name of notebook to test
30
60
  skip_flags=None, # list of flags marking cells to skip
31
61
  force_flags=None, # list of flags marking cells to always run
@@ -34,11 +64,13 @@ async def test_nb(
34
64
  basepath=None, # path to add to sys.path
35
65
  verbose=False, # stream stdout/stderr from cells to console?
36
66
  save=False, # write outputs back to notebook on success?
37
- profile:bool=None # load the IPython profile, as `ipykernel` does? (default: `exec_profile` config key)
67
+ profile:bool=None, # load the IPython profile, as `ipykernel` does? (default: `exec_profile` config key)
68
+ cell_timeout:int=600 # seconds before each cell times out (None: no limit)
38
69
  ):
39
70
  "Execute tests in notebook in `fn` except those with `skip_flags`"
40
- faulthandler.register(signal.SIGINT, file=sys.__stderr__, all_threads=True, chain=True)
71
+ if not IN_NOTEBOOK and threading.current_thread() is threading.main_thread(): signal.signal(signal.SIGINT, _int_handler)
41
72
  fn = Path(fn)
73
+ _cur_nb[0] = fn
42
74
  if basepath: sys.path.insert(0, str(basepath))
43
75
  prev_test = os.environ.get('IN_TEST')
44
76
  if not IN_NOTEBOOK: os.environ['IN_TEST'] = '1'
@@ -57,25 +89,25 @@ async def test_nb(
57
89
  start = time.time()
58
90
  if profile is None: profile = bool(get_config(fn.parent).exec_profile)
59
91
  k = CaptureShell(fn, profile=profile)
92
+ _cur_shell[0] = k
60
93
  if do_print: print(f'Starting {fn}')
61
94
  try:
62
95
  with working_directory(fn.parent):
63
- await k.run_all(nb, exc_stop=True, preproc=_no_eval, verbose=verbose)
96
+ k.run_all(nb, exc_stop=True, preproc=_no_eval, verbose=verbose, cell_timeout=cell_timeout)
64
97
  if save: write_nb(nb, fn)
65
98
  res = True
66
99
  except:
67
100
  if showerr: sys.stderr.write(k.prettytb(fname=fn)+'\n')
68
101
  res=False
102
+ if k.leaks and showerr: sys.stderr.write(f'{fn}: {len(k.leaks)} leaked task(s) survived cancellation\n')
69
103
  if do_print: print(f'- Completed {fn}')
70
104
  return res,time.time()-start
71
105
  finally:
106
+ _cur_nb[0] = _cur_shell[0] = None
72
107
  if prev_test is None: os.environ.pop('IN_TEST', None)
73
108
  else: os.environ['IN_TEST'] = prev_test
74
109
 
75
110
 
76
- # %% ../nbs/api/12_test.ipynb #12516e0a
77
- def _test_nb_sync(fn, **kwargs): return asyncio.run(test_nb(fn, **kwargs))
78
-
79
111
  # %% ../nbs/api/12_test.ipynb #d8bf1f1b-935d-4b69-ba96-827c5d7213f0
80
112
  def _keep_file(
81
113
  p:Path, # filename for which to check for `indicator_fname`
@@ -98,6 +130,7 @@ def nbdev_test(
98
130
  ignore_fname:str='.notest', # Filename that will result in siblings being ignored
99
131
  verbose:bool=False, # Print stdout/stderr from notebook cells?
100
132
  save:bool=False, # Write outputs back to notebooks on success?
133
+ cell_timeout:int=600, # Seconds before each cell times out (0: no limit)
101
134
  **kwargs
102
135
  ):
103
136
  "Test in parallel notebooks matching `path`, passing along `flags`"
@@ -114,8 +147,13 @@ def nbdev_test(
114
147
  else: kw = {'method':'spawn'} if sys.platform=='darwin' else {}
115
148
  wd_pth = cfg.nbs_path
116
149
  with working_directory(wd_pth if (wd_pth and wd_pth.exists()) else os.getcwd()):
117
- results = parallel(_test_nb_sync, files, skip_flags=skip_flags, force_flags=force_flags, n_workers=n_workers,
118
- basepath=cfg.config_path, pause=pause, do_print=do_print, verbose=verbose, save=save, **kw)
150
+ try:
151
+ results = parallel(test_nb, files, skip_flags=skip_flags, force_flags=force_flags, n_workers=n_workers,
152
+ basepath=cfg.config_path, pause=pause, do_print=do_print, verbose=verbose, save=save,
153
+ cell_timeout=cell_timeout or None, **kw)
154
+ except KeyboardInterrupt:
155
+ sys.stderr.write('\nnbdev-test interrupted; in-flight notebook stacks shown above\n')
156
+ sys.exit(130)
119
157
  passed,times = zip(*results)
120
158
  if all(passed): print("Success.")
121
159
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbdev
3
- Version: 3.3.9
3
+ Version: 3.3.11
4
4
  Summary: Create delightful software with Jupyter Notebooks
5
5
  Author-email: "Jeremy Howard and the fast.ai community" <j@fast.ai>
6
6
  License: Apache-2.0
@@ -17,7 +17,7 @@ Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: fastcore>=2.2.7
20
- Requires-Dist: execnb>=0.2.11
20
+ Requires-Dist: execnb>=0.3.2
21
21
  Requires-Dist: astunparse
22
22
  Requires-Dist: ghapi>=2.0.2
23
23
  Requires-Dist: watchdog
@@ -1,5 +1,5 @@
1
1
  fastcore>=2.2.7
2
- execnb>=0.2.11
2
+ execnb>=0.3.2
3
3
  astunparse
4
4
  ghapi>=2.0.2
5
5
  watchdog
@@ -19,7 +19,7 @@ classifiers = [
19
19
  "License :: OSI Approved :: Apache Software License",
20
20
  ]
21
21
  dynamic = ["version"]
22
- dependencies = [ "fastcore>=2.2.7", "execnb>=0.2.11", "astunparse", "ghapi>=2.0.2", "watchdog", "asttokens",
22
+ dependencies = [ "fastcore>=2.2.7", "execnb>=0.3.2", "astunparse", "ghapi>=2.0.2", "watchdog", "asttokens",
23
23
  "setuptools", "build", "fastgit>=0.0.7", "pyyaml", "tomli; python_version < '3.11'", ]
24
24
 
25
25
  [project.optional-dependencies]
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
@@ -339,9 +339,9 @@ def nbdev_docs(
339
339
  **kwargs):
340
340
  "Create Quarto docs and README.md"
341
341
  cache,cfg,path = _pre_docs(path, n_workers=n_workers, **kwargs)
342
+ _sprun(f'cd "{cache}" && quarto render --no-cache')
342
343
  nbdev_readme.__wrapped__(path=path, chk_time=True)
343
344
  nbdev_contributing.__wrapped__(path=path, chk_time=True)
344
- _sprun(f'cd "{cache}" && quarto render --no-cache')
345
345
  shutil.rmtree(cfg.doc_path, ignore_errors=True)
346
346
  move(cache/cfg.doc_path.name, cfg.config_path)
347
347
  _fix_quarto_nav(cfg.doc_path)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes