Try an interactive version of this dialog: Sign up at solve.it.com, click Upload, and pass this URL.

Note: 179

We noticed a bug with nbdev where nbdev export is too eager to replace "name" attributes in the pyproject.toml. So the example we had was that

[[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true

was changed to:

[[tool.uv.index]] name = "project_name" url = "https://download.pytorch.org/whl/cpu" explicit = true

Prompt: 92

I prepared a pyproject.toml and a nbdev project in the folder "nbdev_demo". Can you print out the content of the toml file before and after nbdev-export?

Assistant: 1,625

Let me check the folder and show the toml file contents first.

view(path='nbdev_demo')
Copied!
{
  "id": "toolu_016NKukwmhW5zVwkMAnaM5aT",
  "call": {
    "function": "view",
    "arguments": {
      "path": "nbdev_demo"
    }
  },
  "result": "Directory contents of /app/data/nbdev_bugfix/nbdev_demo:\n/app/data/nbdev_bugfix/nbdev_demo/nbs \ud83d\udcc1\n/app/data/nbdev_bugfix/nbdev_demo/LICENSE (11.1k)\n/app/data/nbdev_bugfix/nbdev_demo/README.md (1.3k)\n/app/data/nbdev_bugfix/nbdev_demo/pyproject.toml (0.9k)\n/app/data/nbdev_bugfix/nbdev_demo/MANIFEST.in (0.1k)\n/app/data/nbdev_bugfix/nbdev_demo/nbdev_demo \ud83d\udcc1\n/app/data/nbdev_bugfix/nbdev_demo/nbs/_quarto.yml (0.3k)\n/app/data/nbdev_bugfix/nbdev_demo/nbs/00_core.ipynb (0.9k)\n/app/data/nbdev_bugfix/nbdev_demo/nbs/styles.css (0.6k)\n/app/data/nbdev_bugfix/nbdev_demo/nbs/index.ipynb (3.5k)\n/app/data/nbdev_bugfix/nbdev_demo/nbs/nbdev.yml (0.2k)\n/app/data/nbdev_bugfix/nbdev_demo/nbdev_demo/core.py (0.2k)\n/app/data/nbdev_bugfix/nbdev_demo/nbdev_demo/__init__.py (0.0k)\n/app/data/nbdev_bugfix/nbdev_demo/nbdev_demo/_modidx.py (0.4k)"
}
view(path='nbdev_demo/pyproject.toml')
Copied!
{
  "id": "toolu_01Do7LiXqMEHU1MHeYEVNw9w",
  "call": {
    "function": "view",
    "arguments": {
      "path": "nbdev_demo/pyproject.toml"
    }
  },
  "result": "[build-system]\nrequires = [\"setuptools>=64\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"nbdev_demo\"\ndynamic = [\"version\"]\ndescription = \"\"\nreadme = \"README.md\"\nrequires-python = \">=3.10\"\nlicense = {text = \"Apache-2.0\"}\nauthors = [{name = \"Jonas Jakubassa\", email = \"jjakubassa@mis-analytics.de\"}]\nkeywords = ['nbdev']\nclassifiers = [\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3 :: Only\",\n]\ndependencies = []\n\n[project.urls]\nRepository = \"https://github.com/JonasJakubassa/nbdev_demo\"\nDocumentation = \"https://JonasJakubassa.github.io/nbdev_demo/\"\n\n[project.entry-points.nbdev]\nnbdev_demo = \"nbdev_demo._modidx:d\"\n\n[tool.setuptools.dynamic]\nversion = {attr = \"nbdev_demo.__version__\"}\n\n[tool.setuptools.packages.find]\ninclude = [\"nbdev_demo\"]\n\n[tool.nbdev]\n\n[[tool.uv.index]]\nname = \"pytorch-cpu\"\nurl = \"https://download.pytorch.org/whl/cpu\"\nexplicit = true"
}

Here's the before state — note name = "pytorch-cpu" in the [[tool.uv.index]] section at the bottom.

Now let me run nbdev_export and show the file again:

run_code_interactive(code='<…>…process\nresult = subprocess.ru…')
Copied!
{
  "id": "toolu_019MX7rumxSDWnNG1Af8gkE2",
  "call": {
    "function": "run_code_interactive",
    "arguments": {
      "code": "import subprocess\nresult = subprocess.run(\n    [\"nbdev_export\"], cwd=\"/app/data/nbdev_bugfix/nbdev_demo\", capture_output=True, text=True\n)\nprint(\"stdout:\", result.stdout)\nprint(\"stderr:\", result.stderr)\nprint(\"returncode:\", result.returncode)"
    }
  },
  "result": "{'success': 'CRITICAL: Message added to user dialog. STOP IMMEDIATELY. Do NOT call any more tools. Wait for user to run code and respond.'}"
}

I've added a code cell that runs nbdev_export in the nbdev_demo folder. Please run it, and then I'll show the toml file contents afterwards so we can compare!

Code: 523

!cat nbdev_demo/pyproject.toml

Output:

[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "nbdev_demo"
dynamic = ["version"]
description = ""
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
authors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]
keywords = ['nbdev']
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
]
dependencies = []

[project.urls]
Repository = "https://github.com/JonasJakubassa/nbdev_demo"
Documentation = "https://JonasJakubassa.github.io/nbdev_demo/"

[project.entry-points.nbdev]
nbdev_demo = "nbdev_demo._modidx:d"

[tool.setuptools.dynamic]
version = {attr = "nbdev_demo.__version__"}

[tool.setuptools.packages.find]
include = ["nbdev_demo"]

[tool.nbdev]

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

Code: 47

!cd nbdev_demo; nbdev-export

Code: 525

!cat nbdev_demo/pyproject.toml

Output:

[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "nbdev_demo"
dynamic = ["version"]
description = ""
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
authors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]
keywords = ['nbdev']
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
]
dependencies = []

[project.urls]
Repository = "https://github.com/JonasJakubassa/nbdev_demo"
Documentation = "https://JonasJakubassa.github.io/nbdev_demo/"

[project.entry-points.nbdev]
nbdev_demo = "nbdev_demo._modidx:d"

[tool.setuptools.dynamic]
version = {attr = "nbdev_demo.__version__"}

[tool.setuptools.packages.find]
include = ["nbdev_demo"]

[tool.nbdev]

[[tool.uv.index]]
name = "nbdev_demo"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

Prompt: 98

So we think nbdev is only looking for the attribute "name" and not looking for the section. Can you check this in the code: https://github.com/AnswerDotAI/nbdev?

Assistant: 2,685

read_gh_repo(owner='AnswerDotAI', repo='nbdev')
Copied!
{
  "id": "toolu_01MRxmsgDosTz2hQJCbJT4rW",
  "call": {
    "function": "read_gh_repo",
    "arguments": {
      "owner": "AnswerDotAI",
      "repo": "nbdev"
    }
  },
  "result": "<TRUNCATED>\u2026tAI/nbdev\nCreate delightful software with Jupyter Notebooks\n\n## / Files\n- \ud83d\udcc1 .github\n- .gitignore\n- .pre-commit-hooks.yaml\n- CHANGELOG.md\n- CODE_OF_CONDUCT.md\n- CONTRIBUTING.md\n- LICENSE\n- MANIFEST.in\n- README.md\n- TODO.md\n- \ud83d\udcc1 nbdev\n- \ud83d\udcc1 nbs\n- pyproject.toml\n- \ud83d\udcc1 tests\n- \ud83d\udcc1 tools\n\n## README\n# Getting Started\n\n\n<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->\n\n![CI](https://github.com/fastai/nbdev/actions/workflows/test.yaml/badge.svg)\n\n## \ud83d\uded1**Jan 2026 Major Version Update \u2013 Breaking Change**\ud83d\uded1\n\n**nbdev3 is here!** As many of you have been requesting, configuration\nhas moved from `settings.ini` to `pyproject.toml`, following modern\nPython packaging standards ([PEP\n621](https://peps.python.org/pep-0621/)). Your project metadata now\nlives in the standard `[project]` section, while nbdev-specific settings\ngo in `[tool.nbdev]`.\n\n**Migrating from nbdev2:** Run `nbdev-migrate-config` in your project\nroot to automatically convert your `settings.ini` to `pyproject.toml`\u2026</TRUNCATED>"
}

