repomatic.tooling.tool_runner module¶
Unified tool runner with managed config resolution.
Provides repomatic run <tool> — a single entry point that installs an
external tool at a pinned version, resolves its configuration through a strict
4-level precedence chain, translates [tool.X] sections from
pyproject.toml into the tool’s native format, and invokes the tool with
the resolved config. The tool catalog it drives (ToolSpec entries, pinned
versions, checksums) lives in tool_registry.py.
Important
Config resolution precedence (first match wins, no merging):
Native config file — tool’s own config file in the repo.
``[tool.X]`` in ``pyproject.toml`` — translated to native format.
Bundled default — from
repomatic/data/.Bare invocation — no config at all.
- repomatic.tooling.tool_runner.load_pyproject_tool_section(tool_name)[source]¶
Load
[tool.<tool_name>]frompyproject.tomlin the current directory.Returns the live
tomlrt.Table(adictsubclass) rather than a plain-dict copy, so the section keeps its comment trivia for formats that can preserve it on materialization (seeNativeFormat.serialize()). Callers that only read values or test truthiness are unaffected.
- repomatic.tooling.tool_runner.resolve_config(spec, tool_config=None)[source]¶
Resolve config for a tool using the 4-level precedence chain.
Caution
The levels do not merge. The walk stops at its first hit, so a native config file or a
[tool.X]section replaces the bundled default in full rather than layering on top of it. A downstream repo overriding one rule must restate every bundled rule it wants to keep, and gains nothing when the bundled default later grows a rule.resolve_config_source()labels a shadowing config sorepomatic run --listshows the loss.- Parameters:
- Return type:
- Returns:
Tuple of (extra CLI args for config, path to clean up). The path is
Nonewhen no cleanup is needed (cache-based configs persist across runs). Non-Nonepaths are CWD files written for tools that have no--configflag.
- repomatic.tooling.tool_runner.DOWNLOAD_TIMEOUT = 30¶
Socket-level timeout for artifact downloads, in seconds.
A stall guard, not a transfer budget:
urlopenapplies it to each blocking socket operation, so a healthy multi-minute download is unaffected while a dead connection fails in seconds instead of hanging a CI job to the runner ceiling. Deliberately larger thanrepomatic.http.DEFAULT_TIMEOUT, which is sized for small JSON API responses.
- repomatic.tooling.tool_runner.download_to(url, dest_path, *, label=None, progress=True)[source]¶
Stream url into dest_path and return its SHA-256 hex digest.
Chunked download with incremental hash computation, so large binaries never load fully into memory. Shows a progress bar on interactive terminals when the server provides a
Content-Lengthheader; passprogress=Falsefrom concurrent callers whose fan-out draws its own progress display.The single download seam for every artifact repomatic fetches by hand: whatever consumes the digest (verification in
_download_and_verify(), checksum harvesting inchecksums.py) builds on this so the truncation guard below applies to all of them. A short body (proxy hiccup, dropped connection) hashes to a wrong digest, so without the guard it would surface later as a checksum mismatch: that reads as a stale pin or a tampered artifact when nothing is wrong upstream. Name the real failure instead.- Parameters:
- Return type:
- Returns:
Lowercase hex SHA-256 digest of the downloaded bytes.
- Raises:
OSError – If the body is shorter than the advertised
Content-Length.
- repomatic.tooling.tool_runner.ensure_binary(name: str) Path[source]¶
Install a registry binary tool and return the path to its executable.
The seam for repomatic code that shells out to a third-party binary but is not itself a
run_tool()invocation. It buys the same guarantees everyrepomatic runbinary gets: the registry-pinned version, its archive verified against the recorded SHA-256, and a shared cache so repeated calls in one run download once.Prefer this over looking the tool up on
PATH. WhateverPATHoffers is whichever version the machine or CI image happens to carry, unpinned and unverified, and it differs between a developer’s laptop and every runner.Memoized per tool name: callers in a loop (
format-imagesoptimizing one PNG per call) hit the install-and-verify path once per process, not once per file. Failures are not memoized, so a transient download error can be retried.
- repomatic.tooling.tool_runner.resolve_default_args(spec)[source]¶
Build the argument batches for a bare
repomatic run <tool>.Combines
default_argswith the file list named bydefault_paths, splitting into one batch per file whenper_fileis set.- Parameters:
spec (
ToolSpec) – The tool to resolve defaults for.- Return type:
- Returns:
One argument list per invocation; a single empty-argument batch when the tool declares no defaults, so the caller runs it bare as before.
Nonewhen the tool wants targets and the repository holds none, which means skip the tool rather than invoke it pathless.
- repomatic.tooling.tool_runner.TOOL_CRASH_EXIT_CODE = 70¶
Exit code reported when a tool contradicts its own rewrite status.
EX_SOFTWAREfromsysexits.h: an internal error in the tool being run. Deliberately outside the set a formatter’s caller tolerates, so a crash cannot land on the code that means “I reformatted a file”. Seerewrite_exit_code.
- repomatic.tooling.tool_runner.run_tool(name, extra_args=(), version=None, checksum=None, skip_checksum=False, no_cache=False)[source]¶
Run an external tool with managed config resolution.
With no extra_args, a tool declaring
default_argsordefault_pathsruns the invocation CI performs, resolved in-process byresolve_default_args(). Any explicit argument suppresses that entirely and is passed through as before.- Parameters:
name (
str) – Tool name (must be inTOOL_REGISTRY).extra_args (
Sequence[str]) – Extra arguments passed through to the tool.checksum (
str|None) – Override the SHA-256 checksum for the current platform.skip_checksum (
bool) – Skip SHA-256 verification entirely.no_cache (
bool) – Bypass the binary cache whenTrue.
- Return type:
- Returns:
The tool’s exit code; the first non-zero one when the defaults resolved to several invocations, or
TOOL_CRASH_EXIT_CODEwhen a tool declaringrewrite_exit_codereports a rewrite it did not perform.
- repomatic.tooling.tool_runner.verify_via_write_path(name, extra_args=(), **run_kwargs)[source]¶
Check a
post_processtool’s formatting without touching the tree.A tool pairing
post_processwithcheck_flagshas no trustworthy check mode: the fixup only runs on the write path, so the check status can flag drift the write path would reconcile, or miss drift it would introduce (seecheck_flags). This runs the write path against throwaway copies instead, then compares, which is the only authoritative answer.Important
The copies are made inside the working directory, not in the system temp area. Formatters discover their config by walking up from each file, so a copy parked outside the repository resolves a different config and silently reports drift that does not exist.
The working tree is never written to: only the copies are formatted, and they are removed before returning.
- Parameters:
name (
str) – Tool name, as inrun_tool().extra_args (
Sequence[str]) – Arguments for the tool. Any existing path among them is copied and rewritten to its copy; check flags are dropped, since they would defeat the write path this relies on. Every other argument is passed through untouched. Empty resolves the tool’s registry defaults, the same setrun_tool()would have run, flattened into one batch: the copies are per-path already, so aper_filesplit would only cost extra invocations.run_kwargs (
Any) – Forwarded verbatim torun_tool().
- Return type:
- Returns:
(exit_code, drifted), whereexit_codeis0when every target is already formatted and1otherwise, anddriftednames the paths the write path would have changed. A tool that fails on the copies yields its own exit code and no drift, since it measured nothing.
- repomatic.tooling.tool_runner.resolve_config_source(spec)[source]¶
Return a human-readable description of the active config source.
Used by
repomatic run --listto show which precedence level is active for each tool in the current repo.- Return type:
- repomatic.tooling.tool_runner.find_unmodified_configs(root=None)[source]¶
Find native config files identical to their bundled defaults.
Iterates over every tool in
TOOL_REGISTRYthat has adefault_config. For each, checks whether any of itsnative_config_filesexists on disk and is content-identical to the bundled default after trailing-whitespace normalization.The normalization (
rstrip() + "\n") matches the convention used by_init_config_fileswhen writing files duringinit.- Parameters:
root (
Path|None) – Directory the relative config paths resolve against. Defaults to the working directory;run_initpasses itsoutput_dirso the scan and the deletion the CLI derives from it (--delete-unmodified) agree on one tree.- Return type:
- Returns:
List of
(tool_name, relative_path)tuples for each unmodified file found.