diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 719417315..cd43a1667 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -12,3 +12,9 @@ name: nbdev_export entry: nbdev_export description: "Export notebooks to modules and build modidx" + +- <<: *hook + id: nbdev_docs + name: nbdev_docs + entry: nbdev_docs + description: "Create Quarto docs and README.md" diff --git a/nbdev/_modidx.py b/nbdev/_modidx.py index 632ee6af8..19ecd46a6 100644 --- a/nbdev/_modidx.py +++ b/nbdev/_modidx.py @@ -82,7 +82,9 @@ 'nbdev.doclinks.nbdev_export': ('api/doclinks.html#nbdev_export', 'nbdev/doclinks.py'), 'nbdev.doclinks.nbglob': ('api/doclinks.html#nbglob', 'nbdev/doclinks.py'), 'nbdev.doclinks.nbglob_cli': ('api/doclinks.html#nbglob_cli', 'nbdev/doclinks.py'), - 'nbdev.doclinks.patch_name': ('api/doclinks.html#patch_name', 'nbdev/doclinks.py')}, + 'nbdev.doclinks.patch_name': ('api/doclinks.html#patch_name', 'nbdev/doclinks.py'), + 'nbdev.doclinks.ruff_fix': ('api/doclinks.html#ruff_fix', 'nbdev/doclinks.py'), + 'nbdev.doclinks.ruff_format': ('api/doclinks.html#ruff_format', 'nbdev/doclinks.py')}, 'nbdev.export': { 'nbdev.export.ExportModuleProc': ('api/export.html#exportmoduleproc', 'nbdev/export.py'), 'nbdev.export.ExportModuleProc.__call__': ('api/export.html#exportmoduleproc.__call__', 'nbdev/export.py'), 'nbdev.export.ExportModuleProc._default_exp_': ( 'api/export.html#exportmoduleproc._default_exp_', diff --git a/nbdev/config.py b/nbdev/config.py index 991557e98..078b6a709 100644 --- a/nbdev/config.py +++ b/nbdev/config.py @@ -54,6 +54,8 @@ def _apply_defaults( language='English', # Language PyPI classifier recursive:bool_arg=True, # Include subfolders in notebook globs? black_formatting:bool_arg=False, # Format libraries with black? + ruff_formatting:bool_arg=False, # Format libraries with ruff format? + ruff_fixing:bool_arg=False, # Fix libraries with ruff check --fix? readme_nb='index.ipynb', # Notebook to export as repo readme title='%(lib_name)s', # Quarto website title allowed_metadata_keys='', # Preserve the list of keys in the main notebook metadata diff --git a/nbdev/doclinks.py b/nbdev/doclinks.py index dc59f67da..7eb09b51a 100644 --- a/nbdev/doclinks.py +++ b/nbdev/doclinks.py @@ -3,7 +3,8 @@ # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/05_doclinks.ipynb. # %% auto 0 -__all__ = ['typs', 'bset', 'patch_name', 'nbglob', 'nbglob_cli', 'nbdev_export', 'create_index', 'NbdevLookup'] +__all__ = ['typs', 'bset', 'patch_name', 'ruff_format', 'ruff_fix', 'nbglob', 'nbglob_cli', 'nbdev_export', 'create_index', + 'NbdevLookup'] # %% ../nbs/api/05_doclinks.ipynb from .config import * @@ -117,6 +118,32 @@ def _build_modidx(dest=None, nbs_path=None, skip_exists=False): except ValueError: pass idxfile.write_text("# Autogenerated by nbdev\n\nd = "+pformat(res, width=140, indent=2, compact=True)+'\n') +# %% ../nbs/api/05_doclinks.ipynb +def ruff_format(): + "Format code with `ruff format` on the entire library" + import subprocess + try: + subprocess.run( + ['ruff', 'format'], + capture_output=True, + check=False + ) + except FileNotFoundError: + raise ImportError("You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev") + +# %% ../nbs/api/05_doclinks.ipynb +def ruff_fix(): + "Lint and auto-fix code with `ruff check --fix` on the entire library" + import subprocess + try: + subprocess.run( + ['ruff', 'check', '--fix'], + capture_output=True, + check=False + ) + except FileNotFoundError: + raise ImportError("You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev") + # %% ../nbs/api/05_doclinks.ipynb @delegates(globtastic) def nbglob(path=None, skip_folder_re = '^[_.]', file_glob='*.ipynb', skip_file_re='^[_.]', key='nbs_path', as_path=False, **kwargs): @@ -156,9 +183,13 @@ def nbdev_export( procs = [getattr(nbdev.export, p) for p in L(procs)] files = nbglob(path=path, as_path=True, **kwargs).sorted('name') for f in files: nb_export(f, procs=procs) - add_init(get_config().lib_path) + cfg = get_config() + add_init(cfg.lib_path) _build_modidx() + if cfg.ruff_formatting: ruff_format() + if cfg.ruff_fixing: ruff_fix() + # %% ../nbs/api/05_doclinks.ipynb typs = 'module','class','method','function' bset = set(dir(builtins)) diff --git a/nbs/api/01_config.ipynb b/nbs/api/01_config.ipynb index 44d740639..7408fca04 100644 --- a/nbs/api/01_config.ipynb +++ b/nbs/api/01_config.ipynb @@ -144,6 +144,8 @@ " language='English', # Language PyPI classifier\n", " recursive:bool_arg=True, # Include subfolders in notebook globs?\n", " black_formatting:bool_arg=False, # Format libraries with black?\n", + " ruff_formatting:bool_arg=False, # Format libraries with ruff format?\n", + " ruff_fixing:bool_arg=False, # Fix libraries with ruff check --fix?\n", " readme_nb='index.ipynb', # Notebook to export as repo readme\n", " title='%(lib_name)s', # Quarto website title\n", " allowed_metadata_keys='', # Preserve the list of keys in the main notebook metadata\n", diff --git a/nbs/api/04_export.ipynb b/nbs/api/04_export.ipynb index 454457bc1..6cbd3520c 100644 --- a/nbs/api/04_export.ipynb +++ b/nbs/api/04_export.ipynb @@ -136,7 +136,7 @@ "source": [ "_cell = read_nb('../../tests/export_procs.ipynb')['cells'][0]\n", "black_format(_cell, force=True)\n", - "test_eq(_cell.source, 'j = [1, 2, 3]')" + "test_eq(_cell.source, 'import ast\\n\\nj = [1, 2, 3]\\nfor i in j:\\n str(1)')" ] }, { diff --git a/nbs/api/05_doclinks.ipynb b/nbs/api/05_doclinks.ipynb index 4017e3a43..d94f1c93f 100644 --- a/nbs/api/05_doclinks.ipynb +++ b/nbs/api/05_doclinks.ipynb @@ -60,12 +60,18 @@ "outputs": [], "source": [ "#| hide\n", + "import shutil\n", + "import tempfile\n", + "import os\n", + "\n", + "from pathlib import Path\n", "from IPython.display import Markdown,display\n", "from unittest.mock import patch as xpatch\n", "from fastcore.test import *\n", "from pdb import set_trace\n", "from importlib import reload\n", - "from nbdev.showdoc import show_doc" + "from nbdev.showdoc import show_doc\n", + "from execnb.nbio import read_nb" ] }, { @@ -284,17 +290,6 @@ "# _build_modidx()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "96bef2c7", - "metadata": {}, - "outputs": [], - "source": [ - "#| hide\n", - "import shutil" - ] - }, { "cell_type": "code", "execution_count": null, @@ -330,7 +325,107 @@ }, { "cell_type": "markdown", - "id": "ad55d279", + "id": "c66f3b37", + "metadata": {}, + "source": [ + "## Ruff" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40a6b9f1", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def ruff_format():\n", + " \"Format code with `ruff format` on the entire library\"\n", + " import subprocess\n", + " try:\n", + " subprocess.run(\n", + " ['ruff', 'format'],\n", + " capture_output=True,\n", + " check=False\n", + " )\n", + " except FileNotFoundError:\n", + " raise ImportError(\"You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b82ba02", + "metadata": {}, + "outputs": [], + "source": [ + "with tempfile.TemporaryDirectory() as tmpdir:\n", + " tmpdir = Path(tmpdir)\n", + " test_nb = Path('../../tests/export_procs.ipynb')\n", + " shutil.copy(test_nb, tmpdir / 'export_procs.ipynb')\n", + "\n", + " old_cwd = os.getcwd()\n", + " try:\n", + " os.chdir(tmpdir)\n", + " ruff_format()\n", + " assert (tmpdir / 'export_procs.ipynb').exists()\n", + " formatted_nb = read_nb(tmpdir / 'export_procs.ipynb')\n", + " formatted_source = formatted_nb['cells'][0]['source']\n", + " finally:\n", + " os.chdir(old_cwd)\n", + "\n", + "test_eq(formatted_source, 'import ast\\n\\nj = [1, 2, 3]\\nfor i in j:\\n str(1)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99e4ac75", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def ruff_fix():\n", + " \"Lint and auto-fix code with `ruff check --fix` on the entire library\"\n", + " import subprocess\n", + " try:\n", + " subprocess.run(\n", + " ['ruff', 'check', '--fix'],\n", + " capture_output=True,\n", + " check=False\n", + " )\n", + " except FileNotFoundError:\n", + " raise ImportError(\"You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41aa4ab7", + "metadata": {}, + "outputs": [], + "source": [ + "with tempfile.TemporaryDirectory() as tmpdir:\n", + " tmpdir = Path(tmpdir)\n", + " test_nb = Path('../../tests/export_procs.ipynb')\n", + " shutil.copy(test_nb, tmpdir / 'export_procs.ipynb')\n", + "\n", + " old_cwd = os.getcwd()\n", + " try:\n", + " os.chdir(tmpdir)\n", + " ruff_fix()\n", + " assert (tmpdir / 'export_procs.ipynb').exists()\n", + " fixed_nb = read_nb(tmpdir / 'export_procs.ipynb')\n", + " fixed_source = fixed_nb['cells'][0]['source']\n", + " finally:\n", + " os.chdir(old_cwd)\n", + "\n", + "test_eq(fixed_source, 'j = [1,\\n 2,\\n 3\\n]\\nfor i in j:\\n str(1)')" + ] + }, + { + "cell_type": "markdown", + "id": "b1205ff6", "metadata": {}, "source": [ "## Export a notebook" @@ -398,8 +493,12 @@ " procs = [getattr(nbdev.export, p) for p in L(procs)]\n", " files = nbglob(path=path, as_path=True, **kwargs).sorted('name')\n", " for f in files: nb_export(f, procs=procs)\n", - " add_init(get_config().lib_path)\n", - " _build_modidx()" + " cfg = get_config()\n", + " add_init(cfg.lib_path)\n", + " _build_modidx()\n", + "\n", + " if cfg.ruff_formatting: ruff_format()\n", + " if cfg.ruff_fixing: ruff_fix()" ] }, { @@ -463,27 +562,7 @@ "execution_count": null, "id": "9ed99190", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'builtins.bool': 'https://docs.python.org/3/library/functions.html#bool',\n", - " 'builtins.bytearray': 'https://docs.python.org/3/library/stdtypes.html#bytearray',\n", - " 'builtins.bytes': 'https://docs.python.org/3/library/stdtypes.html#bytes',\n", - " 'builtins.complex': 'https://docs.python.org/3/library/functions.html#complex',\n", - " 'builtins.dict': 'https://docs.python.org/3/library/stdtypes.html#dict',\n", - " 'builtins.float': 'https://docs.python.org/3/library/functions.html#float',\n", - " 'builtins.frozenset': 'https://docs.python.org/3/library/stdtypes.html#frozenset',\n", - " 'builtins.int': 'https://docs.python.org/3/library/functions.html#int',\n", - " 'builtins.list': 'https://docs.python.org/3/library/stdtypes.html#list',\n", - " 'builtins.memoryview': 'https://docs.python.org/3/library/stdtypes.html#memoryview'}" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "url = 'https://docs.python.org/3'\n", "syms = create_index(url)\n", @@ -1237,6 +1316,14 @@ "from nbdev._modidx import d\n", "assert d['syms']['nbdev.doclinks']['nbdev.doclinks.NbdevLookup'][0]" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4e2d51e", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": {}, diff --git a/settings.ini b/settings.ini index f22bb0be5..264630fdd 100644 --- a/settings.ini +++ b/settings.ini @@ -19,7 +19,7 @@ requirements = fastcore>=1.11.0 execnb>=0.1.12 astunparse ghapi>=1.0.3 watchdog pip_requirements = PyYAML conda_requirements = pyyaml conda_user = fastai -dev_requirements = ipywidgets nbdev-numpy nbdev-stdlib pandas matplotlib black svg.py nbclassic pysymbol_llm llms-txt sphinx plum-dispatch +dev_requirements = ipywidgets nbdev-numpy nbdev-stdlib pandas matplotlib black ruff svg.py nbclassic pysymbol_llm llms-txt sphinx plum-dispatch console_scripts = nbdev_create_config=nbdev.config:nbdev_create_config nbdev_update=nbdev.sync:nbdev_update nbdev_update_license=nbdev.cli:nbdev_update_license @@ -63,6 +63,8 @@ git_url = https://github.com/AnswerDotAI/nbdev lib_path = nbdev title = nbdev black_formatting = False +ruff_formatting = False +ruff_fixing = False readme_nb = getting_started.ipynb allowed_metadata_keys = allowed_cell_metadata_keys = diff --git a/tests/export_procs.ipynb b/tests/export_procs.ipynb index c57cebf41..550468bf0 100644 --- a/tests/export_procs.ipynb +++ b/tests/export_procs.ipynb @@ -7,10 +7,13 @@ "metadata": {}, "outputs": [], "source": [ + "import ast\n", "j = [1,\n", " 2,\n", " 3\n", - "]" + "]\n", + "for i in j:\n", + " str(1);" ] }, {