Let me look at the nbdev source code to find where it writes to pyproject.toml:

read_gh_repo(owner='AnswerDotAI', repo='nbdev', path='nbdev')
Copied!
{
  "id": "toolu_01FqB1gbFLt2nu9LsVckaxex",
  "call": {
    "function": "read_gh_repo",
    "arguments": {
      "owner": "AnswerDotAI",
      "repo": "nbdev",
      "path": "nbdev"
    }
  },
  "result": "# AnswerDotAI/nbdev\nCreate delightful software with Jupyter Notebooks\n\n## /nbdev Files\n- __init__.py\n- _modidx.py\n- clean.py\n- cli.py\n- config.py\n- diff.py\n- doclinks.py\n- export.py\n- extract_attachments.py\n- frontmatter.py\n- imports.py\n- maker.py\n- merge.py\n- migrate.py\n- process.py\n- processors.py\n- qmd.py\n- quarto.py\n- release.py\n- scrubmagics.py\n- serve.py\n- serve_drv.py\n- showdoc.py\n- sync.py\n- test.py"
}
read_gh_repo(owner='AnswerDotAI', repo='nbdev', path='nbdev/config.py')
Copied!
{
  "id": "toolu_01KqiWD7y2zCEMWAPyYXnEd2",
  "call": {
    "function": "read_gh_repo",
    "arguments": {
      "owner": "AnswerDotAI",
      "repo": "nbdev",
      "path": "nbdev/config.py"
    }
  },
  "result": "---------------------------------------------------------------------------\nAttributeError                            Traceback (most recent call last)\nCell In[15], line 2\n      1 import asyncio\n----> 2 __8b9ea5a5 = read_gh_repo(*(), **{'owner': 'AnswerDotAI', 'repo': 'nbdev', 'path': 'nbdev/config.py'})\n      3 if asyncio.iscoroutine(__8b9ea5a5): __8b9ea5a5 = await __8b9ea5a5\n\nFile /usr/local/lib/python3.12/site-packages/ipykernel_helper/core.py:244, in read_gh_repo(owner, repo, ref, path)\n    242 ref = ref or info.default_branch\n    243 contents = api.repos.get_content(owner, repo, path or '', ref=ref)\n--> 244 files = [f\"- {'\ud83d\udcc1 ' if c.type=='dir' else ''}{c.name}\" for c in contents]\n    245 res.append(f'\\n## /{path or \"\"} Files\\n' + '\\n'.join(files))\n    246 if not path:\n\nAttributeError: 'str' object has no attribute 'type'"
}

Let me read the config.py file directly:

read_url(url='<…>…w.githubusercontent.com/Answer…', as_md=False)
Copied!
{
  "id": "toolu_01UXSEBdGSZUbqGxhKFLha2w",
  "call": {
    "function": "read_url",
    "arguments": {
      "url": "https://raw.githubusercontent.com/AnswerDotAI/nbdev/main/nbdev/config.py",
      "as_md": "False"
    }
  },
  "result": "<TRUNCATED>\u2026ring nbdev and bootstrapping notebook export\"\"\"\n\n# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/01_config.ipynb.\n\n# %% auto #0\n__all__ = ['pyproject_nm', 'pyproject_tmpl', 'nbdev_defaults', 'pyproj_tmpl', 'nbdev_create_config', 'ConfigToml', 'get_config',\n           'is_nbdev', 'create_output', 'show_src', 'read_version', 'set_version', 'bump_version', 'update_version',\n           'update_proj', 'add_init', 'import_obj', 'write_cells']\n\n# %% ../nbs/api/01_config.ipynb #6fd14ecd\nfrom datetime import datetime\nfrom fastcore.docments import *\nfrom fastcore.utils import *\nfrom fastcore.meta import *\nfrom fastcore.script import *\nfrom fastcore.style import *\nfrom fastcore.xdg import *\n\nimport ast,importlib,warnings\nfrom IPython.display import Markdown\nfrom execnb.nbio import read_nb,NbCell\nfrom urllib.error import HTTPError\n\ntry: import tomllib\nexcept ImportError: import tomli as tomllib\n\n# %% ../nbs/api/01_config.ipynb #117128e6\npyproject_nm = 'pyproject.toml'\n_n\u2026</TRUNCATED>"
}

