Skip to content
rlsbl.dep_rewrite
On this page

Path dependency detection and rewriting for PyPI packages, converting local file references in pyproject.toml to versioned constraints.

#rlsbl.dep_rewrite

#rlsbl.dep_rewrite

Path-dependency detection and rewriting for Python projects.

Two consumers share this module, and they want the same primitives applied to different section families:

  • The PyPI build (:mod:rlsbl.targets.pypi) rewrites a COPY of

pyproject.toml in a temp directory so a monorepo package's sibling path dependencies become version constraints in the built artifact. It covers the sections that end up in published metadata -- :data:SECTIONS_PUBLISHED.

  • **rlsbl rewrite uv-path-sources** rewrites the WORKING TREE, converting

every path/workspace-sourced internal dependency into a registry floor. It covers :data:SECTIONS_ALL, which adds PEP 735 [dependency-groups]: a dev-only path source is exactly as unbuildable for a consumer's checkout as a runtime one, even though it never reaches published metadata.

The section family is always passed explicitly. A default would silently decide which of those two jobs a caller is doing.

#iter_dep_arrays

python
def iter_dep_arrays(doc, families)

Yield (section_label, array) for each dependency array in doc.

doc is a parsed tomlkit document; the arrays are yielded live, so a caller can mutate them in place and dump the document afterwards. families selects which section families to visit -- see :data:SECTIONS_PUBLISHED / :data:SECTIONS_ALL.

#detect_path_deps_in

python
def detect_path_deps_in(doc, families)

Path dependencies declared in doc, restricted to families.

Returns a list of dicts with keys name, original, line_in_deps (index within its array) and section.

#rewrite_dep_arrays

python
def rewrite_dep_arrays(doc, rewrites, families)

Rewrite path deps named in rewrites, in place. Returns the count.

rewrites maps package name to a version constraint (">=1.2.0"). Only PATH dependencies are rewritten: a dependency already carrying a registry constraint is left alone.

#_split_requirement_parts

python
def _split_requirement_parts(text)

(name, extras, marker) of a requirement, or None when unparseable.

extras keeps its brackets ("[cli]") or is empty; marker keeps its leading ; ("; python_version < '3.12'") or is empty. Both are carried through a rewrite verbatim: dropping an extra changes what gets installed, and dropping a marker changes WHERE it gets installed.

#find_dep_entries

python
def find_dep_entries(doc, names, families)

Every dependency-array entry naming one of names.

names is a mapping of normalized name to the name as declared, so the caller decides the normalization (PEP 503 for PyPI). Returns a list of dicts with section, index, original, normalized.

#floor_dep_entries

python
def floor_dep_entries(doc, floors, families)

Rewrite entries naming a package in floors to name[extras]>=version.

floors maps normalized package name to the floor version string. Unlike :func:rewrite_dep_arrays this touches an entry whatever its current form -- a bare "sibling", an existing "sibling>=0.1" and a direct "sibling @ file:///..." all become "sibling>=<floor>". That is the point of the conversion: the floor must be the LOCKED version, not whatever the manifest happened to say while the dependency resolved from a checkout. Returns the number of entries rewritten.

#_normalize

python
def _normalize(name)

PEP 503 normalization, local to avoid a cycle through dep_floors.

#detect_uv_path_sources

python
def detect_uv_path_sources(doc)

Path/workspace entries in [tool.uv.sources].

Returns {package_name: kind} where kind is "path" or "workspace". Registry-neutral sources (git, url, index) are not returned: they already resolve to something a consumer can fetch. A source declared as a LIST of marker-gated tables counts when any of its entries is a path or workspace source.

#_source_kind

python
def _source_kind(entry)

"path", "workspace" or None for one [tool.uv.sources] entry.

#remove_uv_sources

python
def remove_uv_sources(doc, names)

Remove the path/workspace sources for names. Returns the count.

A source declared as a LIST of marker-gated tables is pruned rather than deleted: only its path/workspace elements go, and a non-path sibling (an index variant for the platforms the checkout does not cover, say) stays. Deleting the whole key would drop a declaration the caller never asked about and that nothing else restores. A list left with exactly one element stays a list -- uv reads it identically, and collapsing it would rewrite formatting this function was not asked to touch.

A list whose every element is a path/workspace source is removed entirely, like a plain table. An emptied sources table is removed too (and an emptied [tool.uv] with it), so the rewrite does not leave a header for nothing.

The count is per NAME, not per element: it answers "how many of the names I asked about were sourced from a checkout", which is what both callers compare against their preview.

#detect_path_deps

python
def detect_path_deps(pyproject_path)

Detect path dependencies in a pyproject.toml file.

Covers the published sections only (:data:SECTIONS_PUBLISHED) -- this is the build's question: "does the artifact's metadata reference a checkout?"

Returns a list of dicts with keys: - name: the dependency package name - original: the full original dependency string - line_in_deps: index within the dependencies array - section: "dependencies" or "optional-dependencies."

#rewrite_pyproject_deps

python
def rewrite_pyproject_deps(content, rewrites)

Rewrite path dependencies in pyproject.toml content to versioned constraints.

Args:

  • content: raw pyproject.toml content string
  • rewrites: dict mapping package name to version constraint string

(e.g., {"core": ">=1.2.0"})

Returns the modified content as a string with formatting preserved.

#build_rewrite_map

python
def build_rewrite_map(workspace_root, projects, graph)

Build a mapping of dependency names to version constraints.

For each project in the workspace that has a detectable version, adds name: ">=version" to the map. This map is intended to be passed to rewrite_pyproject_deps.

Args:

  • workspace_root: absolute path to the workspace root
  • projects: list of project dicts (each with "name" and "path")
  • graph: a WorkspaceGraph instance (unused currently, reserved for

future constraint refinement)

Returns a dict mapping package name to version constraint string.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search