Skip to content

Commit fd9103c

Browse files
committed
moved sorting call outside of nbglob
1 parent 42e29d1 commit fd9103c

File tree

2 files changed

+62
-44
lines changed

2 files changed

+62
-44
lines changed

nbdev/doclinks.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,16 @@ def _build_modidx(dest=None, nbs_path=None, skip_exists=False):
100100

101101
# %% ../nbs/api/doclinks.ipynb 20
102102
@delegates(globtastic)
103-
def nbglob(path=None, skip_folder_re = '^[_.]', file_glob='*.ipynb', skip_file_re='^[_.]', key='nbs_path', as_path=False, sort_by=None, **kwargs):
103+
def nbglob(path=None, skip_folder_re = '^[_.]', file_glob='*.ipynb', skip_file_re='^[_.]', key='nbs_path', as_path=False, **kwargs):
104104
"Find all files in a directory matching an extension given a config key."
105105
path = Path(path or get_config()[key])
106106
recursive=get_config().recursive
107107
res = globtastic(path, file_glob=file_glob, skip_folder_re=skip_folder_re,
108108
skip_file_re=skip_file_re, recursive=recursive, **kwargs)
109109
res = res.map(Path) if as_path else res
110-
if sort_by is not None:
111-
res.sort(key=sort_by)
112110
return res
113111

114-
# %% ../nbs/api/doclinks.ipynb 23
112+
# %% ../nbs/api/doclinks.ipynb 22
115113
def nbglob_cli(
116114
path:str=None, # Path to notebooks
117115
symlinks:bool=False, # Follow symlinks?
@@ -125,24 +123,24 @@ def nbglob_cli(
125123
return nbglob(path, symlinks=symlinks, file_glob=file_glob, file_re=file_re, folder_re=folder_re,
126124
skip_file_glob=skip_file_glob, skip_file_re=skip_file_re, skip_folder_re=skip_folder_re)
127125

128-
# %% ../nbs/api/doclinks.ipynb 24
126+
# %% ../nbs/api/doclinks.ipynb 23
129127
@call_parse
130128
@delegates(nbglob_cli)
131129
def nbdev_export(
132130
path:str=None, # Path or filename
133131
**kwargs):
134132
"Export notebooks in `path` to Python modules"
135133
if os.environ.get('IN_TEST',0): return
136-
files = nbglob(path=path, sort_by=lambda path_str: Path(path_str).name, **kwargs)
134+
files = nbglob(path=path, **kwargs)#.sorted(lambda path_str: Path(path_str).name)
137135
for f in files: nb_export(f)
138136
add_init(get_config().lib_path)
139137
_build_modidx()
140138

141-
# %% ../nbs/api/doclinks.ipynb 26
139+
# %% ../nbs/api/doclinks.ipynb 27
142140
import importlib,ast
143141
from functools import lru_cache
144142

145-
# %% ../nbs/api/doclinks.ipynb 27
143+
# %% ../nbs/api/doclinks.ipynb 28
146144
def _find_mod(mod):
147145
mp,_,mr = mod.partition('/')
148146
spec = importlib.util.find_spec(mp)
@@ -165,7 +163,7 @@ def _get_exps(mod):
165163

166164
def _lineno(sym, fname): return _get_exps(fname).get(sym, None) if fname else None
167165

168-
# %% ../nbs/api/doclinks.ipynb 29
166+
# %% ../nbs/api/doclinks.ipynb 30
169167
def _qual_sym(s, settings):
170168
if not isinstance(s,tuple): return s
171169
nb,py = s
@@ -180,10 +178,10 @@ def _qual_syms(entries):
180178
if 'doc_host' not in settings: return entries
181179
return {'syms': {mod:_qual_mod(d, settings) for mod,d in entries['syms'].items()}, 'settings':settings}
182180

183-
# %% ../nbs/api/doclinks.ipynb 30
181+
# %% ../nbs/api/doclinks.ipynb 31
184182
_re_backticks = re.compile(r'`([^`\s]+)`')
185183

186-
# %% ../nbs/api/doclinks.ipynb 31
184+
# %% ../nbs/api/doclinks.ipynb 32
187185
@lru_cache(None)
188186
class NbdevLookup:
189187
"Mapping from symbol names to docs and source URLs"

nbs/api/doclinks.ipynb

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -306,41 +306,16 @@
306306
"source": [
307307
"#|export\n",
308308
"@delegates(globtastic)\n",
309-
"def nbglob(path=None, skip_folder_re = '^[_.]', file_glob='*.ipynb', skip_file_re='^[_.]', key='nbs_path', as_path=False, sort_by=None, **kwargs):\n",
309+
"def nbglob(path=None, skip_folder_re = '^[_.]', file_glob='*.ipynb', skip_file_re='^[_.]', key='nbs_path', as_path=False, **kwargs):\n",
310310
" \"Find all files in a directory matching an extension given a config key.\"\n",
311311
" path = Path(path or get_config()[key])\n",
312312
" recursive=get_config().recursive\n",
313313
" res = globtastic(path, file_glob=file_glob, skip_folder_re=skip_folder_re,\n",
314314
" skip_file_re=skip_file_re, recursive=recursive, **kwargs)\n",
315315
" res = res.map(Path) if as_path else res\n",
316-
" if sort_by is not None:\n",
317-
" res.sort(key=sort_by)\n",
318316
" return res"
319317
]
320318
},
321-
{
322-
"cell_type": "markdown",
323-
"metadata": {},
324-
"source": [
325-
"`globtastic` uses `glob.glob`, which in turn uses `os.listdir` under the hood. As the [documentation](https://docs.python.org/3/library/os.html#os.listdir) states, the order of results returned by `os.listdir` is arbitrary, hence we need to be able to sort the notebook-paths when needed:"
326-
]
327-
},
328-
{
329-
"cell_type": "code",
330-
"execution_count": null,
331-
"metadata": {},
332-
"outputs": [],
333-
"source": [
334-
"import copy\n",
335-
"\n",
336-
"globtastic_save = copy.copy(globtastic)\n",
337-
"\n",
338-
"globtastic = lambda *args, **kwargs: ['../../8.ipynb', 'api/3.ipynb', '4.ipynb']\n",
339-
"assert nbglob(sort_by=lambda path_str: Path(path_str).name) == ['api/3.ipynb', '4.ipynb', '../../8.ipynb']\n",
340-
"\n",
341-
"globtastic = globtastic_save"
342-
]
343-
},
344319
{
345320
"cell_type": "code",
346321
"execution_count": null,
@@ -376,12 +351,57 @@
376351
" **kwargs):\n",
377352
" \"Export notebooks in `path` to Python modules\"\n",
378353
" if os.environ.get('IN_TEST',0): return\n",
379-
" files = nbglob(path=path, sort_by=lambda path_str: Path(path_str).name, **kwargs)\n",
354+
" files = sorted(nbglob(path=path, **kwargs), key=lambda path_str: Path(path_str).name)\n",
380355
" for f in files: nb_export(f)\n",
381356
" add_init(get_config().lib_path)\n",
382357
" _build_modidx()"
383358
]
384359
},
360+
{
361+
"cell_type": "markdown",
362+
"metadata": {},
363+
"source": [
364+
"When exporting the notebooks, they should be exported in order of the filename to ensure deterministic build behaviour. The below test patches `nbglob` to return a non-ordered list, then checks that `nb_export` is still called on the notebooks in the right order:"
365+
]
366+
},
367+
{
368+
"cell_type": "code",
369+
"execution_count": null,
370+
"metadata": {},
371+
"outputs": [],
372+
"source": [
373+
"from copy import copy\n",
374+
"\n",
375+
"\n",
376+
"class EscapeException(Exception):\n",
377+
" pass\n",
378+
"\n",
379+
"def nb_export_mock(nb_path: str):\n",
380+
" assert nb_path==expected_order.pop()\n",
381+
" \n",
382+
" # escape from the outer function as the tests are done\n",
383+
" if not expected_order:\n",
384+
" raise EscapeException()\n",
385+
" \n",
386+
"\n",
387+
"nbglob_save = copy(nbglob)\n",
388+
"nb_export_save = copy(nb_export)\n",
389+
"\n",
390+
"nbglob = lambda *args, **kwargs: ['../../8.ipynb', 'api/3.ipynb', '4.ipynb']\n",
391+
"\n",
392+
"expected_order = ['api/3.ipynb', '4.ipynb', '../../8.ipynb']\n",
393+
"expected_order.reverse()\n",
394+
"nb_export = nb_export_mock\n",
395+
"\n",
396+
"try:\n",
397+
" nbdev_export()\n",
398+
"except EscapeException:\n",
399+
" pass # this silences the exception used to end nbdev_export early\n",
400+
"\n",
401+
"nbglob = nbglob_save\n",
402+
"nb_export = nb_export_save"
403+
]
404+
},
385405
{
386406
"cell_type": "markdown",
387407
"metadata": {},
@@ -578,7 +598,7 @@
578598
"text/markdown": [
579599
"---\n",
580600
"\n",
581-
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L209){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
601+
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L211){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
582602
"\n",
583603
"### NbdevLookup.doc\n",
584604
"\n",
@@ -589,7 +609,7 @@
589609
"text/plain": [
590610
"---\n",
591611
"\n",
592-
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L209){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
612+
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L211){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
593613
"\n",
594614
"### NbdevLookup.doc\n",
595615
"\n",
@@ -672,7 +692,7 @@
672692
"text/markdown": [
673693
"---\n",
674694
"\n",
675-
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L214){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
695+
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L216){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
676696
"\n",
677697
"### NbdevLookup.code\n",
678698
"\n",
@@ -683,7 +703,7 @@
683703
"text/plain": [
684704
"---\n",
685705
"\n",
686-
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L214){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
706+
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L216){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
687707
"\n",
688708
"### NbdevLookup.code\n",
689709
"\n",
@@ -731,7 +751,7 @@
731751
"text/markdown": [
732752
"---\n",
733753
"\n",
734-
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L231){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
754+
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L233){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
735755
"\n",
736756
"### NbdevLookup.linkify\n",
737757
"\n",
@@ -740,7 +760,7 @@
740760
"text/plain": [
741761
"---\n",
742762
"\n",
743-
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L231){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
763+
"[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L233){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
744764
"\n",
745765
"### NbdevLookup.linkify\n",
746766
"\n",

0 commit comments

Comments
 (0)