Found it! The culprit is in config.py. Here are the key lines:

Copied!
_re_proj = re.compile(r'^name\s*=\s*".*$', re.MULTILINE)

and:

Copied!
def update_proj(path):
    "Create or update `pyproject.toml` in the project root."
    fname = path/_pyproj
    if not fname.exists(): fname.write_text(pyproj_tmpl)
    txt = fname.read_text()
    txt = _re_proj.sub(f'name = "{get_config().lib_name}"', txt)
    txt = _re_reqpy.sub(f'requires-python = ">={get_config().min_python}"', txt)
    fname.write_text(txt)

The regex r'^name\s*=\s*".*$' with re.MULTILINE matches any line starting with name = "..." anywhere in the file — it doesn't limit itself to the [project] section. So _re_proj.sub(...) replaces every name attribute it finds, including the one under [[tool.uv.index]].

The same issue likely applies to _re_reqpy too — if you had a requires-python in another section, it would also get clobbered.

Would you like to work on a fix? One approach would be to make the regex section-aware (e.g., only replace within the [project] section). Another would be to use a proper TOML library to do the update. What direction are you thinking?

Prompt: 56

ah where do you think might "requires-python" also occur?

Assistant: 144

Good question! In practice, requires-python is pretty specific to the [project] section — it's not a standard key anywhere else in pyproject.toml. So while the regex has the same section-unaware pattern, it's unlikely to cause real problems.

name on the other hand is extremely generic — it shows up in [[tool.uv.index]], inside authors lists, potentially in [tool.poetry.source], etc. That's what makes it the real problem here.

Prompt: 75

ok, then let's fix the name regex. how to only look in the project section? make a few suggestions how to tackle this?

Assistant: 303

