nbdev 2.3.12__py3-none-any.whl → 2.3.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nbdev/__init__.py +1 -1
- nbdev/_modidx.py +10 -3
- nbdev/cli.py +64 -9
- nbdev/config.py +3 -1
- nbdev/doclinks.py +13 -9
- nbdev/export.py +23 -4
- nbdev/frontmatter.py +5 -0
- nbdev/maker.py +1 -1
- nbdev/merge.py +2 -2
- nbdev/process.py +6 -5
- nbdev/quarto.py +63 -32
- nbdev/release.py +6 -5
- nbdev/serve.py +5 -5
- nbdev/showdoc.py +16 -16
- nbdev/test.py +2 -1
- {nbdev-2.3.12.dist-info → nbdev-2.3.14.dist-info}/METADATA +36 -44
- nbdev-2.3.14.dist-info/RECORD +29 -0
- {nbdev-2.3.12.dist-info → nbdev-2.3.14.dist-info}/WHEEL +1 -1
- {nbdev-2.3.12.dist-info → nbdev-2.3.14.dist-info}/entry_points.txt +1 -0
- nbdev-2.3.12.dist-info/RECORD +0 -29
- {nbdev-2.3.12.dist-info → nbdev-2.3.14.dist-info}/LICENSE +0 -0
- {nbdev-2.3.12.dist-info → nbdev-2.3.14.dist-info}/top_level.txt +0 -0
nbdev/__init__.py
CHANGED
nbdev/_modidx.py
CHANGED
|
@@ -24,7 +24,8 @@ d = { 'settings': { 'branch': 'master',
|
|
|
24
24
|
'nbdev.cli.chelp': ('api/cli.html#chelp', 'nbdev/cli.py'),
|
|
25
25
|
'nbdev.cli.extract_tgz': ('api/cli.html#extract_tgz', 'nbdev/cli.py'),
|
|
26
26
|
'nbdev.cli.nbdev_filter': ('api/cli.html#nbdev_filter', 'nbdev/cli.py'),
|
|
27
|
-
'nbdev.cli.nbdev_new': ('api/cli.html#nbdev_new', 'nbdev/cli.py')
|
|
27
|
+
'nbdev.cli.nbdev_new': ('api/cli.html#nbdev_new', 'nbdev/cli.py'),
|
|
28
|
+
'nbdev.cli.nbdev_update_license': ('api/cli.html#nbdev_update_license', 'nbdev/cli.py')},
|
|
28
29
|
'nbdev.config': { 'nbdev.config._apply_defaults': ('api/config.html#_apply_defaults', 'nbdev/config.py'),
|
|
29
30
|
'nbdev.config._basic_export_nb': ('api/config.html#_basic_export_nb', 'nbdev/config.py'),
|
|
30
31
|
'nbdev.config._cfg2txt': ('api/config.html#_cfg2txt', 'nbdev/config.py'),
|
|
@@ -76,7 +77,9 @@ d = { 'settings': { 'branch': 'master',
|
|
|
76
77
|
'nbdev.export.ExportModuleProc._exporti_': ('api/export.html#exportmoduleproc._exporti_', 'nbdev/export.py'),
|
|
77
78
|
'nbdev.export.ExportModuleProc.begin': ('api/export.html#exportmoduleproc.begin', 'nbdev/export.py'),
|
|
78
79
|
'nbdev.export.black_format': ('api/export.html#black_format', 'nbdev/export.py'),
|
|
79
|
-
'nbdev.export.nb_export': ('api/export.html#nb_export', 'nbdev/export.py')
|
|
80
|
+
'nbdev.export.nb_export': ('api/export.html#nb_export', 'nbdev/export.py'),
|
|
81
|
+
'nbdev.export.optional_procs': ('api/export.html#optional_procs', 'nbdev/export.py'),
|
|
82
|
+
'nbdev.export.scrub_magics': ('api/export.html#scrub_magics', 'nbdev/export.py')},
|
|
80
83
|
'nbdev.extract_attachments': {},
|
|
81
84
|
'nbdev.frontmatter': { 'nbdev.frontmatter.FrontmatterProc': ('api/frontmatter.html#frontmatterproc', 'nbdev/frontmatter.py'),
|
|
82
85
|
'nbdev.frontmatter.FrontmatterProc._update': ( 'api/frontmatter.html#frontmatterproc._update',
|
|
@@ -231,7 +234,10 @@ d = { 'settings': { 'branch': 'master',
|
|
|
231
234
|
'nbdev.qmd.meta': ('api/qmd.html#meta', 'nbdev/qmd.py'),
|
|
232
235
|
'nbdev.qmd.tbl_row': ('api/qmd.html#tbl_row', 'nbdev/qmd.py'),
|
|
233
236
|
'nbdev.qmd.tbl_sep': ('api/qmd.html#tbl_sep', 'nbdev/qmd.py')},
|
|
234
|
-
'nbdev.quarto': { 'nbdev.quarto.
|
|
237
|
+
'nbdev.quarto': { 'nbdev.quarto.IndentDumper': ('api/quarto.html#indentdumper', 'nbdev/quarto.py'),
|
|
238
|
+
'nbdev.quarto.IndentDumper.increase_indent': ( 'api/quarto.html#indentdumper.increase_indent',
|
|
239
|
+
'nbdev/quarto.py'),
|
|
240
|
+
'nbdev.quarto._SidebarYmlRemoved': ('api/quarto.html#_sidebarymlremoved', 'nbdev/quarto.py'),
|
|
235
241
|
'nbdev.quarto._SidebarYmlRemoved.__enter__': ( 'api/quarto.html#_sidebarymlremoved.__enter__',
|
|
236
242
|
'nbdev/quarto.py'),
|
|
237
243
|
'nbdev.quarto._SidebarYmlRemoved.__exit__': ( 'api/quarto.html#_sidebarymlremoved.__exit__',
|
|
@@ -246,6 +252,7 @@ d = { 'settings': { 'branch': 'master',
|
|
|
246
252
|
'nbdev.quarto._pre': ('api/quarto.html#_pre', 'nbdev/quarto.py'),
|
|
247
253
|
'nbdev.quarto._pre_docs': ('api/quarto.html#_pre_docs', 'nbdev/quarto.py'),
|
|
248
254
|
'nbdev.quarto._readme_mtime_not_older': ('api/quarto.html#_readme_mtime_not_older', 'nbdev/quarto.py'),
|
|
255
|
+
'nbdev.quarto._recursive_parser': ('api/quarto.html#_recursive_parser', 'nbdev/quarto.py'),
|
|
249
256
|
'nbdev.quarto._save_cached_readme': ('api/quarto.html#_save_cached_readme', 'nbdev/quarto.py'),
|
|
250
257
|
'nbdev.quarto._sort': ('api/quarto.html#_sort', 'nbdev/quarto.py'),
|
|
251
258
|
'nbdev.quarto._sprun': ('api/quarto.html#_sprun', 'nbdev/quarto.py'),
|
nbdev/cli.py
CHANGED
|
@@ -25,7 +25,7 @@ from contextlib import redirect_stdout
|
|
|
25
25
|
import os, tarfile, sys
|
|
26
26
|
|
|
27
27
|
# %% auto 0
|
|
28
|
-
__all__ = ['nbdev_filter', 'extract_tgz', 'nbdev_new', 'chelp']
|
|
28
|
+
__all__ = ['mapping', 'nbdev_filter', 'extract_tgz', 'nbdev_new', 'nbdev_update_license', 'chelp']
|
|
29
29
|
|
|
30
30
|
# %% ../nbs/api/13_cli.ipynb 5
|
|
31
31
|
@call_parse
|
|
@@ -67,7 +67,7 @@ def _render_nb(fn, cfg):
|
|
|
67
67
|
def _update_repo_meta(cfg):
|
|
68
68
|
"Enable gh pages and update the homepage and description in your GitHub repo."
|
|
69
69
|
token=os.getenv('GITHUB_TOKEN')
|
|
70
|
-
if token:
|
|
70
|
+
if token:
|
|
71
71
|
from ghapi.core import GhApi
|
|
72
72
|
api = GhApi(owner=cfg.user, repo=cfg.repo, token=token)
|
|
73
73
|
try: api.repos.update(homepage=f'{cfg.doc_host}{cfg.doc_baseurl}', description=cfg.description)
|
|
@@ -85,13 +85,28 @@ def nbdev_new(**kwargs):
|
|
|
85
85
|
_update_repo_meta(cfg)
|
|
86
86
|
|
|
87
87
|
path = Path()
|
|
88
|
-
with warnings.catch_warnings():
|
|
89
|
-
warnings.simplefilter('ignore', UserWarning)
|
|
90
|
-
tag = GhApi(gh_host='https://api.github.com', authenticate=False).repos.get_latest_release('fastai', 'nbdev-template').tag_name
|
|
91
|
-
url = f"https://github.com/fastai/nbdev-template/archive/{tag}.tar.gz"
|
|
92
|
-
extract_tgz(url)
|
|
93
|
-
tmpl_path = path/f'nbdev-template-{tag}'
|
|
94
88
|
|
|
89
|
+
_ORG_OR_USR = 'fastai'
|
|
90
|
+
_REPOSITORY = 'nbdev-template'
|
|
91
|
+
_TEMPLATE = f'{_ORG_OR_USR}/{_TEMPLATE}'
|
|
92
|
+
template = kwargs.get('template', _TEMPLATE)
|
|
93
|
+
try:
|
|
94
|
+
org_or_usr, repo = template.split('/')
|
|
95
|
+
except ValueError:
|
|
96
|
+
org_or_usr, repo = _ORG_OR_USR, _REPOSITORY
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
tag = kwargs.get('tag', None)
|
|
100
|
+
if tag is None:
|
|
101
|
+
with warnings.catch_warnings():
|
|
102
|
+
warnings.simplefilter('ignore', UserWarning)
|
|
103
|
+
|
|
104
|
+
tag = GhApi(gh_host='https://api.github.com', authenticate=False).repos.get_latest_release(org_or_usr, repo).tag_name
|
|
105
|
+
|
|
106
|
+
url = f"https://github.com/{org_or_usr}/{repo}/archive/{tag}.tar.gz"
|
|
107
|
+
extract_tgz(url)
|
|
108
|
+
tmpl_path = path/f'{repo}-{tag}'
|
|
109
|
+
|
|
95
110
|
cfg.nbs_path.mkdir(exist_ok=True)
|
|
96
111
|
nbexists = bool(first(cfg.nbs_path.glob('*.ipynb')))
|
|
97
112
|
_nbs_path_sufs = ('.ipynb','.css')
|
|
@@ -108,7 +123,47 @@ def nbdev_new(**kwargs):
|
|
|
108
123
|
nbdev_export.__wrapped__()
|
|
109
124
|
nbdev_readme.__wrapped__()
|
|
110
125
|
|
|
111
|
-
# %% ../nbs/api/13_cli.ipynb
|
|
126
|
+
# %% ../nbs/api/13_cli.ipynb 13
|
|
127
|
+
mapping = {
|
|
128
|
+
'mit': 'mit',
|
|
129
|
+
'apache2': 'apache-2.0',
|
|
130
|
+
'gpl2': 'gpl-2.0',
|
|
131
|
+
'gpl3': 'gpl-3.0',
|
|
132
|
+
'bsd3': 'bsd-3-clause'
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# %% ../nbs/api/13_cli.ipynb 14
|
|
136
|
+
@call_parse
|
|
137
|
+
def nbdev_update_license(
|
|
138
|
+
to: str=None, # update license to
|
|
139
|
+
):
|
|
140
|
+
"Allows you to update the license of your project."
|
|
141
|
+
from ghapi.core import GhApi
|
|
142
|
+
warnings.filterwarnings("ignore")
|
|
143
|
+
avail_lic = GhApi().licenses.get_all_commonly_used().map(lambda x: x['key'])
|
|
144
|
+
|
|
145
|
+
cfg = get_config()
|
|
146
|
+
curr_lic = cfg['license']
|
|
147
|
+
|
|
148
|
+
mapped = mapping.get(to, None)
|
|
149
|
+
|
|
150
|
+
if mapped not in avail_lic: raise ValueError(f"{to} is not an available license")
|
|
151
|
+
body = GhApi().licenses.get(mapped)['body']
|
|
152
|
+
|
|
153
|
+
body = body.replace('[year], [fullname]', cfg['copyright'])
|
|
154
|
+
body = body.replace('[year] [fullname]', cfg['copyright'])
|
|
155
|
+
|
|
156
|
+
content = open("settings.ini", "r").read()
|
|
157
|
+
content = re.sub(r"^(license\s*=\s*).*?$", r"\1 " + to, content, flags=re.MULTILINE)
|
|
158
|
+
|
|
159
|
+
config = open("settings.ini", "w")
|
|
160
|
+
config.write(content)
|
|
161
|
+
|
|
162
|
+
lic = open('LICENSE', 'w')
|
|
163
|
+
lic.write(body)
|
|
164
|
+
print(f"License updated from {curr_lic} to {to}")
|
|
165
|
+
|
|
166
|
+
# %% ../nbs/api/13_cli.ipynb 17
|
|
112
167
|
@call_parse
|
|
113
168
|
def chelp():
|
|
114
169
|
"Show help for all console scripts"
|
nbdev/config.py
CHANGED
|
@@ -91,7 +91,7 @@ def _get_info(owner, repo, default_branch='main', default_kw='nbdev'):
|
|
|
91
91
|
msg= [f"""Could not access repo: {owner}/{repo} to find your default branch - `{default_branch}` assumed.
|
|
92
92
|
Edit `settings.ini` if this is incorrect.
|
|
93
93
|
In the future, you can allow nbdev to see private repos by setting the environment variable GITHUB_TOKEN as described here:
|
|
94
|
-
https://nbdev.fast.ai/
|
|
94
|
+
https://nbdev.fast.ai/api/release.html#setup"""]
|
|
95
95
|
print(''.join(msg))
|
|
96
96
|
return default_branch,default_kw,''
|
|
97
97
|
|
|
@@ -152,6 +152,8 @@ _nbdev_cfg_tail = '''### Optional ###
|
|
|
152
152
|
# requirements = fastcore pandas
|
|
153
153
|
# dev_requirements =
|
|
154
154
|
# console_scripts =
|
|
155
|
+
# conda_user =
|
|
156
|
+
# package_data =
|
|
155
157
|
'''
|
|
156
158
|
|
|
157
159
|
# %% ../nbs/api/01_config.ipynb 21
|
nbdev/doclinks.py
CHANGED
|
@@ -46,7 +46,7 @@ def patch_name(o):
|
|
|
46
46
|
def _iter_py_cells(p):
|
|
47
47
|
"Yield cells from an exported Python file."
|
|
48
48
|
p = Path(p)
|
|
49
|
-
cells = p.read_text().split("\n# %% ")
|
|
49
|
+
cells = p.read_text(encoding='utf-8').split("\n# %% ")
|
|
50
50
|
for cell in cells[1:]:
|
|
51
51
|
top,code = cell.split('\n', 1)
|
|
52
52
|
try:
|
|
@@ -93,7 +93,7 @@ def _build_modidx(dest=None, nbs_path=None, skip_exists=False):
|
|
|
93
93
|
idxfile = dest/'_modidx.py'
|
|
94
94
|
if skip_exists and idxfile.exists(): return
|
|
95
95
|
with contextlib.suppress(FileNotFoundError): idxfile.unlink()
|
|
96
|
-
if idxfile.exists(): res = exec_local(idxfile.read_text(), 'd')
|
|
96
|
+
if idxfile.exists(): res = exec_local(idxfile.read_text(encoding='utf-8'), 'd')
|
|
97
97
|
else: res = dict(syms={}, settings={})
|
|
98
98
|
res['settings'] = {k:v for k,v in get_config().d.items()
|
|
99
99
|
if k in ('doc_host','doc_baseurl','lib_path','git_url','branch')}
|
|
@@ -131,19 +131,23 @@ def nbglob_cli(
|
|
|
131
131
|
@delegates(nbglob_cli)
|
|
132
132
|
def nbdev_export(
|
|
133
133
|
path:str=None, # Path or filename
|
|
134
|
+
procs:Param("tokens naming the export processors to use.", nargs="*", choices=optional_procs())="black_format",
|
|
134
135
|
**kwargs):
|
|
135
136
|
"Export notebooks in `path` to Python modules"
|
|
136
137
|
if os.environ.get('IN_TEST',0): return
|
|
138
|
+
if procs:
|
|
139
|
+
import nbdev.export
|
|
140
|
+
procs = [getattr(nbdev.export, p) for p in L(procs)]
|
|
137
141
|
files = nbglob(path=path, as_path=True, **kwargs).sorted('name')
|
|
138
|
-
for f in files: nb_export(f)
|
|
142
|
+
for f in files: nb_export(f, procs=procs)
|
|
139
143
|
add_init(get_config().lib_path)
|
|
140
144
|
_build_modidx()
|
|
141
145
|
|
|
142
|
-
# %% ../nbs/api/05_doclinks.ipynb
|
|
146
|
+
# %% ../nbs/api/05_doclinks.ipynb 25
|
|
143
147
|
import importlib,ast
|
|
144
148
|
from functools import lru_cache
|
|
145
149
|
|
|
146
|
-
# %% ../nbs/api/05_doclinks.ipynb
|
|
150
|
+
# %% ../nbs/api/05_doclinks.ipynb 26
|
|
147
151
|
def _find_mod(mod):
|
|
148
152
|
mp,_,mr = mod.partition('/')
|
|
149
153
|
spec = importlib.util.find_spec(mp)
|
|
@@ -155,7 +159,7 @@ def _find_mod(mod):
|
|
|
155
159
|
def _get_exps(mod):
|
|
156
160
|
mf = _find_mod(mod)
|
|
157
161
|
if not mf: return {}
|
|
158
|
-
txt = mf.read_text()
|
|
162
|
+
txt = mf.read_text(encoding='utf-8')
|
|
159
163
|
_def_types = ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef
|
|
160
164
|
d = {}
|
|
161
165
|
for tree in ast.parse(txt).body:
|
|
@@ -166,7 +170,7 @@ def _get_exps(mod):
|
|
|
166
170
|
|
|
167
171
|
def _lineno(sym, fname): return _get_exps(fname).get(sym, None) if fname else None
|
|
168
172
|
|
|
169
|
-
# %% ../nbs/api/05_doclinks.ipynb
|
|
173
|
+
# %% ../nbs/api/05_doclinks.ipynb 28
|
|
170
174
|
def _qual_sym(s, settings):
|
|
171
175
|
if not isinstance(s,tuple): return s
|
|
172
176
|
nb,py = s
|
|
@@ -181,10 +185,10 @@ def _qual_syms(entries):
|
|
|
181
185
|
if 'doc_host' not in settings: return entries
|
|
182
186
|
return {'syms': {mod:_qual_mod(d, settings) for mod,d in entries['syms'].items()}, 'settings':settings}
|
|
183
187
|
|
|
184
|
-
# %% ../nbs/api/05_doclinks.ipynb
|
|
188
|
+
# %% ../nbs/api/05_doclinks.ipynb 29
|
|
185
189
|
_re_backticks = re.compile(r'`([^`\s]+)`')
|
|
186
190
|
|
|
187
|
-
# %% ../nbs/api/05_doclinks.ipynb
|
|
191
|
+
# %% ../nbs/api/05_doclinks.ipynb 30
|
|
188
192
|
@lru_cache(None)
|
|
189
193
|
class NbdevLookup:
|
|
190
194
|
"Mapping from symbol names to docs and source URLs"
|
nbdev/export.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/04_export.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto 0
|
|
4
|
-
__all__ = ['ExportModuleProc', 'black_format', 'nb_export']
|
|
4
|
+
__all__ = ['ExportModuleProc', 'black_format', 'scrub_magics', 'optional_procs', 'nb_export']
|
|
5
5
|
|
|
6
6
|
# %% ../nbs/api/04_export.ipynb 2
|
|
7
7
|
from .config import *
|
|
@@ -26,7 +26,7 @@ class ExportModuleProc:
|
|
|
26
26
|
self.in_all[ifnone(exp_to, '#')].append(cell)
|
|
27
27
|
_exports_=_export_
|
|
28
28
|
|
|
29
|
-
# %% ../nbs/api/04_export.ipynb
|
|
29
|
+
# %% ../nbs/api/04_export.ipynb 8
|
|
30
30
|
def black_format(cell, # Cell to format
|
|
31
31
|
force=False): # Turn black formatting on regardless of settings.ini
|
|
32
32
|
"Processor to format code with `black`"
|
|
@@ -40,8 +40,27 @@ def black_format(cell, # Cell to format
|
|
|
40
40
|
try: cell.source = _format_str(cell.source).strip()
|
|
41
41
|
except: pass
|
|
42
42
|
|
|
43
|
-
# %% ../nbs/api/04_export.ipynb
|
|
44
|
-
|
|
43
|
+
# %% ../nbs/api/04_export.ipynb 10
|
|
44
|
+
# includes the newline, because calling .strip() would affect all cells.
|
|
45
|
+
_magics_pattern = re.compile(r'^\s*(%%|%).*\n?', re.MULTILINE)
|
|
46
|
+
|
|
47
|
+
def scrub_magics(cell): # Cell to format
|
|
48
|
+
"Processor to remove cell magics from exported code"
|
|
49
|
+
try: cfg = get_config()
|
|
50
|
+
except FileNotFoundError: return
|
|
51
|
+
if cell.cell_type != 'code': return
|
|
52
|
+
try: cell.source = _magics_pattern.sub('', cell.source)
|
|
53
|
+
except: pass
|
|
54
|
+
|
|
55
|
+
# %% ../nbs/api/04_export.ipynb 13
|
|
56
|
+
import nbdev.export
|
|
57
|
+
def optional_procs():
|
|
58
|
+
"An explicit list of processors that could be used by `nb_export`"
|
|
59
|
+
return L([p for p in nbdev.export.__all__
|
|
60
|
+
if p not in ["nb_export", "ExportModuleProc", "optional_procs"]])
|
|
61
|
+
|
|
62
|
+
# %% ../nbs/api/04_export.ipynb 16
|
|
63
|
+
def nb_export(nbname, lib_path=None, procs=None, debug=False, mod_maker=ModuleMaker, name=None):
|
|
45
64
|
"Create module(s) from notebook"
|
|
46
65
|
if lib_path is None: lib_path = get_config().lib_path
|
|
47
66
|
exp = ExportModuleProc()
|
nbdev/frontmatter.py
CHANGED
|
@@ -63,5 +63,10 @@ class FrontmatterProc(Processor):
|
|
|
63
63
|
def end(self):
|
|
64
64
|
self.nb.frontmatter_ = self.fm
|
|
65
65
|
if not self.fm: return
|
|
66
|
+
if not hasattr(self.nb, 'path_'):
|
|
67
|
+
raise AttributeError('Notebook missing `path_` attribute.\n\nPlease remove any nbdev-related notebook filters '
|
|
68
|
+
'from your _quarto.yml file (e.g. `ipynb-filter: [nbdev_filter]`), since they are no '
|
|
69
|
+
'longer supported as of nbdev v2.3. See the v2.3 launch post for more information: '
|
|
70
|
+
'https://forums.fast.ai/t/upcoming-changes-in-v2-3-edit-now-released/98905.')
|
|
66
71
|
self.fm.update({'output-file': _nbpath2html(Path(self.nb.path_)).name})
|
|
67
72
|
_insertfm(self.nb, self.fm)
|
nbdev/maker.py
CHANGED
|
@@ -47,7 +47,7 @@ def update_var(varname, func, fn=None, code=None):
|
|
|
47
47
|
"Update the definition of `varname` in file `fn`, by calling `func` with the current definition"
|
|
48
48
|
if fn:
|
|
49
49
|
fn = Path(fn)
|
|
50
|
-
code = fn.read_text()
|
|
50
|
+
code = fn.read_text(encoding='utf-8')
|
|
51
51
|
lines = code.splitlines()
|
|
52
52
|
v = read_var(code, varname)
|
|
53
53
|
res = func(v)
|
nbdev/merge.py
CHANGED
|
@@ -63,7 +63,7 @@ def nbdev_fix(nbname:str, # Notebook filename to fix
|
|
|
63
63
|
"Create working notebook from conflicted notebook `nbname`"
|
|
64
64
|
nbname = Path(nbname)
|
|
65
65
|
if not nobackup and not outname: shutil.copy(nbname, nbname.with_suffix('.ipynb.bak'))
|
|
66
|
-
nbtxt = nbname.read_text()
|
|
66
|
+
nbtxt = nbname.read_text(encoding='utf-8')
|
|
67
67
|
a,b,branch1,branch2 = unpatch(nbtxt)
|
|
68
68
|
ac,bc = dict2nb(loads(a)),dict2nb(loads(b))
|
|
69
69
|
dest = bc if theirs else ac
|
|
@@ -85,7 +85,7 @@ def _git_rebase_head():
|
|
|
85
85
|
for d in ('apply','merge'):
|
|
86
86
|
d = Path(f'.git/rebase-{d}')
|
|
87
87
|
if d.is_dir():
|
|
88
|
-
cmt = (d/'orig-head').read_text()
|
|
88
|
+
cmt = (d/'orig-head').read_text(encoding='utf-8')
|
|
89
89
|
msg = run(f'git show-branch --no-name {cmt}')
|
|
90
90
|
return f'{cmt[:7]} ({msg})'
|
|
91
91
|
|
nbdev/process.py
CHANGED
|
@@ -33,6 +33,7 @@ def _quarto_re(lang=None): return re.compile(_dir_pre(lang) + r'\s*[\w|-]+\s*:')
|
|
|
33
33
|
# %% ../nbs/api/03_process.ipynb 11
|
|
34
34
|
def _directive(s, lang='python'):
|
|
35
35
|
s = re.sub('^'+_dir_pre(lang), f"{langs[lang]}|", s)
|
|
36
|
+
if s.strip().endswith(':'): s = s.replace(':', '') # You can append colon at the end to be Quarto compliant. Ex: #|hide:
|
|
36
37
|
if ':' in s: s = s.replace(':', ': ')
|
|
37
38
|
s = (s.strip()[2:]).strip().split()
|
|
38
39
|
if not s: return None
|
|
@@ -70,22 +71,22 @@ def extract_directives(cell, remove=True, lang='python'):
|
|
|
70
71
|
cell['source'] = ''.join([_norm_quarto(o, lang) for o in dirs if _quarto_re(lang).match(o) or _cell_mgc.match(o)] + code)
|
|
71
72
|
return dict(L(_directive(s, lang) for s in dirs).filter())
|
|
72
73
|
|
|
73
|
-
# %% ../nbs/api/03_process.ipynb
|
|
74
|
+
# %% ../nbs/api/03_process.ipynb 21
|
|
74
75
|
def opt_set(var, newval):
|
|
75
76
|
"newval if newval else var"
|
|
76
77
|
return newval if newval else var
|
|
77
78
|
|
|
78
|
-
# %% ../nbs/api/03_process.ipynb
|
|
79
|
+
# %% ../nbs/api/03_process.ipynb 22
|
|
79
80
|
def instantiate(x, **kwargs):
|
|
80
81
|
"Instantiate `x` if it's a type"
|
|
81
82
|
return x(**kwargs) if isinstance(x,type) else x
|
|
82
83
|
|
|
83
84
|
def _mk_procs(procs, nb): return L(procs).map(instantiate, nb=nb)
|
|
84
85
|
|
|
85
|
-
# %% ../nbs/api/03_process.ipynb
|
|
86
|
+
# %% ../nbs/api/03_process.ipynb 23
|
|
86
87
|
def _is_direc(f): return getattr(f, '__name__', '-')[-1]=='_'
|
|
87
88
|
|
|
88
|
-
# %% ../nbs/api/03_process.ipynb
|
|
89
|
+
# %% ../nbs/api/03_process.ipynb 24
|
|
89
90
|
class NBProcessor:
|
|
90
91
|
"Process cells and nbdev comments in a notebook"
|
|
91
92
|
def __init__(self, path=None, procs=None, nb=None, debug=False, rm_directives=True, process=False):
|
|
@@ -125,7 +126,7 @@ class NBProcessor:
|
|
|
125
126
|
"Process all cells with all processors"
|
|
126
127
|
for proc in self.procs: self._proc(proc)
|
|
127
128
|
|
|
128
|
-
# %% ../nbs/api/03_process.ipynb
|
|
129
|
+
# %% ../nbs/api/03_process.ipynb 34
|
|
129
130
|
class Processor:
|
|
130
131
|
"Base class for processors"
|
|
131
132
|
def __init__(self, nb): self.nb = nb
|
nbdev/quarto.py
CHANGED
|
@@ -15,10 +15,11 @@ from fastcore.shutil import rmtree,move,copytree
|
|
|
15
15
|
from fastcore.meta import delegates
|
|
16
16
|
from .serve import proc_nbs,_proc_file
|
|
17
17
|
from . import serve_drv
|
|
18
|
+
import yaml
|
|
18
19
|
|
|
19
20
|
# %% auto 0
|
|
20
|
-
__all__ = ['BASE_QUARTO_URL', 'install_quarto', 'install', '
|
|
21
|
-
'nbdev_readme', 'nbdev_docs', 'prepare', 'fs_watchdog', 'nbdev_preview']
|
|
21
|
+
__all__ = ['BASE_QUARTO_URL', 'install_quarto', 'install', 'IndentDumper', 'nbdev_sidebar', 'refresh_quarto_yml',
|
|
22
|
+
'nbdev_proc_nbs', 'nbdev_readme', 'nbdev_docs', 'prepare', 'fs_watchdog', 'nbdev_preview']
|
|
22
23
|
|
|
23
24
|
# %% ../nbs/api/14_quarto.ipynb 5
|
|
24
25
|
def _sprun(cmd):
|
|
@@ -29,8 +30,10 @@ def _sprun(cmd):
|
|
|
29
30
|
BASE_QUARTO_URL='https://www.quarto.org/download/latest/'
|
|
30
31
|
|
|
31
32
|
def _install_linux():
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
from os import uname
|
|
34
|
+
machine = 'arm' if uname().machine in ('arm64', 'aarch64', 'armv8', 'armv8l') else 'amd'
|
|
35
|
+
system(f'curl -LO {BASE_QUARTO_URL}quarto-linux-{machine}64.deb')
|
|
36
|
+
system(f'sudo dpkg -i quarto-linux-{machine}64.deb && rm quarto-linux-{machine}64.deb')
|
|
34
37
|
|
|
35
38
|
def _install_mac():
|
|
36
39
|
system(f'curl -LO {BASE_QUARTO_URL}quarto-macos.pkg')
|
|
@@ -76,6 +79,29 @@ def _nbglob_docs(
|
|
|
76
79
|
return nbglob(path, file_glob=file_glob, file_re=file_re, **kwargs)
|
|
77
80
|
|
|
78
81
|
# %% ../nbs/api/14_quarto.ipynb 11
|
|
82
|
+
def _recursive_parser(
|
|
83
|
+
dir_dict: dict, # Directory structure as a dict.
|
|
84
|
+
contents: list, # `contents` list from `sidebar.yaml` template dict.
|
|
85
|
+
dirpath: Path, # Directory path.
|
|
86
|
+
section = None, # `section` mapping.
|
|
87
|
+
set_index: bool = True): # If `True`, `index` file will be set to href.
|
|
88
|
+
for name, val in dir_dict.items():
|
|
89
|
+
if type(val) is str:
|
|
90
|
+
if re.search('index\..*', re.sub('^\d+_', '', val)) and set_index and section:
|
|
91
|
+
section.update({'href': str(dirpath/val)})
|
|
92
|
+
else:
|
|
93
|
+
contents.append(str(dirpath/val))
|
|
94
|
+
elif type(val) is dict:
|
|
95
|
+
name = re.sub('^\d+_', '', name)
|
|
96
|
+
section = {'section': name, 'contents': []}
|
|
97
|
+
contents.append(section)
|
|
98
|
+
_recursive_parser(val, section['contents'], dirpath/name, section=section)
|
|
99
|
+
|
|
100
|
+
class IndentDumper(yaml.Dumper):
|
|
101
|
+
def increase_indent(self, flow=False, indentless=False):
|
|
102
|
+
return super(IndentDumper, self).increase_indent(flow, False)
|
|
103
|
+
|
|
104
|
+
# %% ../nbs/api/14_quarto.ipynb 12
|
|
79
105
|
@call_parse
|
|
80
106
|
@delegates(_nbglob_docs)
|
|
81
107
|
def nbdev_sidebar(
|
|
@@ -89,26 +115,28 @@ def nbdev_sidebar(
|
|
|
89
115
|
path = get_config().nbs_path if not path else Path(path)
|
|
90
116
|
def _f(a,b): return Path(a),b
|
|
91
117
|
files = nbglob(path, func=_f, skip_folder_re=skip_folder_re, **kwargs).sorted(key=_sort)
|
|
92
|
-
lastd,res = Path(),[]
|
|
93
|
-
|
|
118
|
+
lastd, res = Path(), []
|
|
119
|
+
|
|
120
|
+
# Parse directory structure to dict.
|
|
121
|
+
# dir => dict(), file => file.
|
|
122
|
+
parsed_struct = {'website': {'sidebar': {'contents': []}}}
|
|
123
|
+
_contents = parsed_struct['website']['sidebar']['contents']
|
|
124
|
+
dir_struct = dict()
|
|
125
|
+
for dabs, name in files:
|
|
94
126
|
drel = dabs.relative_to(path)
|
|
95
|
-
|
|
96
|
-
for
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
title = re.sub('^\d+_', '', d.name)
|
|
100
|
-
res.append(_pre(d.parent) + f'section: {title}')
|
|
101
|
-
res.append(_pre(d.parent, False) + 'contents:')
|
|
102
|
-
lastd = d
|
|
103
|
-
res.append(f'{_pre(d)}{d.joinpath(name)}')
|
|
127
|
+
_dir = dir_struct
|
|
128
|
+
for subdir in drel.parts:
|
|
129
|
+
_dir = _dir.setdefault(subdir, dict())
|
|
130
|
+
_dir[name] = name
|
|
104
131
|
|
|
132
|
+
_recursive_parser(dir_struct, _contents, Path())
|
|
105
133
|
yml_path = path/'sidebar.yml'
|
|
106
|
-
yml =
|
|
107
|
-
|
|
134
|
+
yml = yaml.dump(parsed_struct, Dumper=IndentDumper, sort_keys=False)
|
|
135
|
+
|
|
108
136
|
if printit: return print(yml)
|
|
109
137
|
yml_path.write_text(yml)
|
|
110
138
|
|
|
111
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
139
|
+
# %% ../nbs/api/14_quarto.ipynb 15
|
|
112
140
|
_quarto_yml="""project:
|
|
113
141
|
type: website
|
|
114
142
|
|
|
@@ -130,7 +158,7 @@ website:
|
|
|
130
158
|
|
|
131
159
|
metadata-files: [nbdev.yml, sidebar.yml]"""
|
|
132
160
|
|
|
133
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
161
|
+
# %% ../nbs/api/14_quarto.ipynb 16
|
|
134
162
|
_nbdev_yml="""project:
|
|
135
163
|
output-dir: {doc_path}
|
|
136
164
|
|
|
@@ -142,7 +170,7 @@ website:
|
|
|
142
170
|
repo-url: "{git_url}"
|
|
143
171
|
"""
|
|
144
172
|
|
|
145
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
173
|
+
# %% ../nbs/api/14_quarto.ipynb 17
|
|
146
174
|
def refresh_quarto_yml():
|
|
147
175
|
"Generate `_quarto.yml` from `settings.ini`."
|
|
148
176
|
cfg = get_config()
|
|
@@ -156,13 +184,13 @@ def refresh_quarto_yml():
|
|
|
156
184
|
if qy.exists() and not str2bool(cfg.get('custom_quarto_yml', True)): qy.unlink()
|
|
157
185
|
if not qy.exists(): qy.write_text(_quarto_yml)
|
|
158
186
|
|
|
159
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
187
|
+
# %% ../nbs/api/14_quarto.ipynb 18
|
|
160
188
|
def _ensure_quarto():
|
|
161
189
|
if shutil.which('quarto'): return
|
|
162
190
|
print("Quarto is not installed. We will download and install it for you.")
|
|
163
191
|
install.__wrapped__()
|
|
164
192
|
|
|
165
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
193
|
+
# %% ../nbs/api/14_quarto.ipynb 19
|
|
166
194
|
def _pre_docs(path=None, n_workers:int=defaults.cpus, **kwargs):
|
|
167
195
|
cfg = get_config()
|
|
168
196
|
path = Path(path) if path else cfg.nbs_path
|
|
@@ -174,21 +202,21 @@ def _pre_docs(path=None, n_workers:int=defaults.cpus, **kwargs):
|
|
|
174
202
|
cache = proc_nbs(path, n_workers=n_workers, **kwargs)
|
|
175
203
|
return cache,cfg,path
|
|
176
204
|
|
|
177
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
205
|
+
# %% ../nbs/api/14_quarto.ipynb 20
|
|
178
206
|
@call_parse
|
|
179
207
|
@delegates(proc_nbs)
|
|
180
208
|
def nbdev_proc_nbs(**kwargs):
|
|
181
209
|
"Process notebooks in `path` for docs rendering"
|
|
182
210
|
_pre_docs(**kwargs)[0]
|
|
183
211
|
|
|
184
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
212
|
+
# %% ../nbs/api/14_quarto.ipynb 22
|
|
185
213
|
def _readme_mtime_not_older(readme_path, readme_nb_path):
|
|
186
214
|
if not readme_nb_path.exists():
|
|
187
215
|
print(f"Could not find {readme_nb_path}")
|
|
188
216
|
return True
|
|
189
217
|
return readme_path.exists() and readme_path.stat().st_mtime>=readme_nb_path.stat().st_mtime
|
|
190
218
|
|
|
191
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
219
|
+
# %% ../nbs/api/14_quarto.ipynb 23
|
|
192
220
|
class _SidebarYmlRemoved:
|
|
193
221
|
"Context manager for `nbdev_readme` to avoid rendering whole docs website"
|
|
194
222
|
def __init__(self,path): self._path=path
|
|
@@ -201,14 +229,14 @@ class _SidebarYmlRemoved:
|
|
|
201
229
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
202
230
|
if self._moved: (self._path/'sidebar.yml.bak').rename(self._yml_path)
|
|
203
231
|
|
|
204
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
232
|
+
# %% ../nbs/api/14_quarto.ipynb 24
|
|
205
233
|
def _copytree(a,b):
|
|
206
234
|
if sys.version_info.major >=3 and sys.version_info.minor >=8: copytree(a, b, dirs_exist_ok=True)
|
|
207
235
|
else:
|
|
208
236
|
from distutils.dir_util import copy_tree
|
|
209
237
|
copy_tree(a, b)
|
|
210
238
|
|
|
211
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
239
|
+
# %% ../nbs/api/14_quarto.ipynb 25
|
|
212
240
|
def _save_cached_readme(cache, cfg):
|
|
213
241
|
tmp_doc_path = cache/cfg.doc_path.name
|
|
214
242
|
readme = tmp_doc_path/'README.md'
|
|
@@ -219,11 +247,12 @@ def _save_cached_readme(cache, cfg):
|
|
|
219
247
|
_rdmi = tmp_doc_path/((cache/cfg.readme_nb).stem + '_files') # Supporting files for README
|
|
220
248
|
if _rdmi.exists(): _copytree(_rdmi, cfg.config_path/_rdmi.name)
|
|
221
249
|
|
|
222
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
250
|
+
# %% ../nbs/api/14_quarto.ipynb 26
|
|
223
251
|
@call_parse
|
|
224
252
|
def nbdev_readme(
|
|
225
253
|
path:str=None, # Path to notebooks
|
|
226
254
|
chk_time:bool=False): # Only build if out of date
|
|
255
|
+
"Create README.md from readme_nb (index.ipynb by default)"
|
|
227
256
|
cfg = get_config()
|
|
228
257
|
path = Path(path) if path else cfg.nbs_path
|
|
229
258
|
if chk_time and _readme_mtime_not_older(cfg.config_path/'README.md', path/cfg.readme_nb): return
|
|
@@ -234,7 +263,7 @@ def nbdev_readme(
|
|
|
234
263
|
|
|
235
264
|
_save_cached_readme(cache, cfg)
|
|
236
265
|
|
|
237
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
266
|
+
# %% ../nbs/api/14_quarto.ipynb 29
|
|
238
267
|
@call_parse
|
|
239
268
|
@delegates(_nbglob_docs)
|
|
240
269
|
def nbdev_docs(
|
|
@@ -248,7 +277,7 @@ def nbdev_docs(
|
|
|
248
277
|
shutil.rmtree(cfg.doc_path, ignore_errors=True)
|
|
249
278
|
move(cache/cfg.doc_path.name, cfg.config_path)
|
|
250
279
|
|
|
251
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
280
|
+
# %% ../nbs/api/14_quarto.ipynb 31
|
|
252
281
|
@call_parse
|
|
253
282
|
def prepare():
|
|
254
283
|
"Export, test, and clean notebooks, and render README if needed"
|
|
@@ -259,7 +288,7 @@ def prepare():
|
|
|
259
288
|
refresh_quarto_yml()
|
|
260
289
|
nbdev_readme.__wrapped__(chk_time=True)
|
|
261
290
|
|
|
262
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
291
|
+
# %% ../nbs/api/14_quarto.ipynb 33
|
|
263
292
|
@contextmanager
|
|
264
293
|
def fs_watchdog(func, path, recursive:bool=True):
|
|
265
294
|
"File system watchdog dispatching to `func`"
|
|
@@ -275,13 +304,14 @@ def fs_watchdog(func, path, recursive:bool=True):
|
|
|
275
304
|
observer.stop()
|
|
276
305
|
observer.join()
|
|
277
306
|
|
|
278
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
307
|
+
# %% ../nbs/api/14_quarto.ipynb 34
|
|
279
308
|
@call_parse
|
|
280
309
|
@delegates(_nbglob_docs)
|
|
281
310
|
def nbdev_preview(
|
|
282
311
|
path:str=None, # Path to notebooks
|
|
283
312
|
port:int=None, # The port on which to run preview
|
|
284
313
|
host:str=None, # The host on which to run preview
|
|
314
|
+
no_browser:bool=False, # Do not open a browser
|
|
285
315
|
n_workers:int=defaults.cpus, # Number of workers
|
|
286
316
|
**kwargs):
|
|
287
317
|
"Preview docs locally"
|
|
@@ -290,6 +320,7 @@ def nbdev_preview(
|
|
|
290
320
|
xtra = []
|
|
291
321
|
if port: xtra += ['--port', str(port)]
|
|
292
322
|
if host: xtra += ['--host', host]
|
|
323
|
+
if no_browser: xtra += ['--no-browser']
|
|
293
324
|
|
|
294
325
|
def _f(e):
|
|
295
326
|
res = _proc_file(Path(e.src_path), cache, path)
|
nbdev/release.py
CHANGED
|
@@ -201,7 +201,8 @@ def _get_conda_meta():
|
|
|
201
201
|
doc_url = (cfg.doc_host + cfg.doc_baseurl) if (cfg.doc_host and cfg.doc_baseurl) else url
|
|
202
202
|
dev_url = cfg.git_url if cfg.git_url else url
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
hostreqs = ['packaging', f'python >={cfg.min_python}']
|
|
205
|
+
reqs = hostreqs+[]
|
|
205
206
|
if cfg.get('requirements'): reqs += cfg.requirements.split()
|
|
206
207
|
if cfg.get('conda_requirements'): reqs += cfg.conda_requirements.split()
|
|
207
208
|
|
|
@@ -220,7 +221,7 @@ def _get_conda_meta():
|
|
|
220
221
|
d2 = {
|
|
221
222
|
'build': {'number': '0', 'noarch': 'python',
|
|
222
223
|
'script': '{{ PYTHON }} -m pip install . -vv'},
|
|
223
|
-
'requirements': {'host':
|
|
224
|
+
'requirements': {'host':hostreqs, 'run':reqs},
|
|
224
225
|
'test': {'imports': [cfg.lib_path.name]},
|
|
225
226
|
'about': {
|
|
226
227
|
'license': 'Apache Software',
|
|
@@ -312,7 +313,7 @@ def release_pypi(
|
|
|
312
313
|
system(f'cd {_dir} && rm -rf dist build && python setup.py sdist bdist_wheel')
|
|
313
314
|
system(f'twine upload --repository {repository} {_dir}/dist/*')
|
|
314
315
|
|
|
315
|
-
# %% ../nbs/api/18_release.ipynb
|
|
316
|
+
# %% ../nbs/api/18_release.ipynb 52
|
|
316
317
|
@call_parse
|
|
317
318
|
def release_both(
|
|
318
319
|
path:str='conda', # Path where package will be created
|
|
@@ -328,7 +329,7 @@ def release_both(
|
|
|
328
329
|
release_conda.__wrapped__(path, do_build=do_build, build_args=build_args, skip_upload=skip_upload, mambabuild=mambabuild, upload_user=upload_user)
|
|
329
330
|
nbdev_bump_version.__wrapped__()
|
|
330
331
|
|
|
331
|
-
# %% ../nbs/api/18_release.ipynb
|
|
332
|
+
# %% ../nbs/api/18_release.ipynb 54
|
|
332
333
|
def bump_version(version, part=2, unbump=False):
|
|
333
334
|
version = version.split('.')
|
|
334
335
|
incr = -1 if unbump else 1
|
|
@@ -336,7 +337,7 @@ def bump_version(version, part=2, unbump=False):
|
|
|
336
337
|
for i in range(part+1, 3): version[i] = '0'
|
|
337
338
|
return '.'.join(version)
|
|
338
339
|
|
|
339
|
-
# %% ../nbs/api/18_release.ipynb
|
|
340
|
+
# %% ../nbs/api/18_release.ipynb 55
|
|
340
341
|
@call_parse
|
|
341
342
|
def nbdev_bump_version(
|
|
342
343
|
part:int=2, # Part of version to bump
|
nbdev/serve.py
CHANGED
|
@@ -22,8 +22,8 @@ def _is_qpy(path:Path):
|
|
|
22
22
|
"Is `path` a py script starting with frontmatter?"
|
|
23
23
|
path = Path(path)
|
|
24
24
|
if not path.suffix=='.py': return
|
|
25
|
-
p = ast.parse(path.read_text())
|
|
26
|
-
# try: p = ast.parse(path.read_text())
|
|
25
|
+
p = ast.parse(path.read_text(encoding='utf-8'))
|
|
26
|
+
# try: p = ast.parse(path.read_text(encoding='utf-8'))
|
|
27
27
|
# except: return
|
|
28
28
|
if not p.body: return
|
|
29
29
|
a = p.body[0]
|
|
@@ -62,15 +62,15 @@ def proc_nbs(
|
|
|
62
62
|
cfg = get_config()
|
|
63
63
|
cache = cfg.config_path/'_proc'
|
|
64
64
|
path = Path(path or cfg.nbs_path)
|
|
65
|
-
files = nbglob(path, func=Path, file_glob=
|
|
65
|
+
files = nbglob(path, func=Path, file_glob=file_glob, file_re=file_re, **kwargs)
|
|
66
66
|
if (path/'_quarto.yml').exists(): files.append(path/'_quarto.yml')
|
|
67
|
-
if (path/'_extensions').exists(): files.extend(nbglob(path/'_extensions', func=Path, file_glob=
|
|
67
|
+
if (path/'_extensions').exists(): files.extend(nbglob(path/'_extensions', func=Path, file_glob=file_glob, file_re=file_re, skip_file_re='^[.]'))
|
|
68
68
|
|
|
69
69
|
# If settings.ini or filter script newer than cache folder modified, delete cache
|
|
70
70
|
chk_mtime = max(cfg.config_file.stat().st_mtime, Path(__file__).stat().st_mtime)
|
|
71
71
|
cache.mkdir(parents=True, exist_ok=True)
|
|
72
72
|
cache_mtime = cache.stat().st_mtime
|
|
73
|
-
if force or (cache.exists and cache_mtime<chk_mtime): rmtree(cache)
|
|
73
|
+
if force or (cache.exists() and cache_mtime<chk_mtime): rmtree(cache)
|
|
74
74
|
|
|
75
75
|
files = files.map(_proc_file, mtime=cache_mtime, cache=cache, path=path).filter()
|
|
76
76
|
kw = {} if IN_NOTEBOOK else {'method':'spawn'}
|
nbdev/showdoc.py
CHANGED
|
@@ -29,7 +29,7 @@ def _escape_markdown(s):
|
|
|
29
29
|
return s.replace('\n', '<br>')
|
|
30
30
|
|
|
31
31
|
# %% ../nbs/api/08_showdoc.ipynb 9
|
|
32
|
-
def _maybe_nm(o):
|
|
32
|
+
def _maybe_nm(o):
|
|
33
33
|
if (o == inspect._empty): return ''
|
|
34
34
|
else: return o.__name__ if hasattr(o, '__name__') else _escape_markdown(str(o))
|
|
35
35
|
|
|
@@ -40,7 +40,7 @@ def _list2row(l:list): return '| '+' | '.join([_maybe_nm(o) for o in l]) + ' |'
|
|
|
40
40
|
class DocmentTbl:
|
|
41
41
|
# this is the column order we want these items to appear
|
|
42
42
|
_map = OrderedDict({'anno':'Type', 'default':'Default', 'docment':'Details'})
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
def __init__(self, obj, verbose=True, returns=True):
|
|
45
45
|
"Compute the docment table string"
|
|
46
46
|
self.verbose = verbose
|
|
@@ -52,30 +52,30 @@ class DocmentTbl:
|
|
|
52
52
|
if 'self' in _dm: del _dm['self']
|
|
53
53
|
for d in _dm.values(): d['docment'] = ifnone(d['docment'], inspect._empty)
|
|
54
54
|
self.dm = _dm
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
@property
|
|
57
57
|
def _columns(self):
|
|
58
58
|
"Compute the set of fields that have at least one non-empty value so we don't show tables empty columns"
|
|
59
59
|
cols = set(flatten(L(self.dm.values()).filter().map(_non_empty_keys)))
|
|
60
60
|
candidates = self._map if self.verbose else {'docment': 'Details'}
|
|
61
61
|
return OrderedDict({k:v for k,v in candidates.items() if k in cols})
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
@property
|
|
64
|
-
def has_docment(self): return 'docment' in self._columns and self._row_list
|
|
64
|
+
def has_docment(self): return 'docment' in self._columns and self._row_list
|
|
65
65
|
|
|
66
66
|
@property
|
|
67
67
|
def has_return(self): return self.returns and bool(_non_empty_keys(self.dm.get('return', {})))
|
|
68
|
-
|
|
69
|
-
def _row(self, nm, props):
|
|
68
|
+
|
|
69
|
+
def _row(self, nm, props):
|
|
70
70
|
"unpack data for single row to correspond with column names."
|
|
71
71
|
return [nm] + [props[c] for c in self._columns]
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
@property
|
|
74
74
|
def _row_list(self):
|
|
75
75
|
"unpack data for all rows."
|
|
76
76
|
ordered_params = [(p, self.dm[p]) for p in self.params if p != 'self' and p in self.dm]
|
|
77
77
|
return L([self._row(nm, props) for nm,props in ordered_params])
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
@property
|
|
80
80
|
def _hdr_list(self): return [' '] + [_bold(l) for l in L(self._columns.values())]
|
|
81
81
|
|
|
@@ -84,23 +84,23 @@ class DocmentTbl:
|
|
|
84
84
|
"The markdown string for the header portion of the table"
|
|
85
85
|
md = _list2row(self._hdr_list)
|
|
86
86
|
return md + '\n' + _list2row(['-' * len(l) for l in self._hdr_list])
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
@property
|
|
89
|
-
def params_str(self):
|
|
89
|
+
def params_str(self):
|
|
90
90
|
"The markdown string for the parameters portion of the table."
|
|
91
91
|
return '\n'.join(self._row_list.map(_list2row))
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
@property
|
|
94
94
|
def return_str(self):
|
|
95
95
|
"The markdown string for the returns portion of the table."
|
|
96
96
|
return _list2row(['**Returns**']+[_bold(_maybe_nm(self.dm['return'][c])) for c in self._columns])
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
def _repr_markdown_(self):
|
|
99
99
|
if not self.has_docment: return ''
|
|
100
100
|
_tbl = [self.hdr_str, self.params_str]
|
|
101
101
|
if self.has_return: _tbl.append(self.return_str)
|
|
102
102
|
return '\n'.join(_tbl)
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
def __eq__(self,other): return self.__str__() == str(other).strip()
|
|
105
105
|
|
|
106
106
|
__str__ = _repr_markdown_
|
|
@@ -171,7 +171,7 @@ class BasicMarkdownRenderer(ShowDocRenderer):
|
|
|
171
171
|
doc += f'{h} {self.nm}\n\n'
|
|
172
172
|
sig = _wrap_sig(f"{self.nm} {_fmt_sig(self.sig)}") if self.sig else ''
|
|
173
173
|
doc += f'{sig}'
|
|
174
|
-
if self.docs: doc += f"\n\n{self.docs}"
|
|
174
|
+
if self.docs: doc += f"\n\n*{self.docs}*"
|
|
175
175
|
if self.dm.has_docment: doc += f"\n\n{self.dm}"
|
|
176
176
|
return doc
|
|
177
177
|
__repr__=__str__=_repr_markdown_
|
|
@@ -199,7 +199,7 @@ class BasicHtmlRenderer(ShowDocRenderer):
|
|
|
199
199
|
doc = '<hr/>\n'
|
|
200
200
|
doc += f'<h{self.title_level}>{self.nm}</h{self.title_level}>\n'
|
|
201
201
|
doc += f'<blockquote><pre><code>{self.nm}{_fmt_sig(self.sig)}</code></pre></blockquote>'
|
|
202
|
-
if self.docs: doc += f"<p>{self.docs}</p>"
|
|
202
|
+
if self.docs: doc += f"<p><i>{self.docs}</i></p>"
|
|
203
203
|
return doc
|
|
204
204
|
|
|
205
205
|
def doc(self):
|
nbdev/test.py
CHANGED
|
@@ -85,7 +85,8 @@ def nbdev_test(
|
|
|
85
85
|
if n_workers is None: n_workers = 0 if len(files)==1 else min(num_cpus(), 8)
|
|
86
86
|
if IN_NOTEBOOK: kw = {'method':'spawn'} if os.name=='nt' else {'method':'forkserver'}
|
|
87
87
|
else: kw = {}
|
|
88
|
-
|
|
88
|
+
wd_pth = get_config().nbs_path
|
|
89
|
+
with working_directory(wd_pth if (wd_pth and wd_pth.exists()) else os.getcwd()):
|
|
89
90
|
results = parallel(test_nb, files, skip_flags=skip_flags, force_flags=force_flags, n_workers=n_workers,
|
|
90
91
|
basepath=get_config().config_path, pause=pause, do_print=do_print, **kw)
|
|
91
92
|
passed,times = zip(*results)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nbdev
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.14
|
|
4
4
|
Summary: Create delightful software with Jupyter Notebooks
|
|
5
5
|
Home-page: https://github.com/fastai/nbdev
|
|
6
6
|
Author: Jeremy Howard and Hamel Husain
|
|
@@ -15,14 +15,17 @@ Classifier: Programming Language :: Python :: 3.7
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
19
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
19
20
|
Requires-Python: >=3.7
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
21
22
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: packaging
|
|
24
|
+
Requires-Dist: ipywidgets <=8.0.4
|
|
25
|
+
Requires-Dist: fastcore >=1.5.27
|
|
26
|
+
Requires-Dist: execnb >=0.1.4
|
|
24
27
|
Requires-Dist: astunparse
|
|
25
|
-
Requires-Dist: ghapi
|
|
28
|
+
Requires-Dist: ghapi >=1.0.3
|
|
26
29
|
Requires-Dist: watchdog
|
|
27
30
|
Requires-Dist: asttokens
|
|
28
31
|
Requires-Dist: PyYAML
|
|
@@ -35,22 +38,12 @@ Requires-Dist: black ; extra == 'dev'
|
|
|
35
38
|
Requires-Dist: svg.py ; extra == 'dev'
|
|
36
39
|
Requires-Dist: jupyter ; extra == 'dev'
|
|
37
40
|
|
|
38
|
-
Getting Started
|
|
39
|
-
================
|
|
41
|
+
# Getting Started
|
|
40
42
|
|
|
41
43
|
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
42
44
|
|
|
43
45
|

|
|
44
46
|
|
|
45
|
-
*NB: This is nbdev v2, a major upgrade of nbdev. Whilst the differences
|
|
46
|
-
to nbdev1 aren’t huge, it does require some changes. The old version
|
|
47
|
-
docs are at [nbdev1.fast.ai](https://nbdev1.fast.ai). You can use
|
|
48
|
-
version-pinning in `settings.ini` (i.e `'nbdev<2'`) to stop nbdev from
|
|
49
|
-
upgrading. To upgrade, follow the [migration
|
|
50
|
-
tutorial](https://nbdev.fast.ai/migrating.html).*
|
|
51
|
-
|
|
52
|
-
------------------------------------------------------------------------
|
|
53
|
-
|
|
54
47
|
`nbdev` is a notebook-driven development platform. Simply write
|
|
55
48
|
notebooks with lightweight markup and get high-quality documentation,
|
|
56
49
|
tests, continuous integration, and packaging for free!
|
|
@@ -129,35 +122,34 @@ available commands:
|
|
|
129
122
|
!nbdev_help
|
|
130
123
|
```
|
|
131
124
|
|
|
132
|
-
nbdev_bump_version
|
|
133
|
-
nbdev_changelog
|
|
134
|
-
nbdev_clean
|
|
135
|
-
nbdev_conda
|
|
136
|
-
nbdev_create_config
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
nbdev_update Propagate change in modules matching `fname` to notebooks that created them
|
|
125
|
+
nbdev_bump_version Increment version in settings.ini by one
|
|
126
|
+
nbdev_changelog Create a CHANGELOG.md file from closed and labeled GitHub issues
|
|
127
|
+
nbdev_clean Clean all notebooks in `fname` to avoid merge conflicts
|
|
128
|
+
nbdev_conda Create a `meta.yaml` file ready to be built into a package, and optionally build and upload it
|
|
129
|
+
nbdev_create_config Create a config file.
|
|
130
|
+
nbdev_docs Create Quarto docs and README.md
|
|
131
|
+
nbdev_export Export notebooks in `path` to Python modules
|
|
132
|
+
nbdev_filter A notebook filter for Quarto
|
|
133
|
+
nbdev_fix Create working notebook from conflicted notebook `nbname`
|
|
134
|
+
nbdev_help Show help for all console scripts
|
|
135
|
+
nbdev_install Install Quarto and the current library
|
|
136
|
+
nbdev_install_hooks Install Jupyter and git hooks to automatically clean, trust, and fix merge conflicts in notebooks
|
|
137
|
+
nbdev_install_quarto Install latest Quarto on macOS or Linux, prints instructions for Windows
|
|
138
|
+
nbdev_merge Git merge driver for notebooks
|
|
139
|
+
nbdev_migrate Convert all markdown and notebook files in `path` from v1 to v2
|
|
140
|
+
nbdev_new Create an nbdev project.
|
|
141
|
+
nbdev_prepare Export, test, and clean notebooks, and render README if needed
|
|
142
|
+
nbdev_preview Preview docs locally
|
|
143
|
+
nbdev_proc_nbs Process notebooks in `path` for docs rendering
|
|
144
|
+
nbdev_pypi Create and upload Python package to PyPI
|
|
145
|
+
nbdev_readme Create README.md from readme_nb (index.ipynb by default)
|
|
146
|
+
nbdev_release_both Release both conda and PyPI packages
|
|
147
|
+
nbdev_release_gh Calls `nbdev_changelog`, lets you edit the result, then pushes to git and calls `nbdev_release_git`
|
|
148
|
+
nbdev_release_git Tag and create a release in GitHub for the current version
|
|
149
|
+
nbdev_sidebar Create sidebar.yml
|
|
150
|
+
nbdev_test Test in parallel notebooks matching `path`, passing along `flags`
|
|
151
|
+
nbdev_trust Trust notebooks matching `fname`
|
|
152
|
+
nbdev_update Propagate change in modules matching `fname` to notebooks that created them
|
|
161
153
|
|
|
162
154
|
## FAQ
|
|
163
155
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
nbdev/__init__.py,sha256=Lvc7TM66PIvnPiPsx8AcP4Y5wo23vGqy1GYdDnNAzZY,90
|
|
2
|
+
nbdev/_modidx.py,sha256=TdX0nyg7miq1j7D_LN99_kAmqQJEbrrlIJ7XBM1PDHs,39709
|
|
3
|
+
nbdev/clean.py,sha256=lLtfhCLjkDBqf8MvIG9lDSWSmA9vUDvn2QhS6LPprHI,9284
|
|
4
|
+
nbdev/cli.py,sha256=nn9UxmpJaA_NorxDwP6jlFl5XnmWQdFqJHt0XAaKfbM,5637
|
|
5
|
+
nbdev/config.py,sha256=fTTtYCQHyp8sXCFAyGN93eLOr3N3uOkv4LJ5-9Bclxs,12235
|
|
6
|
+
nbdev/doclinks.py,sha256=X5d8Xz52p-LlWwhhCN6d-00RQ0E8BsONuPH_xUSA6kc,10287
|
|
7
|
+
nbdev/export.py,sha256=LEhn7UpfMSm9QKrC31q91Bsw-ZHd68DNv7MjhsNi15A,3275
|
|
8
|
+
nbdev/extract_attachments.py,sha256=O4mS4EFIOXL_yQ3jmsnBStrWxGR_nPNvxLYXHtLeimw,2208
|
|
9
|
+
nbdev/frontmatter.py,sha256=L9XbOPA99XuHSWU6eJfRIR4mBU4-NhDIQSP80EIk51g,2708
|
|
10
|
+
nbdev/imports.py,sha256=f5Ynco14hsJyFCf43-uP_YARMhHADe6lM-20Mc_vXhw,95
|
|
11
|
+
nbdev/maker.py,sha256=1hmOlElYhREqaJEc5jimiZ2Q5kkFUtQ9MNR05B-3L6c,9729
|
|
12
|
+
nbdev/merge.py,sha256=QrP8tdlPRfZZ-TH5dTRwj9jXr7jWEsrLwgcIVPSHJSs,4319
|
|
13
|
+
nbdev/migrate.py,sha256=l2hO2Ymkjm1C3_JmFDsM-DN6bxgFeLjj-pBr1xNqunI,7317
|
|
14
|
+
nbdev/process.py,sha256=7dl9U7JLL9wbJta-KKTTnkMktBVsK5s-y8OBxtfk68I,5863
|
|
15
|
+
nbdev/processors.py,sha256=GyF0UAb9SQ9KEzqPF-iK9-FOtFzcUJENI8pY3sRhLcg,10048
|
|
16
|
+
nbdev/qmd.py,sha256=3Cskd8ynm25Hh7bo-_t0hxCMF6jqXxgq_VfkpLBKu_w,2958
|
|
17
|
+
nbdev/quarto.py,sha256=uPburjx-OG9ruNTI08Ku_iOV5gerUUGNQUXDeOGmAco,12017
|
|
18
|
+
nbdev/release.py,sha256=1rqBx77XPWSfb9JW8X7rTyaHz7wc4aRd3BHIkJafcKI,14229
|
|
19
|
+
nbdev/serve.py,sha256=7Gpu8tQGrHzNW4UJO-xz6AWQo7xKTWxXOOsHq606M58,3044
|
|
20
|
+
nbdev/serve_drv.py,sha256=IZ2acem_KKsXYYe0iUECiR_orkYLBkT1ZG_258ZS7SQ,657
|
|
21
|
+
nbdev/showdoc.py,sha256=sWkpTLpWLUMQBsysHqyvS7czIVcmfkJ_pjSU3zcp-AI,9150
|
|
22
|
+
nbdev/sync.py,sha256=ZKcWRJd49EaYJXeIB8hSa8oWHDrqRWoyRbGUGHDYxJg,2898
|
|
23
|
+
nbdev/test.py,sha256=74db-sK_rnc69Q3beztibXDSZUeOk6M9nIiIORLHzlo,4397
|
|
24
|
+
nbdev-2.3.14.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
25
|
+
nbdev-2.3.14.dist-info/METADATA,sha256=esMAIjRT278jcHcSxvuv-lJyTkUhsDz9O57vHJ7GfV4,10161
|
|
26
|
+
nbdev-2.3.14.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
27
|
+
nbdev-2.3.14.dist-info/entry_points.txt,sha256=lFZD1JQyZQI8x8PuaaSqbfnhInHhpm56nCR6n84nFKk,1272
|
|
28
|
+
nbdev-2.3.14.dist-info/top_level.txt,sha256=3cWYLMuaXsZjz3TQRGEkWGs9Z8ieEDmYcq8TZS3y3vU,6
|
|
29
|
+
nbdev-2.3.14.dist-info/RECORD,,
|
nbdev-2.3.12.dist-info/RECORD
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
nbdev/__init__.py,sha256=FIXLZy7vM0c6pa6BXbUECFllpbDViTDlw76Z4O45EFE,90
|
|
2
|
-
nbdev/_modidx.py,sha256=ho1sHw5rtVkkU87wHZcWp9Yjg0R95mNQuUY6ggbOF3U,38909
|
|
3
|
-
nbdev/clean.py,sha256=lLtfhCLjkDBqf8MvIG9lDSWSmA9vUDvn2QhS6LPprHI,9284
|
|
4
|
-
nbdev/cli.py,sha256=4edu0A4BH6UYEluGpUWx-8JSXbJyTXpRirGNMTxCLj8,4122
|
|
5
|
-
nbdev/config.py,sha256=RNjMuc2L8jilwm1esa9nb0cKTAplCugTmLVCwrbLiJo,12222
|
|
6
|
-
nbdev/doclinks.py,sha256=z7G0Rx2G93VaSsYUkBRLRvp-sGMBQRxzGFy2oCOD-SQ,10011
|
|
7
|
-
nbdev/export.py,sha256=Kyb_OM0z1aGKb4F23KrpyMwrDHAlsTYVzarqjkD9rWs,2520
|
|
8
|
-
nbdev/extract_attachments.py,sha256=O4mS4EFIOXL_yQ3jmsnBStrWxGR_nPNvxLYXHtLeimw,2208
|
|
9
|
-
nbdev/frontmatter.py,sha256=BIyWZ7SUCrdXxSoTcpLJTzO2-bgzCyB5-9RxHezawYA,2193
|
|
10
|
-
nbdev/imports.py,sha256=f5Ynco14hsJyFCf43-uP_YARMhHADe6lM-20Mc_vXhw,95
|
|
11
|
-
nbdev/maker.py,sha256=4I516TYaaWnSkaAlkP1_9KwqyEm6lpA-xjCerzat420,9713
|
|
12
|
-
nbdev/merge.py,sha256=UQ49Y5VSlfq-vsP9lRdjx6HY-Nf-tCwK8RZZ6CSiD-M,4287
|
|
13
|
-
nbdev/migrate.py,sha256=l2hO2Ymkjm1C3_JmFDsM-DN6bxgFeLjj-pBr1xNqunI,7317
|
|
14
|
-
nbdev/process.py,sha256=jcCKRM8_7DX79pMRmK9ey_waCiMY3v-jC62hMfD_3PI,5737
|
|
15
|
-
nbdev/processors.py,sha256=GyF0UAb9SQ9KEzqPF-iK9-FOtFzcUJENI8pY3sRhLcg,10048
|
|
16
|
-
nbdev/qmd.py,sha256=3Cskd8ynm25Hh7bo-_t0hxCMF6jqXxgq_VfkpLBKu_w,2958
|
|
17
|
-
nbdev/quarto.py,sha256=Jv-1U3EtAGXOpGRyfYXtwY7wFPKViCZpnXJVwRzHpjM,10579
|
|
18
|
-
nbdev/release.py,sha256=dwjfJ9XCmo5vAeeQHdI-7n8W39wookKhfYcLt7jPKJM,14185
|
|
19
|
-
nbdev/serve.py,sha256=Aq2jgi-OUpU4_PgBcK11FgyfRuqntw5_7_mse-96wmI,2986
|
|
20
|
-
nbdev/serve_drv.py,sha256=IZ2acem_KKsXYYe0iUECiR_orkYLBkT1ZG_258ZS7SQ,657
|
|
21
|
-
nbdev/showdoc.py,sha256=RlXkrtb2f7z_La25asE_znS2uTipRlArgVwCDKXX0jM,9185
|
|
22
|
-
nbdev/sync.py,sha256=ZKcWRJd49EaYJXeIB8hSa8oWHDrqRWoyRbGUGHDYxJg,2898
|
|
23
|
-
nbdev/test.py,sha256=twe14EExkrCXizYmOm0cnGI35dHwX2mYaANi7vfs_PE,4328
|
|
24
|
-
nbdev-2.3.12.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
25
|
-
nbdev-2.3.12.dist-info/METADATA,sha256=o4LqsnqIVDKQ7LOe3qQ1Iep67KXtPaCuy2XBqEzMtyc,10699
|
|
26
|
-
nbdev-2.3.12.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
27
|
-
nbdev-2.3.12.dist-info/entry_points.txt,sha256=9zzSNu4DhySbWrGiPvj6ZTk5N2fsBYktgkswJDHMCLM,1218
|
|
28
|
-
nbdev-2.3.12.dist-info/top_level.txt,sha256=3cWYLMuaXsZjz3TQRGEkWGs9Z8ieEDmYcq8TZS3y3vU,6
|
|
29
|
-
nbdev-2.3.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|