fromargs
fromargs is a self-healing, agent-friendly wrapper around
Cyclopts. It composes one cyclopts.App,
forces every command's return value to JSON, and repairs the argv mistakes an
LLM agent tends to make, without ever guessing at intent it cannot verify.
Install
uv add fromargs
# or
pip install fromargs
fromargs pins cyclopts>=5,<6.
Quick start
from typing import Annotated
import fromargs
app = fromargs.App("cheese-cave", help="Track wheels of cheese as they ripen.")
@app.command
def age(
name: str,
*,
weeks: Annotated[int, fromargs.Parameter(help="Number of weeks to age.")],
dry_run: bool = False,
) -> dict[str, object]:
"""Age one wheel for more weeks."""
if weeks < 1:
raise fromargs.CliError(f"--weeks must be at least 1, got {weeks}")
return {"name": name, "weeks": weeks, "dry_run": dry_run}
if __name__ == "__main__":
app.main()
$ python cheese_cave.py age brie --weeks 2
{
"name": "brie",
"weeks": 2,
"dry_run": false
}
See examples/cheese_cave.py for a fuller example, with a command group and
a truncated list result.
Output contract
- A handler returns data, not text. A non-
Nonereturn value prints as one JSON document on stdout, then the process exits0. - A
Nonereturn value means exit0with no stdout. - A
setorfrozensetprints as a sorted JSON list. Items that cannot sort together order by their canonical JSON text, so the output stays stable.bytes,bytearray,memoryview, and iterators (such as generators) raiseTypeError: return astror alistinstead. - Every error is one JSON line on stderr:
{"error": <message>, "exit_code": <n>}. - Raise
fromargs.CliError(message)for exit code2. Passexit_code=nfor a code from2to255.CliErrorrejects any other code with an error that is neitherValueErrornorTypeError. Cyclopts reports those as bad input. Exit0means success, and exit1means an unexpected exception. - Use
fromargs.contract_error(exc, context=...)to wrap a caught exception at exit code3. A Cyclopts parse error reports at exit code2. - A converter or validator that raises
ValueError,TypeError, orAssertionErroris a usage error at exit code2. Any other exception from a converter, a validator, the handler, or the result serialization reports at exit code1. Its envelope adds atracebackkey with the path of a temporary file.fromargskeeps the file, so the caller can read it after the process exits. sys.exit(n)sets the exit status, andrun()catches it while it parses, runs the handler, and prints the result.0orNoneexits0with no output. Anintfrom1to255reports an error envelope at that exit code. Anintoutside that range reportsexited with status <code>at exit code1. Any othersys.exitvalue reports its text at exit code1.KeyboardInterruptreports{"error": "interrupted"}at exit code130.- A quote-split repair (below) prints one plain-text
note:line on stderr; it never changes stdout or the exit code.
Global flags
fromargs strips two flags from argv before Cyclopts ever sees them, from
anywhere before the end-of-options marker:
--jsonis a no-op. Agents that append it by habit get plain JSON either way, so the flag costs nothing and fails nothing.--fullturns off result truncation for the current call.
Neither flag reaches a handler, and neither is a real Cyclopts option.
The rule is literal. Every bare --json or --full token before the marker
is global, even where a parameter would take a hyphen-leading value (for
example Parameter(allow_leading_hyphen=True)). To pass one of these tokens
as data, put it after the marker (-- by default): wrap jq -- --json x.
The --full=1 and --json=true forms are not global flags. Cyclopts
rejects them as unknown options.
limit
@app.command(limit=n) truncates a sequence or set result to its first n
items, unless the caller passes --full. A set truncates after sorting.
Truncation prints a note: line on stderr and never applies to a mapping or
a string. App.default accepts the same limit keyword.
App.default
@app.default (bare or called, matching @app.command) registers the
handler that runs when argv names no subcommand at that app or group level.
It is rejected at registration if it declares a json or full parameter,
the same rule @app.command enforces. Registering a second default on the
same app or group raises ValueError.
Self-healing
An agent's shell layer sometimes merges two arguments into one quoted token,
for example --weeks "2 --dry-run" instead of --weeks 2 --dry-run. When
Cyclopts rejects an argv, fromargs shell-splits each option's value once
and re-parses. It applies a split only when:
- the split has at least two pieces, and one looks like a flag; and
- the option can take a split value (not a boolean flag, not free-text
strorPath); and - exactly one split candidate among all options parses cleanly.
It prints note: split quoted argument ... into ... on stderr when it
applies a repair. fromargs refuses to guess when a split is ambiguous
(more than one candidate parses), when the option takes free text, or for
any token after the end-of-options marker (-- by default). In every
refusal case, the original parse error is reported unchanged.
Version resolution
fromargs.App(name) reports the version of the calling module, not the
version of fromargs itself. It resolves, in order:
- an explicit
version=argument, if the caller passes one; importlib.metadata.version(...)for the caller's installed distribution;- the caller module's
__version__attribute; "0.0.0", if none of the above resolve.
App.group(name, version=..., **cyclopts_kwargs) forwards every extra
keyword, including version, to the nested cyclopts.App, so a group can
report its own version independently of the root app.
Cyclopts options
App, App.group, and App.command forward keyword arguments to Cyclopts.
run() owns error output, the exit status, and the result. So these three
raise ValueError for a Cyclopts option that run() never honors:
error_formatter, result_action, suppress_keyboard_interrupt,
print_error, exit_on_error, help_on_error, and error_console.
Metadata
Release files for fromargs 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fromargs-0.2.0.tar.gz | 44.7 kB | Without provenance | Details for fromargs-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fromargs-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for fromargs-0.2.0-py3-none-any.whl |
Total release size: 62.2 kB
Release files / fromargs-0.2.0.tar.gz
| Download URL | fromargs-0.2.0.tar.gz |
|---|---|
| Size | 44.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9467253d82079143d221586035903be33e7a133bc9870bc391f5e04599ef7e77
|
|
BLAKE2b-256 checksum How to use checksums |
12dd0c37cb959dbe9b8f9888a72ff2d0814d3a3aed02306909dfd3cc6d2e88cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fromargs-0.2.0-py3-none-any.whl
| Download URL | fromargs-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
62ef933618c8568f9b36a0a41b95d275ec98d4493a6aeebb93b97cdc4c517f54
|
|
BLAKE2b-256 checksum How to use checksums |
f14023776d8376234fddbe0b1c830aeaa16b1b72e8a2a2178892c01237755fc4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for fromargs 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.