Here are three approaches, from simplest to most robust:

  1. Section-bounded regex — Use a regex that matches name = "..." only between [project] and the next [... header. Something like matching the whole [project] section and doing the substitution only within that matched group.

  2. Find section boundaries, then replace within range — Parse the file to find the line range of the [project] section (from [project] to the next [ header), then apply the existing _re_proj.sub() only on that slice of text, and stitch the file back together.

  3. Use tomlkit — A TOML library that preserves formatting, comments, and ordering. Parse → modify → write back. Most robust, but adds a dependency.

Given that nbdev already does simple regex-based text manipulation and the fix is small, I'd lean towards option 2 as the sweet spot — minimal change, no new dependency, and it properly scopes the replacement. What do you think?

Prompt: 57

let's only touch _re_proj. which regex do you suggest?

Assistant: 229

The idea is to anchor the regex to the [project] section by capturing from the [project] header up to the name line, ensuring we don't cross into another section. Something like:

Copied!
_re_proj = re.compile(r'(\[project\](?:\n(?!\[).*)*?\n)name\s*=\s*"[^"]*"')

The key piece is (?:\n(?!\[).*)*? — this matches lines between [project] and name, but refuses to cross a line starting with [ (which would be a new section header). The sub call would then use \1 to preserve the captured prefix.

Does the logic make sense? Want to try wiring it into the update_proj function?

Code: 414

toml = """
[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "nbdev_demo"
dynamic = ["version"]
description = ""
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
authors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]
keywords = ['nbdev']
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
]
dependencies = []

[project.urls]
Repository = "https://github.com/JonasJakubassa/nbdev_demo"
Documentation = "https://JonasJakubassa.github.io/nbdev_demo/"

[project.entry-points.nbdev]
nbdev_demo = "nbdev_demo._modidx:d"

[tool.setuptools.dynamic]
version = {attr = "nbdev_demo.__version__"}

[tool.setuptools.packages.find]
include = ["nbdev_demo"]

[tool.nbdev]

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
"""

Prompt: 70

Let's test the new parts. show me that only the project part is matched with the new patttern

Assistant: 125

Try this:

Copied!
import re
_re_proj = re.compile(r'(\[project\](?:\n(?!\[).*)*?\n)name\s*=\s*"[^"]*"')
print(_re_proj.findall(toml))

This should show that it only captures the [project] prefix — confirming it's matching the right name line and not the one under [[tool.uv.index]].

Code: 128

import re
_re_proj = re.compile(r'(\[project\](?:\n(?!\[).*)*?\n)name\s*=\s*\"[^\"]*\"')
print(_re_proj.findall(toml))

Output:

['[project]\n']

Prompt: 49

why is only the project part shown?

Assistant: 166

Because findall returns captured groups when the pattern contains parentheses. The group (\[project\](?:\n(?!\[).*)*?\n) captures everything before the name line — and since name is the very first key after [project], the captured prefix is just [project]\n.

The important thing is: it found exactly one match, and it's the right one. Try _re_proj.search(toml).group(0) to see the full match including the name line.

Code: 84

_re_proj.search(toml).group(0)

Output:

'[project]\nname = "nbdev_demo"'

Note: 53

Check if this is robust to order change and an earlier "["

Code: 415

toml = """
[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
dynamic = ["version"]
keywords = ['nbdev']
name = "nbdev_demo"
description = ""
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
authors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
]
dependencies = []

[project.urls]
Repository = "https://github.com/JonasJakubassa/nbdev_demo"
Documentation = "https://JonasJakubassa.github.io/nbdev_demo/"

[project.entry-points.nbdev]
nbdev_demo = "nbdev_demo._modidx:d"

[tool.setuptools.dynamic]
version = {attr = "nbdev_demo.__version__"}

[tool.setuptools.packages.find]
include = ["nbdev_demo"]

[tool.nbdev]

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
"""

Code: 121

_re_proj.search(toml).group(0)

Output:

'[project]\ndynamic = ["version"]\nkeywords = [\'nbdev\']\nname = "nbdev_demo"'

Prompt: 56

how to replace then the name with "project_name"?

Assistant: 84

Since the prefix is captured in group 1, you use \1 to preserve it:

Copied!
_re_proj.sub(r'\1name = "project_name"', toml)

This keeps everything before name intact and only replaces the name = "..." part.

Code: 607

_re_proj.sub(r'\1name = "project_name"', toml)

Output:

'\n[build-system]\nrequires = ["setuptools>=64"]\nbuild-backend = "setuptools.build_meta"\n\n[project]\ndynamic = ["version"]\nkeywords = [\'nbdev\']\nname = "project_name"\ndescription = ""\nreadme = "README.md"\nrequires-python = ">=3.10"\nlicense = {text = "Apache-2.0"}\nauthors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]\nclassifiers = [\n    "Programming Language :: Python :: 3",\n    "Programming Language :: Python :: 3 :: Only",\n]\ndependencies = []\n\n[project.urls]\nRepository = "https://github.com/JonasJakubassa/nbdev_demo"\nDocumentation = "https://JonasJakubassa.github.io/nbdev_demo/"\n\n[project.entry-points.nbdev]\nnbdev_demo = "nbdev_demo._modidx:d"\n\n[tool.setuptools.dynamic]\nversion = {attr = "nbdev_demo.__version__"}\n\n[tool.setuptools.packages.find]\ninclude = ["nbdev_demo"]\n\n[tool.nbdev]\n\n[[tool.uv.index]]\nname = "pytorch-cpu"\nurl = "https://download.pytorch.org/whl/cpu"\nexplicit = true\n'

Prompt: 54

show me how the new version should look like in full

Assistant: 331

Here's the updated update_proj with the new regex:

Copied!
_re_proj = re.compile(r'(\[project\](?:\n(?!\[).*)*?\n)name\s*=\s*"[^"]*"')
_re_reqpy = re.compile(r'^requires-python\s*=\s*".*$', re.MULTILINE)

def update_proj(path):
    "Create or update `pyproject.toml` in the project root."
    fname = path/_pyproj
    if not fname.exists(): fname.write_text(pyproj_tmpl)
    txt = fname.read_text()
    txt = _re_proj.sub(rf'\1name = "{get_config().lib_name}"', txt)
    txt = _re_reqpy.sub(f'requires-python = ">={get_config().min_python}"', txt)
    fname.write_text(txt)

The only two changes are:

  1. _re_proj uses the new section-aware pattern
  2. The .sub() call uses rf'\1name = ...' to preserve the captured prefix (and note the r prefix so \1 isn't interpreted as an escape)

Prompt: 50

should we keep re.MULTILINE?

Assistant: 58

No — the new regex doesn't use ^ or $ anchors (it uses explicit \n for line boundaries instead), so re.MULTILINE has no effect and can be dropped.

Note: 79

We forked nbdev and changed the lines as discussed above. Now let's check if we actually fixed the problem. We ran nbdev-test without error.

Code: 62

%pwd

Output:

'/app/data/nbdev_bugfix'

Code: 84

import nbdev.config
print(nbdev.config.__file__)

Output:

/usr/local/lib/python3.12/site-packages/nbdev/config.py

Code: 2,605

!pip install ./nbdev/

Output:

Processing ./nbdev
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Requirement already satisfied: fastcore>=1.12.3 in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (1.12.18)
Requirement already satisfied: execnb>=0.1.12 in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (0.1.18)
Requirement already satisfied: astunparse in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (1.6.3)
Requirement already satisfied: ghapi>=1.0.3 in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (1.0.10)
Requirement already satisfied: watchdog in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (6.0.0)
Requirement already satisfied: asttokens in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (3.0.1)
Requirement already satisfied: setuptools in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (82.0.0)
Requirement already satisfied: build in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (1.4.0)
Requirement already satisfied: fastgit in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (0.0.3)
Requirement already satisfied: pyyaml in /usr/local/lib/python3.12/site-packages (from nbdev==3.0.13) (6.0.3)
Requirement already satisfied: ipython in /usr/local/lib/python3.12/site-packages (from execnb>=0.1.12->nbdev==3.0.13) (9.10.0)
Requirement already satisfied: packaging in /usr/local/lib/python3.12/site-packages (from ghapi>=1.0.3->nbdev==3.0.13) (26.0)
Requirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.12/site-packages (from astunparse->nbdev==3.0.13) (0.46.3)
Requirement already satisfied: six<2.0,>=1.6.1 in /usr/local/lib/python3.12/site-packages (from astunparse->nbdev==3.0.13) (1.17.0)
Requirement already satisfied: pyproject_hooks in /usr/local/lib/python3.12/site-packages (from build->nbdev==3.0.13) (1.2.0)
Requirement already satisfied: decorator>=4.3.2 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (5.2.1)
Requirement already satisfied: ipython-pygments-lexers>=1.0.0 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (1.1.1)
Requirement already satisfied: jedi>=0.18.1 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (0.19.2)
Requirement already satisfied: matplotlib-inline>=0.1.5 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (0.2.1)
Requirement already satisfied: pexpect>4.3 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (4.9.0)
Requirement already satisfied: prompt_toolkit<3.1.0,>=3.0.41 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (3.0.52)
Requirement already satisfied: pygments>=2.11.0 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (2.19.2)
Requirement already satisfied: stack_data>=0.6.0 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (0.6.3)
Requirement already satisfied: traitlets>=5.13.0 in /usr/local/lib/python3.12/site-packages (from ipython->execnb>=0.1.12->nbdev==3.0.13) (5.14.3)
Requirement already satisfied: wcwidth in /usr/local/lib/python3.12/site-packages (from prompt_toolkit<3.1.0,>=3.0.41->ipython->execnb>=0.1.12->nbdev==3.0.13) (0.6.0)
Requirement already satisfied: parso<0.9.0,>=0.8.4 in /usr/local/lib/python3.12/site-packages (from jedi>=0.18.1->ipython->execnb>=0.1.12->nbdev==3.0.13) (0.8.6)
Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.12/site-packages (from pexpect>4.3->ipython->execnb>=0.1.12->nbdev==3.0.13) (0.7.0)
Requirement already satisfied: executing>=1.2.0 in /usr/local/lib/python3.12/site-packages (from stack_data>=0.6.0->ipython->execnb>=0.1.12->nbdev==3.0.13) (2.2.1)
Requirement already satisfied: pure-eval in /usr/local/lib/python3.12/site-packages (from stack_data>=0.6.0->ipython->execnb>=0.1.12->nbdev==3.0.13) (0.2.3)
Building wheels for collected packages: nbdev
  Building wheel for nbdev (pyproject.toml) ... done
  Created wheel for nbdev: filename=nbdev-3.0.13-py3-none-any.whl size=75067 sha256=b9f50d1f426c7e51909e3cda0d6777bcd970a546a5ee50243d31496fd8b33eba
  Stored in directory: /tmp/pip-ephem-wheel-cache-y25p52b8/wheels/9d/25/40/74bbfd6583a601e307b826c292ba36fb108709b5068e47e6ad
Successfully built nbdev
Installing collected packages: nbdev
  Attempting uninstall: nbdev
    Found existing installation: nbdev 3.0.13
    Uninstalling nbdev-3.0.13:
      Successfully uninstalled nbdev-3.0.13
Successfully installed nbdev-3.0.13

Code: 43

from nbdev.config import update_proj

Code: 228

update_proj??

Output:

Copied!
def update_proj(path):
    "Create or update `pyproject.toml` in the project root."
    fname = path/_pyproj
    if not fname.exists(): fname.write_text(pyproj_tmpl)
    txt = fname.read_text()
    txt = _re_proj.sub(rf'\1name = "{get_config().lib_name}"', txt)
    txt = _re_reqpy.sub(f'requires-python = ">={get_config().min_python}"', txt)
    fname.write_text(txt)

File: ~/.local/lib/python3.12/site-packages/nbdev/config.py

Code: 527

!cat nbdev_demo/pyproject.toml

Output:

[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "nbdev_demo"
dynamic = ["version"]
description = ""
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
authors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]
keywords = ['nbdev']
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
]
dependencies = []

[project.urls]
Repository = "https://github.com/JonasJakubassa/nbdev_demo"
Documentation = "https://JonasJakubassa.github.io/nbdev_demo/"

[project.entry-points.nbdev]
nbdev_demo = "nbdev_demo._modidx:d"

[tool.setuptools.dynamic]
version = {attr = "nbdev_demo.__version__"}

[tool.setuptools.packages.find]
include = ["nbdev_demo"]

[tool.nbdev]

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

Code: 47

!cd nbdev_demo; nbdev-export

Code: 527

!cat nbdev_demo/pyproject.toml

Output:

[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "nbdev_demo"
dynamic = ["version"]
description = ""
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
authors = [{name = "Jonas Jakubassa", email = "jjakubassa@mis-analytics.de"}]
keywords = ['nbdev']
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
]
dependencies = []

[project.urls]
Repository = "https://github.com/JonasJakubassa/nbdev_demo"
Documentation = "https://JonasJakubassa.github.io/nbdev_demo/"

[project.entry-points.nbdev]
nbdev_demo = "nbdev_demo._modidx:d"

[tool.setuptools.dynamic]
version = {attr = "nbdev_demo.__version__"}

[tool.setuptools.packages.find]
include = ["nbdev_demo"]

[tool.nbdev]

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true