Skip to content

Commit 14ad26c

Browse files
authored
Merge branch 'fastai:master' into master
2 parents 5ed5ad5 + 8f994cd commit 14ad26c

25 files changed

+444
-60
lines changed

CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,51 @@
22

33
<!-- do not remove -->
44

5+
## 2.3.9
6+
7+
### New Features
8+
9+
- utility that creates a `requirements.txt` file from `settings.ini` ([#1202](https://github.com/fastai/nbdev/pull/1202)), thanks to [@hamelsmu](https://github.com/hamelsmu)
10+
- user-friendly error if py file has `# %%` comments with unexpected format ([#1211](https://github.com/fastai/nbdev/pull/1211)), thanks to [@seeM](https://github.com/seeM)
11+
- add parameter for name to `nb_export` ([#1204](https://github.com/fastai/nbdev/pull/1204)), thanks to [@hamelsmu](https://github.com/hamelsmu)
12+
- ensure newline at end of `_modidx.py` ([#1186](https://github.com/fastai/nbdev/issues/1186))
13+
14+
### Bugs Squashed
15+
16+
- end `sidebar.yml` with newline ([#1212](https://github.com/fastai/nbdev/pull/1212)), thanks to [@seeM](https://github.com/seeM)
17+
- fix: incorrect regex pattern for setting `output-file` ([#1210](https://github.com/fastai/nbdev/pull/1210)), thanks to [@seeM](https://github.com/seeM)
18+
- ensure newline at end of `_modidx.py` ([#1209](https://github.com/fastai/nbdev/pull/1209)), thanks to [@seeM](https://github.com/seeM)
19+
- fix: `nbdev_install_quarto` may install and remove unrelated packages ([#1208](https://github.com/fastai/nbdev/pull/1208)), thanks to [@seeM](https://github.com/seeM)
20+
- fix: key error if widgets is missing `state` ([#1207](https://github.com/fastai/nbdev/pull/1207)), thanks to [@seeM](https://github.com/seeM)
21+
-`nbdev_install_quarto` may install and remove unrelated packages ([#1182](https://github.com/fastai/nbdev/issues/1182))
22+
- Key error if widgets is missing `state` ([#1167](https://github.com/fastai/nbdev/issues/1167))
23+
24+
25+
## 2.3.8
26+
27+
### New Features
28+
29+
- better error messages for `nbdev_migrate` ([#1177](https://github.com/fastai/nbdev/issues/1177))
30+
- experimental: Users can provide extra processors via the `procs` key in `settings.ini` ([#1157](https://github.com/fastai/nbdev/pull/1157)), thanks to [@seeM](https://github.com/seeM)
31+
- enable Documentation Only Sites With `nbdev` ( + tutorial ) ([#1121](https://github.com/fastai/nbdev/pull/1121)), thanks to [@hamelsmu](https://github.com/hamelsmu)
32+
- support non-library projects ([#1119](https://github.com/fastai/nbdev/issues/1119))
33+
- throw a warning when imports and code are mixed in a cell ([#714](https://github.com/fastai/nbdev/issues/714))
34+
35+
### Bugs Squashed
36+
37+
- fix duplicated sections in the sidebar ([#1165](https://github.com/fastai/nbdev/pull/1165)), thanks to [@seeM](https://github.com/seeM)
38+
- setting `#| echo` in a cell with `show_doc` causes a Quarto error ([#1163](https://github.com/fastai/nbdev/issues/1163))
39+
- fix copying of index assets ([#1143](https://github.com/fastai/nbdev/pull/1143)), thanks to [@hamelsmu](https://github.com/hamelsmu)
40+
- images in index.ipynb causing deployments issues ([#1140](https://github.com/fastai/nbdev/issues/1140))
41+
- clean takes forever on notebooks with large output ([#1132](https://github.com/fastai/nbdev/issues/1132))
42+
- `nbdev_update` includes folders starting with `_` or `.` (e.g. `.ipynb_checkpoints`) ([#1130](https://github.com/fastai/nbdev/pull/1130)), thanks to [@seeM](https://github.com/seeM)
43+
- `nbdev_new` defaults bool parameters to `False` (e.g. `put_version_in_init`) ([#1129](https://github.com/fastai/nbdev/pull/1129)), thanks to [@seeM](https://github.com/seeM)
44+
- `black_formatting` setting is ignored ([#1122](https://github.com/fastai/nbdev/pull/1122)), thanks to [@jmoralez](https://github.com/jmoralez)
45+
- `nbdev_readme()` fails on the second run for the notebook with support files (e.g. Fig image). ([#1106](https://github.com/fastai/nbdev/issues/1106))
46+
- `nbdev_new` fails with `AttributeError: path_` ([#1063](https://github.com/fastai/nbdev/issues/1063))
47+
- fix #1041 ([#1049](https://github.com/fastai/nbdev/pull/1049)), thanks to [@seeM](https://github.com/seeM)
48+
49+
550
## 2.3.7
651

752
### New Features

nbdev/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "2.3.8"
1+
__version__ = "2.3.10"
22

33
from .doclinks import nbdev_export
44
from .showdoc import show_doc

nbdev/_modidx.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@
277277
'nbdev.release.release_gh': ('api/release.html#release_gh', 'nbdev/release.py'),
278278
'nbdev.release.release_git': ('api/release.html#release_git', 'nbdev/release.py'),
279279
'nbdev.release.release_pypi': ('api/release.html#release_pypi', 'nbdev/release.py'),
280-
'nbdev.release.write_conda_meta': ('api/release.html#write_conda_meta', 'nbdev/release.py')},
280+
'nbdev.release.write_conda_meta': ('api/release.html#write_conda_meta', 'nbdev/release.py'),
281+
'nbdev.release.write_requirements': ('api/release.html#write_requirements', 'nbdev/release.py')},
281282
'nbdev.serve': { 'nbdev.serve._is_qpy': ('api/serve.html#_is_qpy', 'nbdev/serve.py'),
282283
'nbdev.serve._proc_file': ('api/serve.html#_proc_file', 'nbdev/serve.py'),
283284
'nbdev.serve.proc_nbs': ('api/serve.html#proc_nbs', 'nbdev/serve.py')},
@@ -331,4 +332,4 @@
331332
'nbdev.sync.nbdev_update': ('api/sync.html#nbdev_update', 'nbdev/sync.py')},
332333
'nbdev.test': { 'nbdev.test._keep_file': ('api/test.html#_keep_file', 'nbdev/test.py'),
333334
'nbdev.test.nbdev_test': ('api/test.html#nbdev_test', 'nbdev/test.py'),
334-
'nbdev.test.test_nb': ('api/test.html#test_nb', 'nbdev/test.py')}}}
335+
'nbdev.test.test_nb': ('api/test.html#test_nb', 'nbdev/test.py')}}}

nbdev/doclinks.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@ def _iter_py_cells(p):
4949
cells = p.read_text().split("\n# %% ")
5050
for cell in cells[1:]:
5151
top,code = cell.split('\n', 1)
52-
nb,idx = top.split()
52+
try: nb,idx = top.split()
53+
except ValueError: raise ValueError(f"Unexpected format in '{p}' at cell:\n```\n# %% {cell.strip()}.\n```\n"
54+
"The expected format is: '# %% {nb_path} {cell_idx}'.")
5355
nb_path = None if nb=='auto' else (p.parent/nb).resolve() # NB paths are stored relative to .py file
5456
if code.endswith('\n'): code=code[:-1]
5557
yield AttrDict(nb=nb, idx=int(idx), code=code, nb_path=nb_path, py_path=p.resolve())
5658

5759
# %% ../nbs/api/doclinks.ipynb 11
58-
def _nbpath2html(p): return p.with_name(re.sub(r'\d+[a-zA-Z0-9]*_', '', p.name.lower())).with_suffix('.html')
60+
def _nbpath2html(p): return p.with_name(re.sub(r'^\d+[a-zA-Z0-9]*_', '', p.name.lower())).with_suffix('.html')
5961

6062
# %% ../nbs/api/doclinks.ipynb 13
6163
def _get_modidx(py_path, code_root, nbs_path):
@@ -96,7 +98,7 @@ def _build_modidx(dest=None, nbs_path=None, skip_exists=False):
9698
code_root = dest.parent.resolve()
9799
for file in globtastic(dest, file_glob="*.py", skip_file_re='^_', skip_folder_re="\.ipynb_checkpoints"):
98100
res['syms'].update(_get_modidx((dest.parent/file).resolve(), code_root, nbs_path=nbs_path))
99-
idxfile.write_text("# Autogenerated by nbdev\n\nd = "+pformat(res, width=140, indent=2, compact=True))
101+
idxfile.write_text("# Autogenerated by nbdev\n\nd = "+pformat(res, width=140, indent=2, compact=True)+'\n')
100102

101103
# %% ../nbs/api/doclinks.ipynb 20
102104
@delegates(globtastic)

nbdev/export.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,19 @@ def black_format(cell, # Cell to format
4141
except: pass
4242

4343
# %% ../nbs/api/export.ipynb 9
44-
def nb_export(nbname, lib_path=None, procs=black_format, debug=False, mod_maker=ModuleMaker):
44+
def nb_export(nbname, lib_path=None, procs=black_format, debug=False, mod_maker=ModuleMaker, name=None):
4545
"Create module(s) from notebook"
4646
if lib_path is None: lib_path = get_config().lib_path
4747
exp = ExportModuleProc()
4848
nb = NBProcessor(nbname, [exp]+L(procs), debug=debug)
4949
nb.process()
5050
for mod,cells in exp.modules.items():
5151
all_cells = exp.in_all[mod]
52-
name = getattr(exp, 'default_exp', None) if mod=='#' else mod
53-
if not name:
52+
nm = ifnone(name, getattr(exp, 'default_exp', None) if mod=='#' else mod)
53+
if not nm:
5454
warn(f"Notebook '{nbname}' uses `#|export` without `#|default_exp` cell.\n"
5555
"Note nbdev2 no longer supports nbdev1 syntax. Run `nbdev_migrate` to upgrade.\n"
5656
"See https://nbdev.fast.ai/getting_started.html for more information.")
5757
return
58-
mm = mod_maker(dest=lib_path, name=name, nb_path=nbname, is_new=mod=='#')
58+
mm = mod_maker(dest=lib_path, name=nm, nb_path=nbname, is_new=bool(name) or mod=='#')
5959
mm.make(cells, all_cells, lib_path=lib_path)

nbdev/migrate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# %% ../nbs/api/migrate.ipynb 5
1818
def _cat_slug(fmdict):
1919
"Get the partial slug from the category front matter."
20-
slug = '/'.join(sorted(fmdict.get('categories', '')))
20+
slug = '/'.join(fmdict.get('categories', ''))
2121
return '/' + slug if slug else ''
2222

2323
# %% ../nbs/api/migrate.ipynb 7

nbdev/processors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ def end(self):
211211
mimetype = 'application/vnd.jupyter.widget-state+json'
212212
old = nested_idx(self.nb.metadata, 'widgets', mimetype) or {'state': {}}
213213
new = Widget.get_manager_state(drop_defaults=True)
214-
widgets = {**old, **new, 'state': {**old['state'], **new['state']}}
214+
widgets = {**old, **new, 'state': {**old.get('state', {}), **new['state']}}
215215
self.nb.metadata['widgets'] = {mimetype: widgets}
216216

217217
# %% ../nbs/api/processors.ipynb 42

nbdev/quarto.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ def _sprun(cmd):
3030

3131
def _install_linux():
3232
system(f'curl -LO {BASE_QUARTO_URL}quarto-linux-amd64.deb')
33-
system('sudo dpkg -i *64.deb && rm *64.deb')
33+
system('sudo dpkg -i quarto-linux-amd64.deb && rm quarto-linux-amd64.deb')
3434

3535
def _install_mac():
3636
system(f'curl -LO {BASE_QUARTO_URL}quarto-macos.pkg')
37-
system('sudo installer -pkg quarto-macos.pkg -target /')
37+
system('sudo installer -pkg quarto-macos.pkg -target / && rm quarto-macos.pkg')
3838

3939
@call_parse
4040
def install_quarto():
@@ -104,7 +104,7 @@ def _f(a,b): return Path(a),b
104104

105105
yml_path = path/'sidebar.yml'
106106
yml = "website:\n sidebar:\n contents:\n"
107-
yml += '\n'.join(f' {o}' for o in res)
107+
yml += '\n'.join(f' {o}' for o in res)+'\n'
108108
if printit: return print(yml)
109109
yml_path.write_text(yml)
110110

nbdev/release.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
# %% auto 0
44
__all__ = ['GH_HOST', 'Release', 'changelog', 'release_git', 'release_gh', 'pypi_json', 'latest_pypi', 'pypi_details',
5-
'conda_output_path', 'write_conda_meta', 'anaconda_upload', 'release_conda', 'chk_conda_rel', 'release_pypi',
6-
'release_both', 'bump_version', 'nbdev_bump_version']
5+
'conda_output_path', 'write_conda_meta', 'write_requirements', 'anaconda_upload', 'release_conda',
6+
'chk_conda_rel', 'release_pypi', 'release_both', 'bump_version', 'nbdev_bump_version']
77

88
# %% ../nbs/api/release.ipynb 14
99
from fastcore.all import *
@@ -178,9 +178,9 @@ def _run(cmd):
178178
return res
179179

180180
# %% ../nbs/api/release.ipynb 38
181-
def conda_output_path(name):
181+
def conda_output_path(name, build='build'):
182182
"Output path for conda build"
183-
return run(f'conda build --output {name}').strip().replace('\\', '/')
183+
return run(f'conda {build} --output {name}').strip().replace('\\', '/')
184184

185185
# %% ../nbs/api/release.ipynb 39
186186
def _write_yaml(path, name, d1, d2):
@@ -198,6 +198,9 @@ def _get_conda_meta():
198198
name,ver = cfg.lib_name,cfg.version
199199
url = cfg.doc_host or cfg.git_url
200200

201+
doc_url = (cfg.doc_host + cfg.doc_baseurl) if (cfg.doc_host and cfg.doc_baseurl) else url
202+
dev_url = cfg.git_url if cfg.git_url else url
203+
201204
reqs = ['pip', 'python', 'packaging']
202205
if cfg.get('requirements'): reqs += cfg.requirements.split()
203206
if cfg.get('conda_requirements'): reqs += cfg.conda_requirements.split()
@@ -222,7 +225,7 @@ def _get_conda_meta():
222225
'about': {
223226
'license': 'Apache Software',
224227
'license_family': 'APACHE',
225-
'home': url, 'doc_url': url, 'dev_url': url,
228+
'home': dev_url, 'doc_url': doc_url, 'dev_url': dev_url,
226229
'summary': cfg.get('description'),
227230
'description': descr
228231
},
@@ -236,6 +239,15 @@ def write_conda_meta(path='conda'):
236239
_write_yaml(path, *_get_conda_meta())
237240

238241
# %% ../nbs/api/release.ipynb 43
242+
# This function is used as a utility for creating HF spaces.
243+
def write_requirements(directory=None):
244+
"Writes a `requirements.txt` file to `directory` based on settings.ini."
245+
cfg = get_config()
246+
d = Path(directory) if directory else cfg.config_path
247+
req = '\n'.join([cfg.get(k, '').replace(' ', '\n') for k in ['requirements', 'pip_requirements']])
248+
(d/'requirements.txt').mk_write(req)
249+
250+
# %% ../nbs/api/release.ipynb 45
239251
def anaconda_upload(name, loc=None, user=None, token=None, env_token=None):
240252
"Upload `name` to anaconda"
241253
user = f'-u {user} ' if user else ''
@@ -245,7 +257,7 @@ def anaconda_upload(name, loc=None, user=None, token=None, env_token=None):
245257
if not loc: raise Exception("Failed to find output")
246258
return _run(f'anaconda {token} upload {user} {loc} --skip-existing')
247259

248-
# %% ../nbs/api/release.ipynb 44
260+
# %% ../nbs/api/release.ipynb 47
249261
@call_parse
250262
def release_conda(
251263
path:str='conda', # Path where package will be created
@@ -260,20 +272,22 @@ def release_conda(
260272
write_conda_meta(path)
261273
out = f"Done. Next steps:\n```\ncd {path}\n"""
262274
os.chdir(path)
263-
loc = conda_output_path(name)
264-
out_upl = f"anaconda upload {loc}"
265275
build = 'mambabuild' if mambabuild else 'build'
266-
if not do_build: return print(f"{out}conda {build} .\n{out_upl}\n```")
267-
268-
print(f"conda {build} --no-anaconda-upload {build_args} {name}")
269-
res = _run(f"conda {build} --no-anaconda-upload {build_args} {name}")
270-
if skip_upload: return
276+
if not do_build: return print(f"{out}conda {build} {name}")
277+
278+
cmd = f"conda {build} --output-folder out --no-anaconda-upload {build_args} {name}"
279+
print(cmd)
280+
res = _run(cmd)
281+
outs = globtastic('out', file_glob='*.tar.bz2')
282+
assert len(outs)==1
283+
loc = outs[0]
284+
if skip_upload: return print(loc)
271285
if not upload_user: upload_user = get_config().conda_user
272286
if not upload_user: return print("`conda_user` not in settings.ini and no `upload_user` passed. Cannot upload")
273287
if 'anaconda upload' not in res: return print(f"{res}\n\Failed. Check auto-upload not set in .condarc. Try `--do_build False`.")
274288
return anaconda_upload(name, loc)
275289

276-
# %% ../nbs/api/release.ipynb 45
290+
# %% ../nbs/api/release.ipynb 48
277291
def chk_conda_rel(
278292
nm:str, # Package name on pypi
279293
apkg:str=None, # Anaconda Package (defaults to {nm})
@@ -287,7 +301,7 @@ def chk_conda_rel(
287301
pypitag = latest_pypi(nm)
288302
if force or not condatag or pypitag > max(condatag): return f'{pypitag}'
289303

290-
# %% ../nbs/api/release.ipynb 47
304+
# %% ../nbs/api/release.ipynb 50
291305
@call_parse
292306
def release_pypi(
293307
repository:str="pypi" # Respository to upload to (defined in ~/.pypirc)
@@ -297,7 +311,7 @@ def release_pypi(
297311
system(f'cd {_dir} && rm -rf dist && python setup.py sdist bdist_wheel')
298312
system(f'twine upload --repository {repository} {_dir}/dist/*')
299313

300-
# %% ../nbs/api/release.ipynb 48
314+
# %% ../nbs/api/release.ipynb 51
301315
@call_parse
302316
def release_both(
303317
path:str='conda', # Path where package will be created
@@ -313,15 +327,15 @@ def release_both(
313327
release_conda.__wrapped__(path, do_build=do_build, build_args=build_args, skip_upload=skip_upload, mambabuild=mambabuild, upload_user=upload_user)
314328
nbdev_bump_version.__wrapped__()
315329

316-
# %% ../nbs/api/release.ipynb 50
330+
# %% ../nbs/api/release.ipynb 53
317331
def bump_version(version, part=2, unbump=False):
318332
version = version.split('.')
319333
incr = -1 if unbump else 1
320334
version[part] = str(int(version[part]) + incr)
321335
for i in range(part+1, 3): version[i] = '0'
322336
return '.'.join(version)
323337

324-
# %% ../nbs/api/release.ipynb 51
338+
# %% ../nbs/api/release.ipynb 54
325339
@call_parse
326340
def nbdev_bump_version(
327341
part:int=2, # Part of version to bump

nbs/api/doclinks.ipynb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@
137137
" cells = p.read_text().split(\"\\n# %% \")\n",
138138
" for cell in cells[1:]:\n",
139139
" top,code = cell.split('\\n', 1)\n",
140-
" nb,idx = top.split()\n",
140+
" try: nb,idx = top.split()\n",
141+
" except ValueError: raise ValueError(f\"Unexpected format in '{p}' at cell:\\n```\\n# %% {cell.strip()}.\\n```\\n\"\n",
142+
" \"The expected format is: '# %% {nb_path} {cell_idx}'.\")\n",
141143
" nb_path = None if nb=='auto' else (p.parent/nb).resolve() # NB paths are stored relative to .py file\n",
142144
" if code.endswith('\\n'): code=code[:-1]\n",
143145
" yield AttrDict(nb=nb, idx=int(idx), code=code, nb_path=nb_path, py_path=p.resolve())"
@@ -162,7 +164,7 @@
162164
"outputs": [],
163165
"source": [
164166
"#|export\n",
165-
"def _nbpath2html(p): return p.with_name(re.sub(r'\\d+[a-zA-Z0-9]*_', '', p.name.lower())).with_suffix('.html')"
167+
"def _nbpath2html(p): return p.with_name(re.sub(r'^\\d+[a-zA-Z0-9]*_', '', p.name.lower())).with_suffix('.html')"
166168
]
167169
},
168170
{
@@ -237,7 +239,7 @@
237239
" code_root = dest.parent.resolve()\n",
238240
" for file in globtastic(dest, file_glob=\"*.py\", skip_file_re='^_', skip_folder_re=\"\\.ipynb_checkpoints\"):\n",
239241
" res['syms'].update(_get_modidx((dest.parent/file).resolve(), code_root, nbs_path=nbs_path))\n",
240-
" idxfile.write_text(\"# Autogenerated by nbdev\\n\\nd = \"+pformat(res, width=140, indent=2, compact=True))"
242+
" idxfile.write_text(\"# Autogenerated by nbdev\\n\\nd = \"+pformat(res, width=140, indent=2, compact=True)+'\\n')"
241243
]
242244
},
243245
{

0 commit comments

Comments
 (0)