diff --git a/nbdev/doclinks.py b/nbdev/doclinks.py index 8efbb40c..90648a25 100644 --- a/nbdev/doclinks.py +++ b/nbdev/doclinks.py @@ -17,7 +17,8 @@ from fastcore.net import urlread import ast,builtins,contextlib -import pkg_resources,importlib +import importlib.metadata +import importlib.util from astunparse import unparse from io import BytesIO @@ -236,9 +237,10 @@ def _build_lookup_table(strip_libs=None, incl_libs=None, skip_mods=None): strip_libs = L(strip_libs) if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique() entries = {} - for o in pkg_resources.iter_entry_points(group='nbdev'): - if incl_libs is not None and o.dist.key not in incl_libs: continue - try: entries[o.name] = _qual_syms(o.resolve()) + eps = importlib.metadata.entry_points() + for o in eps.select(group='nbdev') if hasattr(eps, 'select') else eps.get('nbdev', []): + if incl_libs is not None and o.dist.name not in incl_libs: continue + try: entries[o.name] = _qual_syms(o.load()) except Exception: pass py_syms = merge(*L(o['syms'].values() for o in entries.values()).concat()) for m in strip_libs: diff --git a/nbs/api/05_doclinks.ipynb b/nbs/api/05_doclinks.ipynb index 4b4097ed..97064611 100644 --- a/nbs/api/05_doclinks.ipynb +++ b/nbs/api/05_doclinks.ipynb @@ -22,7 +22,20 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "expected 'except' or 'finally' block (doclinks.py, line 244)", + "output_type": "error", + "traceback": [ + "Traceback \u001b[36m(most recent call last)\u001b[39m:\n", + " File \u001b[92m~/git/nbdev/.venv/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3672\u001b[39m in \u001b[95mrun_code\u001b[39m\n exec(code_obj, self.user_global_ns, self.user_ns)\n", + " Cell \u001b[92mIn[2]\u001b[39m\u001b[92m, line 2\u001b[39m\n from nbdev.config import *\n", + "\u001b[36m \u001b[39m\u001b[36mFile \u001b[39m\u001b[32m~/git/nbdev/nbdev/__init__.py:3\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mfrom .doclinks import nbdev_export\u001b[39m\n", + " \u001b[36mFile \u001b[39m\u001b[32m~/git/nbdev/nbdev/doclinks.py:244\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mpy_syms = merge(*L(o['syms'].values() for o in entries.values()).concat())\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m expected 'except' or 'finally' block\n" + ] + } + ], "source": [ "#|export\n", "from nbdev.config import *\n", @@ -36,7 +49,8 @@ "from fastcore.net import urlread\n", "\n", "import ast,builtins,contextlib\n", - "import pkg_resources,importlib\n", + "import importlib.metadata\n", + "import importlib.util\n", "\n", "from astunparse import unparse\n", "from io import BytesIO\n", @@ -51,7 +65,20 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "expected 'except' or 'finally' block (doclinks.py, line 244)", + "output_type": "error", + "traceback": [ + "Traceback \u001b[36m(most recent call last)\u001b[39m:\n", + " File \u001b[92m~/git/nbdev/.venv/lib/python3.12/site-packages/IPython/core/interactiveshell.py:3672\u001b[39m in \u001b[95mrun_code\u001b[39m\n exec(code_obj, self.user_global_ns, self.user_ns)\n", + " Cell \u001b[92mIn[3]\u001b[39m\u001b[92m, line 7\u001b[39m\n from nbdev.showdoc import show_doc\n", + "\u001b[36m \u001b[39m\u001b[36mFile \u001b[39m\u001b[32m~/git/nbdev/nbdev/__init__.py:3\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mfrom .doclinks import nbdev_export\u001b[39m\n", + " \u001b[36mFile \u001b[39m\u001b[32m~/git/nbdev/nbdev/doclinks.py:244\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mpy_syms = merge(*L(o['syms'].values() for o in entries.values()).concat())\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m expected 'except' or 'finally' block\n" + ] + } + ], "source": [ "#|hide\n", "from IPython.display import Markdown,display\n", @@ -619,9 +646,10 @@ " strip_libs = L(strip_libs)\n", " if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique()\n", " entries = {}\n", - " for o in pkg_resources.iter_entry_points(group='nbdev'):\n", - " if incl_libs is not None and o.dist.key not in incl_libs: continue\n", - " try: entries[o.name] = _qual_syms(o.resolve())\n", + " eps = importlib.metadata.entry_points()\n", + " for o in eps.select(group='nbdev') if hasattr(eps, 'select') else eps.get('nbdev', []):\n", + " if incl_libs is not None and o.dist.name not in incl_libs: continue\n", + " try: entries[o.name] = _qual_syms(o.load())\n", " except Exception: pass\n", " py_syms = merge(*L(o['syms'].values() for o in entries.values()).concat())\n", " for m in strip_libs:\n", @@ -675,13 +703,13 @@ "# Create mock entry points\n", "class BadEntryPoint:\n", " name = 'bad_entry'\n", - " dist = type('Dist', (), {'key': 'bad_lib'})()\n", - " def resolve(self): raise AttributeError(\"Simulated error\")\n", + " dist = type('Dist', (), {'name': 'bad_lib'})()\n", + " def load(self): raise AttributeError(\"Simulated error\")\n", "\n", "class GoodEntryPoint:\n", " name = 'good_entry'\n", - " dist = type('Dist', (), {'key': 'good_lib'})()\n", - " def resolve(self): return {'syms': {}, 'settings': {}}" + " dist = type('Dist', (), {'name': 'good_lib'})()\n", + " def load(self): return {'syms': {}, 'settings': {}}" ] }, { @@ -704,8 +732,22 @@ "# Clear the cache before testing\n", "_build_lookup_table.cache_clear()\n", "\n", - "# Patch iter_entry_points\n", - "with xpatch('pkg_resources.iter_entry_points', return_value=[BadEntryPoint(), GoodEntryPoint()]):\n", + "# Create a mock entry points object that supports both access patterns\n", + "class MockEntryPoints:\n", + " def __init__(self, entries):\n", + " self.entries = entries\n", + " \n", + " def select(self, group):\n", + " return self.entries if group == 'nbdev' else []\n", + " \n", + " def get(self, group, default=None):\n", + " return self.entries if group == 'nbdev' else (default or [])\n", + "\n", + "# Create mock entry points\n", + "mock_eps = MockEntryPoints([BadEntryPoint(), GoodEntryPoint()])\n", + "\n", + "# Patch importlib.metadata.entry_points\n", + "with xpatch('importlib.metadata.entry_points', return_value=mock_eps):\n", " entries, py_syms = _build_lookup_table()\n", " \n", " # Should only contain the good entry\n",