Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nbdev/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@
'nbdev.export.black_format': ('api/export.html#black_format', 'nbdev/export.py'),
'nbdev.export.nb_export': ('api/export.html#nb_export', 'nbdev/export.py'),
'nbdev.export.optional_procs': ('api/export.html#optional_procs', 'nbdev/export.py'),
'nbdev.export.ruff_fix': ('api/export.html#ruff_fix', 'nbdev/export.py'),
'nbdev.export.ruff_format': ('api/export.html#ruff_format', 'nbdev/export.py'),
'nbdev.export.scrub_magics': ('api/export.html#scrub_magics', 'nbdev/export.py')},
'nbdev.extract_attachments': {},
'nbdev.frontmatter': { 'nbdev.frontmatter.FrontmatterProc': ('api/frontmatter.html#frontmatterproc', 'nbdev/frontmatter.py'),
Expand Down
2 changes: 2 additions & 0 deletions nbdev/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion nbdev/doclinks.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def nbglob_cli(
@delegates(nbglob_cli)
def nbdev_export(
path:str=None, # Path or filename
procs:Param("tokens naming the export processors to use.", nargs="*", choices=optional_procs())="black_format",
procs:Param("tokens naming the export processors to use.", nargs="*", choices=optional_procs())=["black_format", "ruff_format", "ruff_fix"],
**kwargs):
"Export notebooks in `path` to Python modules"
if os.environ.get('IN_TEST',0): return
Expand Down
60 changes: 59 additions & 1 deletion nbdev/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/04_export.ipynb.

# %% auto 0
__all__ = ['ExportModuleProc', 'black_format', 'scrub_magics', 'optional_procs', 'nb_export']
__all__ = ['ExportModuleProc', 'black_format', 'ruff_format', 'ruff_fix', 'scrub_magics', 'optional_procs', 'nb_export']

# %% ../nbs/api/04_export.ipynb
from .config import *
Expand Down Expand Up @@ -47,6 +47,64 @@ def black_format(cell, # Cell to format
try: cell.source = _format_str(cell.source).strip()
except: pass

# %% ../nbs/api/04_export.ipynb
def ruff_format(cell, # Cell to format
force=False): # Turn ruff formatting on regardless of settings.ini
"Processor to format code with `ruff format`"
try: cfg = get_config()
except FileNotFoundError: return
if (not cfg.ruff_formatting and not force) or cell.cell_type != 'code': return

try: import subprocess
except: raise ImportError("subprocess is required")

try:
subprocess.run(['ruff', '--version'], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
raise ImportError("You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev")
else:
try:
result = subprocess.run(
['ruff', 'format', '-'],
input=cell.source,
capture_output=True,
text=True,
check=False
)
if result.returncode == 0:
cell.source = result.stdout.strip()
except:
pass

# %% ../nbs/api/04_export.ipynb
def ruff_fix(cell, # Cell to lint and fix
force=False): # Turn ruff fixing on regardless of settings.ini
"Processor to lint and auto-fix code with `ruff check --fix`"
try: cfg = get_config()
except FileNotFoundError: return
if (not cfg.ruff_fixing and not force) or cell.cell_type != 'code': return

try: import subprocess
except: raise ImportError("subprocess is required")

try:
subprocess.run(['ruff', '--version'], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
raise ImportError("You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev")
else:
try:
result = subprocess.run(
['ruff', 'check', '--fix', '-'],
input=cell.source,
capture_output=True,
text=True,
check=False
)
if result.returncode == 0 or result.returncode == 1: # 1 means fixes were applied
cell.source = result.stdout.strip() if result.stdout else cell.source
except:
pass

# %% ../nbs/api/04_export.ipynb
# includes the newline, because calling .strip() would affect all cells.
_magics_pattern = re.compile(r'^\s*(%%|%).*\n?', re.MULTILINE)
Expand Down
2 changes: 2 additions & 0 deletions nbs/api/01_config.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
119 changes: 117 additions & 2 deletions nbs/api/04_export.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,27 @@
"### Optional export processors"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False False False\n"
]
}
],
"source": [
"print(\n",
" get_config().black_formatting,\n",
" get_config().ruff_formatting,\n",
" get_config().ruff_fixing,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -136,7 +157,101 @@
"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)')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"def ruff_format(cell, # Cell to format\n",
" force=False): # Turn ruff formatting on regardless of settings.ini\n",
" \"Processor to format code with `ruff format`\"\n",
" try: cfg = get_config()\n",
" except FileNotFoundError: return\n",
" if (not cfg.ruff_formatting and not force) or cell.cell_type != 'code': return\n",
" \n",
" try: import subprocess\n",
" except: raise ImportError(\"subprocess is required\")\n",
" \n",
" try:\n",
" subprocess.run(['ruff', '--version'], capture_output=True, check=True)\n",
" except (subprocess.CalledProcessError, FileNotFoundError):\n",
" raise ImportError(\"You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev\")\n",
" else:\n",
" try:\n",
" result = subprocess.run(\n",
" ['ruff', 'format', '-'],\n",
" input=cell.source,\n",
" capture_output=True,\n",
" text=True,\n",
" check=False\n",
" )\n",
" if result.returncode == 0:\n",
" cell.source = result.stdout.strip()\n",
" except:\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_cell = read_nb('../../tests/export_procs.ipynb')['cells'][0]\n",
"ruff_format(_cell, force=True)\n",
"test_eq(_cell.source, 'import ast\\n\\nj = [1, 2, 3]\\nfor i in j:\\n str(1)')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"def ruff_fix(cell, # Cell to lint and fix\n",
" force=False): # Turn ruff fixing on regardless of settings.ini\n",
" \"Processor to lint and auto-fix code with `ruff check --fix`\"\n",
" try: cfg = get_config()\n",
" except FileNotFoundError: return\n",
" if (not cfg.ruff_fixing and not force) or cell.cell_type != 'code': return\n",
" \n",
" try: import subprocess\n",
" except: raise ImportError(\"subprocess is required\")\n",
"\n",
" try:\n",
" subprocess.run(['ruff', '--version'], capture_output=True, check=True)\n",
" except (subprocess.CalledProcessError, FileNotFoundError):\n",
" raise ImportError(\"You must install ruff: `pip install ruff` if you wish to use ruff formatting with nbdev\")\n",
" else:\n",
" try:\n",
" result = subprocess.run(\n",
" ['ruff', 'check', '--fix', '-'],\n",
" input=cell.source,\n",
" capture_output=True,\n",
" text=True,\n",
" check=False\n",
" )\n",
" if result.returncode == 0 or result.returncode == 1: # 1 means fixes were applied\n",
" cell.source = result.stdout.strip() if result.stdout else cell.source\n",
" except:\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_cell = read_nb('../../tests/export_procs.ipynb')['cells'][0]\n",
"ruff_fix(_cell, force=True)\n",
"test_eq(_cell.source, 'j = [1,\\n 2,\\n 3\\n]\\nfor i in j:\\n str(1)')"
]
},
{
Expand Down Expand Up @@ -221,7 +336,7 @@
"outputs": [],
"source": [
"# every optional processor should be explicitly listed here\n",
"test_eq(optional_procs(), ['black_format', 'scrub_magics'])"
"test_eq(optional_procs(), ['black_format', 'ruff_format', 'ruff_fix', 'scrub_magics'])"
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions nbs/api/05_doclinks.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
"@delegates(nbglob_cli)\n",
"def nbdev_export(\n",
" path:str=None, # Path or filename\n",
" procs:Param(\"tokens naming the export processors to use.\", nargs=\"*\", choices=optional_procs())=\"black_format\",\n",
" procs:Param(\"tokens naming the export processors to use.\", nargs=\"*\", choices=optional_procs())=[\"black_format\", \"ruff_format\", \"ruff_fix\"],\n",
" **kwargs):\n",
" \"Export notebooks in `path` to Python modules\"\n",
" if os.environ.get('IN_TEST',0): return\n",
Expand All @@ -385,7 +385,7 @@
"source": [
"`procs` names the optional processors you wish to run on the exported cells of your notebook.\n",
"\n",
"N.B.: the `black_format` processor is passed in by default. But it is a no-op, unless `black_formatting=True` is set in your `settings.ini` configuration. You can omit it from `nbdev_export` on the command line by passing in `--procs`."
"N.B.: the `[\"black_format\", \"ruff_format\", \"ruff_fix\"]` processor is passed in by default. But it is a no-op, unless `black_formatting=True`, `ruff_formatting=True`, or `ruff_fixing=True` is set in your `settings.ini` configuration. You can omit it from `nbdev_export` on the command line by passing in `--procs`."
]
},
{
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dynamic = [ "keywords", "description", "version", "dependencies", "optional-depe

[tool.uv]
cache-keys = [{ file = "pyproject.toml" }, { file = "settings.ini" }, { file = "setup.py" }]

4 changes: 3 additions & 1 deletion settings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ requirements = fastcore>=1.8.14 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
Expand Down Expand Up @@ -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 =
Expand Down
11 changes: 9 additions & 2 deletions tests/export_procs.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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);"
]
},
{
Expand All @@ -36,9 +39,13 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "env",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.13.3"
}
},
"nbformat": 4,
Expand Down