repomatic.deps.lockfile module

Read a uv.lock through the views the dependency commands need.

A lock runs to hundreds of kilobytes, and one command asks it many questions: the locked versions, their upload times, the cooldown it was resolved under, the specifiers each package declares. LockFile is the one entry point. It parses the file through the shared TOML cache, and builds each view the first time a caller asks for it.

repomatic.deps.lockfile.LOCK_TIMESTAMP_SENTINEL = '0001-01-01T00:00:00Z'

Placeholder uv writes to options.exclude-newer in uv.lock when the user-configured value is a relative span. The real cutoff is in options.exclude-newer-span as an ISO 8601 duration.

repomatic.deps.lockfile.UTC_TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%SZ'

The RFC 3339 UTC form of an instant, as uv writes it into uv.lock.

uv stores a value in this form verbatim, on every machine. That is why every cutoff this package writes uses it.

repomatic.deps.lockfile.EXTRA_MARKER_RE = re.compile("\\bextra\\s*==\\s*'([^']+)'")

Match the extra a requires-dist marker gates its dependency behind.

Searched rather than anchored: uv writes the bare extra == 'x' form most of the time, but combines it with a version guard (python_full_version >= ‘3.11’ and extra == ‘x’) when the declaration carries one. Anchoring would read those as unconditional dependencies.

class repomatic.deps.lockfile.LockSpecifiers(by_package, by_subgraph, by_main=<factory>)[source]

Bases: object

Dependency specifiers extracted from a uv.lock file.

Three views of the same data, built in a single pass over the lock packages:

by_package

{package_name: {dep_name: specifier}}. Every dependency declared by a package (main and dev) keyed by the declaring package name. Used for edge labels in dependency graphs.

by_subgraph

{subgraph_name: {dep_name: specifier}}. Primary dependencies keyed by dev-group name or extra name. Used for node labels inside subgraphs.

by_main

{package_name: {dep_name: specifier}}. Only the dependencies a package declares unconditionally, behind neither an extra nor a dev group. This is the authoritative answer to “what does installing this project pull in by default”, which a CycloneDX SBOM does not reliably give. See filter_root_edges(). A package with no metadata table is absent from the mapping entirely, telling “declares nothing unconditionally” apart from “not described here”.

by_package: dict[str, dict[str, str]]
by_subgraph: dict[str, dict[str, str]]
by_main: dict[str, dict[str, str]]
class repomatic.deps.lockfile.LockFile(data=None)[source]

Bases: object

A parsed uv.lock, with one lazily built view per question.

A missing lock reads as an empty one rather than as an error: several callers run before the first uv lock.

data: Mapping[str, Any]

The parsed document. Shared with the TOML cache: read-only.

classmethod load(lock_path)[source]

Read lock_path, or an empty lock when the file does not exist.

Return type:

LockFile

property versions: dict[str, str][source]

Package name to locked version, keyed by the canonical name uv writes.

property upload_times: dict[str, str][source]

Package name to the ISO 8601 upload-time of its sdist entry.

Packages with no sdist or no upload time are absent: a git or path source has no release to date.

property cooldown_span: timedelta | None[source]

Width of the rolling cooldown, from options.exclude-newer-span.

None when the lock records an absolute cutoff instead of a span, which leaves the cooldown-expiry forecasts nothing to project against.

property exclude_newer: str[source]

Effective options.exclude-newer cutoff, as an ISO 8601 instant.

When the project configures a relative span, uv writes LOCK_TIMESTAMP_SENTINEL here and the real width to options.exclude-newer-span. The cutoff then resolves to now - span the first time it is read. Empty when neither field is present.

property specifiers: LockSpecifiers[source]

The dependency specifiers of every package, in three views.

A single pass reads [package.metadata].requires-dist and [package.metadata.requires-dev]. See LockSpecifiers.

version_of(name)[source]

Locked version of name, in any PEP 503 spelling.

uv writes every package under its canonical name, while a declaration or an exclude-newer-package key keeps the spelling its author typed.

Parameters:

name (str) – A package name, in any spelling.

Return type:

str

Returns:

The version, or empty when the lock does not hold the package.

upload_time_of(name)[source]

Upload time of the locked version of name, in any PEP 503 spelling.

Parameters:

name (str) – A package name, in any spelling.

Return type:

str

Returns:

The ISO 8601 upload time. Empty for a package the lock does not hold, and for one it holds from a git or path source: version_of() tells the two apart.

uploaded_at(name)[source]

Parsed upload_time_of(), or None when there is none to parse.

Return type:

datetime | None

repomatic.deps.lockfile.diff_lock_versions(before, after)[source]

Compare two version mappings and return the list of changes.

Parameters:
  • before (Mapping[str, str]) – Package versions before the upgrade.

  • after (Mapping[str, str]) – Package versions after the upgrade.

Return type:

list[tuple[str, str, str]]

Returns:

A sorted list of (name, old_version, new_version) tuples. old_version is empty for added packages; new_version is empty for removed packages.