Skip to content

Add a Client that mirrors the CLI command tree - #48

Merged
jon-trust merged 1 commit into
mainfrom
feat/library-client
Oct 9, 2026
Merged

jon-trust merged 1 commit into
mainfrom
feat/library-client

Conversation

@jon-trust

Copy link
Copy Markdown
Contributor

Adds a Python library entry point that mirrors the CLI:

from tmxcaliber import Client

api = Client()
api.me()
api.threatmodels.list(provider="aws")
api.threatmodels.threats("aws-s3", feature_class="S3.FC1")
api.compliance.mappings.list(framework="nist-800-53-r5")

What changes

  • Client, exported from the package root along with the error classes. Every CLI command is an attribute path built at construction from contract.ROUTES, so a route the API adds is a method and a command together, with no route named in the library code. Each operation carries an inspect.Signature derived from its route, so help() and argument errors read like an ordinary function.
  • Credentials resolve exactly as the CLI's do. api_key and api_url arguments override both, and when both are passed the credentials file is not read. A key that would reach a stored endpoint raises the CLI's note as a UserWarning.
  • Incomplete answers raise IncompleteAnswer, which carries the rows that did arrive. allow_incomplete=True mirrors --allow-incomplete.
  • lib/remote/operations.py now owns the route-to-request logic: path filling, filters, paging and incompleteness. run_api_command and Client both call it, and the CLI's behavior is unchanged.
  • Settings.stored_endpoint_note owns the stored-endpoint wording for both surfaces. sends_environment_key_to_stored_endpoint is renamed sends_key_to_stored_endpoint, and it also covers a key passed as an argument.
  • README: new "Use the API from Python" section.
  • Version: bumped to 0.6.0, with uv.lock.

Testing

  • New test/test_remote_api.py, which iterates over ROUTES rather than naming routes: every route is reachable at its command path, sends its own path and filters, and has a signature matching its CLI options. It also covers paging, @release, refused arguments, required filters, incompleteness, credential precedence, and the stored-endpoint warning.
  • Full suite: 638 passed. ruff format --check, ruff check and mypy are clean. uv build and twine check pass.

from tmxcaliber import Client exposes every API command as an attribute
path built at construction from the route table, so a new route becomes
a method and a command together:

    api = Client()
    api.threatmodels.threats("aws-s3", feature_class="S3.FC1")

Each operation carries a signature derived from its route. Credentials
resolve as the CLI's do, with api_key and api_url arguments ranked above
both. An incomplete answer raises IncompleteAnswer unless the caller
passes allow_incomplete=True.

The route-to-request logic moves from remote_cli into
lib/remote/operations.py so the CLI and Client share it, and the stored
endpoint note moves onto Settings so both surfaces word it the same way.

Bump to 0.6.0.
@jon-trust
jon-trust merged commit daf5c5d into main Oct 9, 2026
4 checks passed
@jon-trust
jon-trust deleted the feat/library-client branch October 9, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant