Skip to content

Edit pyproject.toml file's tool.uv.sources option if it is a uv-managed project#81

Open
erral wants to merge 10 commits intomxstack:mainfrom
erral:uv-managed
Open

Edit pyproject.toml file's tool.uv.sources option if it is a uv-managed project#81
erral wants to merge 10 commits intomxstack:mainfrom
erral:uv-managed

Conversation

@erral
Copy link
Copy Markdown

@erral erral commented Mar 27, 2026

For uv-managed projects (those with [tool.uv] managed=true in pyproject.toml) write in the [tool.uv.sources] option of the pyproject.toml the paths to the downloaded packages.

See erral/mxdev-uv-pyproject-updater#1 and plone/cookieplone-templates#352 for reference

Copy link
Copy Markdown

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed heading

Co-authored-by: Steve Piercy <web@stevepiercy.com>
Copy link
Copy Markdown
Member

@jensens jensens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feature concept is sound but needs rework in a few areas before merge. See inline comments.

"Programming Language :: Python :: 3.14",
]
dependencies = ["packaging"]
dependencies = ["packaging", "tomlkit>=0.12.0"]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mxdev's design principle is "Minimal dependencies: Only packaging at runtime." tomlkit should be an optional/extras dependency, not a hard one.

Suggested change
dependencies = ["packaging", "tomlkit>=0.12.0"]
dependencies = ["packaging"]

Add an extras group instead:

[project.optional-dependencies]
uv = ["tomlkit>=0.12.0"]

Then lazy-import in the hook with a clear install hint on ImportError.


import logging
import re
import tomlkit
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Top-level import means the module fails to load if tomlkit is not installed. Move this into the methods that need it (lazy import) so the hook class can be loaded without the dependency. Raise a clear RuntimeError with install instructions (pip install mxdev[uv]) when the import fails at call time.


def normalize_name(name: str) -> str:
"""PEP 503 normalization: lowercased, runs of -, _, . become single -"""
return re.sub(r"[-_.]+", "-", name).lower()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

packaging is already a dependency and provides packaging.utils.canonicalize_name() which does exactly this (PEP 503). No need to reimplement.

Suggested change
return re.sub(r"[-_.]+", "-", name).lower()
from packaging.utils import canonicalize_name as normalize_name

pass

def write(self, state: State) -> None:
pyproject_path = Path("pyproject.toml")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path("pyproject.toml") is CWD-relative. If mxdev is invoked with -c /other/path/mx.ini, this reads/writes the wrong file. Should resolve relative to the config file location or accept the path from state/config.

with pyproject_path.open("r", encoding="utf-8") as f:
doc = tomlkit.load(f)
except Exception as e:
logger.error("[%s] Failed to read pyproject.toml: %s", self.namespace, e)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching bare Exception swallows programming errors (TypeError, AttributeError, KeyError). Narrow to OSError (covers permission, encoding, disk-full). Same applies to the write block below at line 59.

doc.add("project", tomlkit.table())

if "dependencies" not in doc["project"]:
doc["project"]["dependencies"] = tomlkit.array()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire block (lines 108–126) should be removed.

Writing to [project.dependencies] mutates the project's public dependency metadata. Problems:

  • Entries survive after source dirs are removed — no cleanup mechanism
  • Easily committed to VCS by accident
  • Changes uv sync / pip install behavior permanently
  • Only grows, never shrinks (not idempotent)

[tool.uv.sources] is the correct place for dev-time path mappings. Users should manage [project.dependencies] themselves.

from mxdev.hooks import Hook
from mxdev.state import State
from pathlib import Path
from typing import Any
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any is only used for doc param in _update_pyproject. Use tomlkit.TOMLDocument for actual type safety.

sources = doc["tool"]["uv"]["sources"]
assert sources["editable-pkg"]["editable"] is True
assert sources["fixed-pkg"]["editable"] is False
assert "skip-pkg" not in sources
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage:

  • subdirectory handling (code line 82–84)
  • Existing dependencies not duplicated
  • Error paths (unreadable/unwritable pyproject.toml)
  • Idempotency (hook run twice → same result)
  • Hook behavior when tomlkit is not installed (after making it optional)

## 5.2.0 (unreleased)

- Feature: Built-in integration with `uv` through `pyproject.toml`. When `mxdev` is run, it checks if the project has a `pyproject.toml` containing `[tool.uv]` with `managed = true`. If so, mxdev automatically adds checked-out packages to `[tool.uv.sources]` and `[project.dependencies]`. This allows for seamless use of `uv sync` or `uv run` with local checkouts. `tomlkit` is now a core dependency to preserve `pyproject.toml` formatting during updates.
[erral, 2026-03-27]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't match the project's established changelog format. Should be:

Suggested change
[erral, 2026-03-27]
[erral]


## uv pyproject.toml integration

mxdev includes a built-in hook to automatically update your `pyproject.toml` file when working with [uv](https://docs.astral.sh/uv/)-managed projects.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace after the period.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants