Skip to main content

AXM Logo

axm-ingot — Shared helper library for the AXM forge

CI axm-audit axm-init Coverage PyPI Python 3.12+ Docs


axm-ingot is the AXM forge's shared helper library: the single home for small, general-purpose functions that more than one package needs. Instead of copy-pasting the same logic into axm-ast, axm-audit, axm-init and axm-anvil — and testing it N times, inconsistently — the logic lives here once, is tested once, and is imported as a normal workspace dependency.

It is a pure library: no CLI, no MCP tool, no side effects. Just typed, import-ready helpers.

📖 Full documentation

Why it exists

A monorepo accumulates duplication: the same "walk up to the project root", "read [tool.uv.workspace].members", "resolve the workspace members" snippets reappear in package after package, each with its own subtle bugs and its own half-tested copy. axm-ingot is the deliberate counter-move — a thin, dependency-light ingot of common code that downstream packages melt into their own logic:

  • Factor once, fix once — a bug fixed here is fixed everywhere.
  • Test once, trust everywhere — helpers are covered in this package, so consumers don't re-test the same primitive.
  • Stable public surface — consumers import from axm_ingot, not from each other, keeping the dependency graph a tree (no cross-tool coupling).

Today the shared surface is uv-workspace resolution; the library grows by promotion — when a helper proves useful to a second package, it moves here.

What belongs here (the light-leaf invariant)

axm-ingot is a dependency-graph leaf imported across the whole galaxy. Its value depends on staying light: every consumer that imports it for one small helper inherits all of its dependencies. A fat leaf is no longer a leaf.

Hard rule — a helper may be promoted into axm-ingot only if its dependencies are stdlib-only — it adds no dependency at all. dependencies here stays empty. Even Pydantic — a near-universal AXM dependency — does not belong: it is still a dependency, and adding it breaks the leaf invariant. Value objects here are frozen @dataclass, not BaseModel.

Belongs in ingot ✅ Does not belong ❌
stdlib-only (pathlib, tomllib, re, dataclasses, json…) pulls httpx, pandas, torch, a DB driver, an SDK…
frozen @dataclass value objects pydantic models (Pydantic is a dependency — a fat leaf is no longer a leaf)
e.g. resolve_workspace (tomllib + pathlib) e.g. request_with_retry (needs httpx)

A reusable-but-heavy helper does not come here even when it has several consumers: keep it reuse_in_place (import it from the package that owns it), or give it a thematic light lib of its own (e.g. an axm-net for resilient HTTP) — never the generic leaf. The Rule of Three (≥ 2 consumers) is necessary for promotion but not sufficient; the dependency gate is the second lock.

Features

  • uv-workspace resolutionresolve_workspace() parses [tool.uv.workspace], expands member globs, subtracts exclude, keeps only directories carrying a pyproject.toml, and returns members sorted by name.
  • Project-root discoveryfind_project_root() walks parents to the first ancestor holding any pyproject.toml (never returns None); find_workspace_root() finds the nearest uv-workspace root specifically.
  • Pure parsing primitiveparse_workspace_members() returns the raw, unexpanded member strings from pyproject text, with no filesystem access.
  • Typed value objects — frozen ResolvedWorkspace and Member dataclasses describe the resolved result.
  • Defensive by design — a missing, malformed, non-UTF-8, or otherwise unreadable pyproject.toml, and hostile member globs (absolute paths, patterns Path.glob rejects), all degrade to empty/None results rather than raising.
  • Modern Python — 3.12+ with strict typing, zero runtime dependencies beyond the standard library.

Installation

uv add axm-ingot

Or, as a sibling package inside the workspace, declare it as a workspace dependency in your pyproject.toml:

[project]
dependencies = ["axm-ingot"]

[tool.uv.sources]
axm-ingot = { workspace = true }

Quick Start

axm-ingot is a library — you import its helpers, there is no command to run.

from pathlib import Path

from axm_ingot import (
    find_project_root,
    find_workspace_root,
    resolve_workspace,
)

# Walk up to the nearest project root (any pyproject.toml ancestor).
root = find_project_root(Path("packages/axm-ast/src/axm_ast/core"))

# Resolve a uv workspace into its sorted members.
workspace = resolve_workspace(root)
if workspace is not None:
    for member in workspace.members:
        print(member.name, "->", member.path)

# Or just locate the nearest uv-workspace root.
ws_root = find_workspace_root(Path.cwd())

The pure parsing primitive is available from the uv subpackage when you only have pyproject text (no filesystem):

from axm_ingot.uv import parse_workspace_members

members = parse_workspace_members('[tool.uv.workspace]\nmembers = ["packages/*"]\n')
# ['packages/*']  — raw, unexpanded

Public API

Symbol Kind Description
resolve_workspace(dir) function Resolve a uv workspace → ResolvedWorkspace | None
find_project_root(start) function Nearest ancestor with any pyproject.toml (never None)
find_workspace_root(start) function Nearest uv-workspace root → Path | None
parse_workspace_members(text) function Raw members from pyproject text (axm_ingot.uv)
ResolvedWorkspace dataclass {root, members} — a resolved workspace
Member dataclass {name, path} — one workspace member

Development

This package is part of the axm-forge workspace.

git clone https://github.com/axm-protocols/axm-forge.git
cd axm-forge
uv sync --all-groups
uv run --package axm-ingot --directory packages/axm-ingot pytest -x -q

License

Apache-2.0 — © 2026 axm-protocols

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

axm_ingot-0.2.0.tar.gz (28.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

axm_ingot-0.2.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file axm_ingot-0.2.0.tar.gz.

File metadata

  • Download URL: axm_ingot-0.2.0.tar.gz
  • Upload date:
  • Size: 28.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for axm_ingot-0.2.0.tar.gz
Algorithm Hash digest
SHA256 74269ddfd886bf863ab9efa2949ee594d42c569b6d61502248230b556f698d73
MD5 984236ba9cbfdcf27ae0765ee1c17e9b
BLAKE2b-256 ee21bd56cc56a94d9b06dce3de9fe7f552dc8d8e61d82dd96d5ed729f992bda3

See more details on using hashes here.

Provenance

The following attestation bundles were made for axm_ingot-0.2.0.tar.gz:

Publisher: publish.yml on axm-protocols/axm-forge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file axm_ingot-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: axm_ingot-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for axm_ingot-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3877f6c875e70678ae6f566e5e43807dfa07bb99b28590bf18c790bdd4ddeb26
MD5 a345104cd30ed2f58b9dbe702ef259d6
BLAKE2b-256 23649de1da6caa2ad905291ef81fce27edc5d7568702ec58339699cc8199790e

See more details on using hashes here.

Provenance

The following attestation bundles were made for axm_ingot-0.2.0-py3-none-any.whl:

Publisher: publish.yml on axm-protocols/axm-forge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page