mafw.devtools.toolchain.pyproject_modifier
Pyproject.toml modifier for toolchain dependency management.
This module provides the PyprojectModifier helper that reads,
modifies, and writes dependency specifiers in pyproject.toml while
preserving all existing comments, formatting, and key ordering via
tomlkit.
Multiple concrete ToolChainTool
implementations delegate their update logic to this helper so that
version-specifier manipulation is centralized and tested once.
Supported section paths:
project.dependenciesproject.optional-dependencies.<group>tool.hatch.envs.<env>.extra-dependencies
Classes
|
Read, modify, and write dependency specifiers in |
- class mafw.devtools.toolchain.pyproject_modifier.PyprojectModifier(path: Path)[source]
Bases:
objectRead, modify, and write dependency specifiers in
pyproject.toml.The modifier preserves all TOML comments, formatting, and key ordering by using
tomlkitfor parsing and serialization.Usage:
modifier = PyprojectModifier(Path('pyproject.toml')) modifier.read() specifier = modifier.get_dependency_specifier( 'pytest', 'project.optional-dependencies.test' ) modifier.update_lower_bound( 'pytest', '9.1.0', 'project.optional-dependencies.test' ) modifier.write()
- Parameters:
path (Path) – Path to the
pyproject.tomlfile.
- static _split_section_path(section_path: str) list[str][source]
Split a section path into individual keys for document traversal.
Handles the special case of
optional-dependenciesandextra-dependencieswhere the key itself contains a hyphen and must not be split on it.Known patterns: -
project.dependencies→["project", "dependencies"]-project.optional-dependencies.test→["project", "optional-dependencies", "test"]-tool.hatch.envs.types.extra-dependencies→["tool", "hatch", "envs", "types", "extra-dependencies"]- Parameters:
section_path (str) – The dot-separated section path.
- Returns:
List of keys to traverse.
- Return type:
list[str]
- _resolve_section(section_path: str) Array[source]
Navigate the TOML document to the dependency array at section_path.
Supported path formats:
project.dependenciesproject.optional-dependencies.<group>tool.hatch.envs.<env>.extra-dependencies
- Parameters:
section_path (str) – Dot-separated path to the dependency array.
- Returns:
The tomlkit Array object containing the dependency strings.
- Raises:
DevtoolsError – If the section path does not exist in the document.
- get_dependency_specifier(dependency_name: str, section_path: str) str[source]
Return the raw PEP 508 requirement string for a named dependency.
The dependency is located using PEP 503–normalized name comparison (case-insensitive, hyphens ≡ underscores ≡ dots).
- Parameters:
dependency_name (str) – The package name to look up (e.g.
"ruff").section_path (str) – Dot-separated path to the dependency array (e.g.
"project.optional-dependencies.dev").
- Returns:
The raw requirement string as stored in the TOML array.
- Return type:
str
- Raises:
DevtoolsError – If the section does not exist or the dependency is not found within it.
- read() None[source]
Load
pyproject.tomlfrom disk preserving formatting.- Raises:
DevtoolsError – If the file does not exist or cannot be parsed.
- update_lower_bound(dependency_name: str, new_version: str, section_path: str) None[source]
Replace the
>=lower-bound version for a named dependency.Only the version portion of the first
>=specifier is replaced; all surrounding whitespace, additional specifiers, extras, and environment markers are preserved.- Parameters:
dependency_name (str) – The package name whose bound to update.
new_version (str) – The new PEP 440 version string (e.g.
"9.1.0").section_path (str) – Dot-separated path to the dependency array.
- Raises:
DevtoolsError – If the section does not exist, the dependency is not found, or the dependency does not contain a
>=specifier.
- write() None[source]
Write the (possibly modified) document back to disk.
Preserves all comments, formatting, and key ordering for portions of the file that were not explicitly modified.
- Raises:
DevtoolsError – If no document has been loaded via
read().
- mafw.devtools.toolchain.pyproject_modifier._extract_package_name(specifier: str) str[source]
Extract the distribution name from a PEP 508 requirement string.
Handles extras (e.g.
"pandas[hdf5]>=2.2.3"), environment markers (e.g.'; python_version >= "3.14"'), and bare names.- Parameters:
specifier (str) – A PEP 508 requirement string.
- Returns:
The distribution name portion.
- Return type:
str
- mafw.devtools.toolchain.pyproject_modifier._normalize_name(name: str) str[source]
Normalize a Python package name per PEP 503.
Converts to lowercase and replaces runs of hyphens, underscores, and dots with a single hyphen, making name comparison canonical.
- Parameters:
name (str) – The raw package name (e.g.
"ruamel.yaml").- Returns:
Normalized form (e.g.
"ruamel-yaml").- Return type:
str
- mafw.devtools.toolchain.pyproject_modifier._LOWER_BOUND_RE: Final[Pattern[str]] = re.compile('(>=)\\s*([A-Za-z0-9.*!+]+)')
Regex matching a
>=specifier and its version portion.Group 1 captures the
>=operator. Group 2 captures the version string to be replaced.
- mafw.devtools.toolchain.pyproject_modifier._PEP503_NORMALIZE_RE: Final[Pattern[str]] = re.compile('[-_.]+')
Regex used for PEP 503 package-name normalization.