Edit pyproject.toml file's tool.uv.sources option if it is a uv-managed project#81
Edit pyproject.toml file's tool.uv.sources option if it is a uv-managed project#81erral wants to merge 10 commits intomxstack:mainfrom
tool.uv.sources option if it is a uv-managed project#81Conversation
Co-authored-by: Steve Piercy <web@stevepiercy.com>
jensens
left a comment
There was a problem hiding this comment.
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"] |
There was a problem hiding this comment.
mxdev's design principle is "Minimal dependencies: Only packaging at runtime." tomlkit should be an optional/extras dependency, not a hard one.
| 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 |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
packaging is already a dependency and provides packaging.utils.canonicalize_name() which does exactly this (PEP 503). No need to reimplement.
| 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") |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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 installbehavior 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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Missing test coverage:
subdirectoryhandling (code line 82–84)- Existing dependencies not duplicated
- Error paths (unreadable/unwritable pyproject.toml)
- Idempotency (hook run twice → same result)
- Hook behavior when
tomlkitis 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] |
There was a problem hiding this comment.
Doesn't match the project's established changelog format. Should be:
| [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. |
There was a problem hiding this comment.
Trailing whitespace after the period.
For uv-managed projects (those with
[tool.uv] managed=trueinpyproject.toml) write in the[tool.uv.sources]option of thepyproject.tomlthe paths to the downloaded packages.See erral/mxdev-uv-pyproject-updater#1 and plone/cookieplone-templates#352 for reference