diff --git a/nbdev/_modidx.py b/nbdev/_modidx.py index f7001f25..b3b116dc 100644 --- a/nbdev/_modidx.py +++ b/nbdev/_modidx.py @@ -58,6 +58,7 @@ 'nbdev.doclinks.NbdevLookup.link_line': ('api/doclinks.html#nbdevlookup.link_line', 'nbdev/doclinks.py'), 'nbdev.doclinks.NbdevLookup.linkify': ('api/doclinks.html#nbdevlookup.linkify', 'nbdev/doclinks.py'), 'nbdev.doclinks._binop_leafs': ('api/doclinks.html#_binop_leafs', 'nbdev/doclinks.py'), + 'nbdev.doclinks._build_lookup_table': ('api/doclinks.html#_build_lookup_table', 'nbdev/doclinks.py'), 'nbdev.doclinks._build_modidx': ('api/doclinks.html#_build_modidx', 'nbdev/doclinks.py'), 'nbdev.doclinks._find_mod': ('api/doclinks.html#_find_mod', 'nbdev/doclinks.py'), 'nbdev.doclinks._get_exps': ('api/doclinks.html#_get_exps', 'nbdev/doclinks.py'), diff --git a/nbdev/doclinks.py b/nbdev/doclinks.py index 3a72c581..9e0e0dcf 100644 --- a/nbdev/doclinks.py +++ b/nbdev/doclinks.py @@ -200,29 +200,33 @@ def _qual_syms(entries): # %% ../nbs/api/05_doclinks.ipynb _re_backticks = re.compile(r'`([^`\s]+?)(?:\(\))?`') +# %% ../nbs/api/05_doclinks.ipynb +@lru_cache(None) +def _build_lookup_table(strip_libs=None, incl_libs=None, skip_mods=None): + cfg = get_config() + if strip_libs is None: + try: strip_libs = cfg.get('strip_libs', cfg.get('lib_path', 'nbdev').name).split() + except FileNotFoundError: strip_libs = 'nbdev' + skip_mods = setify(skip_mods) + strip_libs = L(strip_libs) + if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique() + entries = {o.name: _qual_syms(o.resolve()) for o in list(pkg_resources.iter_entry_points(group='nbdev')) + if incl_libs is None or o.dist.key in incl_libs} + py_syms = merge(*L(o['syms'].values() for o in entries.values()).concat()) + for m in strip_libs: + if m in entries: + _d = entries[m] + stripped = {remove_prefix(k,f"{mod}."):v + for mod,dets in _d['syms'].items() if mod not in skip_mods + for k,v in dets.items()} + py_syms = merge(stripped, py_syms) + return entries,py_syms + # %% ../nbs/api/05_doclinks.ipynb class NbdevLookup: "Mapping from symbol names to docs and source URLs" def __init__(self, strip_libs=None, incl_libs=None, skip_mods=None, ns=None): - cfg = get_config() - if strip_libs is None: - try: strip_libs = cfg.get('strip_libs', cfg.get('lib_path', 'nbdev').name).split() - except FileNotFoundError: strip_libs = 'nbdev' - skip_mods = setify(skip_mods) - strip_libs = L(strip_libs) - if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique() - # Dict from lib name to _nbdev module for incl_libs (defaults to all) - self.entries = {o.name: _qual_syms(o.resolve()) for o in list(pkg_resources.iter_entry_points(group='nbdev')) - if incl_libs is None or o.dist.key in incl_libs} - py_syms = merge(*L(o['syms'].values() for o in self.entries.values()).concat()) - for m in strip_libs: - if m in self.entries: - _d = self.entries[m] - stripped = {remove_prefix(k,f"{mod}."):v - for mod,dets in _d['syms'].items() if mod not in skip_mods - for k,v in dets.items()} - py_syms = merge(stripped, py_syms) - self.syms = py_syms + self.entries,self.syms = _build_lookup_table(strip_libs, incl_libs, skip_mods) self.aliases = {n:o.__name__ for n,o in (ns or {}).items() if isinstance(o, ModuleType)} def __getitem__(self, s): diff --git a/nbs/api/05_doclinks.ipynb b/nbs/api/05_doclinks.ipynb index 9f34cc3c..b9479c5e 100644 --- a/nbs/api/05_doclinks.ipynb +++ b/nbs/api/05_doclinks.ipynb @@ -431,6 +431,28 @@ "def _lineno(sym, fname): return _get_exps(fname).get(sym, None) if fname else None" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#|hide\n", + "_get_exps.cache_clear()\n", + "\n", + "# Test _get_exps caching\n", + "initial = _get_exps.cache_info()\n", + "_ = _get_exps('nbdev/maker.py') # First call should miss\n", + "after_first = _get_exps.cache_info()\n", + "_ = _get_exps('nbdev/maker.py') # Second call should hit\n", + "after_second = _get_exps.cache_info()\n", + "\n", + "test_eq(after_first.misses, initial.misses + 1)\n", + "test_eq(after_first.hits, initial.hits)\n", + "test_eq(after_second.hits, after_first.hits + 1)\n", + "test_eq(after_second.misses, after_first.misses)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -496,6 +518,35 @@ "test_eq(_re_backticks.findall('consecutive `foo``bar``baz`'), ['foo', 'bar', 'baz'])" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "@lru_cache(None)\n", + "def _build_lookup_table(strip_libs=None, incl_libs=None, skip_mods=None):\n", + " cfg = get_config()\n", + " if strip_libs is None:\n", + " try: strip_libs = cfg.get('strip_libs', cfg.get('lib_path', 'nbdev').name).split()\n", + " except FileNotFoundError: strip_libs = 'nbdev'\n", + " skip_mods = setify(skip_mods)\n", + " strip_libs = L(strip_libs)\n", + " if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique()\n", + " entries = {o.name: _qual_syms(o.resolve()) for o in list(pkg_resources.iter_entry_points(group='nbdev'))\n", + " if incl_libs is None or o.dist.key in incl_libs}\n", + " py_syms = merge(*L(o['syms'].values() for o in entries.values()).concat())\n", + " for m in strip_libs:\n", + " if m in entries:\n", + " _d = entries[m]\n", + " stripped = {remove_prefix(k,f\"{mod}.\"):v\n", + " for mod,dets in _d['syms'].items() if mod not in skip_mods\n", + " for k,v in dets.items()}\n", + " py_syms = merge(stripped, py_syms)\n", + " return entries,py_syms" + ] + }, { "cell_type": "code", "execution_count": null, @@ -506,25 +557,7 @@ "class NbdevLookup:\n", " \"Mapping from symbol names to docs and source URLs\"\n", " def __init__(self, strip_libs=None, incl_libs=None, skip_mods=None, ns=None):\n", - " cfg = get_config()\n", - " if strip_libs is None:\n", - " try: strip_libs = cfg.get('strip_libs', cfg.get('lib_path', 'nbdev').name).split()\n", - " except FileNotFoundError: strip_libs = 'nbdev'\n", - " skip_mods = setify(skip_mods)\n", - " strip_libs = L(strip_libs)\n", - " if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique()\n", - " # Dict from lib name to _nbdev module for incl_libs (defaults to all)\n", - " self.entries = {o.name: _qual_syms(o.resolve()) for o in list(pkg_resources.iter_entry_points(group='nbdev'))\n", - " if incl_libs is None or o.dist.key in incl_libs}\n", - " py_syms = merge(*L(o['syms'].values() for o in self.entries.values()).concat())\n", - " for m in strip_libs:\n", - " if m in self.entries:\n", - " _d = self.entries[m]\n", - " stripped = {remove_prefix(k,f\"{mod}.\"):v\n", - " for mod,dets in _d['syms'].items() if mod not in skip_mods\n", - " for k,v in dets.items()}\n", - " py_syms = merge(stripped, py_syms)\n", - " self.syms = py_syms\n", + " self.entries,self.syms = _build_lookup_table(strip_libs, incl_libs, skip_mods)\n", " self.aliases = {n:o.__name__ for n,o in (ns or {}).items() if isinstance(o, ModuleType)}\n", " \n", " def __getitem__(self, s): \n", @@ -566,6 +599,37 @@ " return '\\n'.join(lines)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nathan/miniconda3/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "#|hide\n", + "_build_lookup_table.cache_clear()\n", + "\n", + "# Test _build_lookup_table caching\n", + "initial = _build_lookup_table.cache_info()\n", + "_ = _build_lookup_table() # First call should miss\n", + "after_first = _build_lookup_table.cache_info()\n", + "_ = _build_lookup_table() # Second call should hit\n", + "after_second = _build_lookup_table.cache_info()\n", + "\n", + "test_eq(after_first.misses, initial.misses + 1)\n", + "test_eq(after_first.hits, initial.hits)\n", + "test_eq(after_second.hits, after_first.hits + 1)\n", + "test_eq(after_second.misses, after_first.misses)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -578,14 +642,6 @@ "execution_count": null, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/nathan/miniconda3/envs/main/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - }, { "data": { "text/plain": [ @@ -614,7 +670,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L227){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L234){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### NbdevLookup.doc\n", "\n", @@ -625,7 +681,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L227){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L234){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### NbdevLookup.doc\n", "\n", @@ -709,7 +765,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L232){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L239){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### NbdevLookup.code\n", "\n", @@ -720,7 +776,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L232){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L239){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### NbdevLookup.code\n", "\n", @@ -768,7 +824,7 @@ "text/markdown": [ "---\n", "\n", - "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L250){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L257){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### NbdevLookup.linkify\n", "\n", @@ -777,7 +833,7 @@ "text/plain": [ "---\n", "\n", - "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L250){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", + "[source](https://github.com/fastai/nbdev/blob/master/nbdev/doclinks.py#L257){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", "\n", "### NbdevLookup.linkify\n", "\n", @@ -899,6 +955,13 @@ "from nbdev._modidx import d\n", "assert d['syms']['nbdev.doclinks']['nbdev.doclinks.NbdevLookup'][0]" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": {