Skip to content

Refactor arglite to use explicit flag declarations instead of source-code reflection - #5

Merged
dluman merged 2 commits into
mainfrom
api-approach
Jul 30, 2026
Merged

Refactor arglite to use explicit flag declarations instead of source-code reflection#5
dluman merged 2 commits into
mainfrom
api-approach

Conversation

@dluman

@dluman dluman commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

This PR replaces arglite's fragile source-code reflection approach with a small, explicit declaration API. The old parser flattened sys.argv into a string and regex-scanned the caller's source file to determine which flags were required or optional. That approach broke on aliases, dynamic access, from arglite import parser, and any non-trivial code structure.

The new implementation parses sys.argv directly as tokens, requires users to declare their flags explicitly, and handles the edge cases that were previously broken.

New API

import arglite

arglite.parser.require("name", type=str)
arglite.parser.optional("count", default=1, type=int)
arglite.parser.flag("verbose")

print(arglite.parser.name)
print(arglite.parser.count)
print(arglite.parser.verbose)
  • parser.require(name, short=None, type=None)
  • parser.optional(name, short=None, default=None, type=None)
  • parser.flag(name, short=None)

If short is omitted, the first letter of the flag name is used automatically as a short alias (e.g. --name can also be -n). Flags are accessed directly on the parser instance, not through parser.required / parser.optional namespaces.

Edge cases now handled

  • --flag value, --flag=value, -f value, and -f=value
  • Values containing spaces (via normal shell quoting)
  • Empty string values: --name ""
  • Negative numbers as values: --offset -5
  • Explicit type conversion with clear error messages
  • Boolean flags including --verbose=false
  • Unknown flags, missing required flags, and failed conversions exit with code 1
  • ast.literal_eval for structured literals when no explicit type is given

Files changed

  • src/arglite/arglite.pyParser orchestrates declarations, validation, and lazy access
  • src/arglite/flag.pyFlag descriptor, value conversion, and boolean normalization
  • src/arglite/parse.py — stateless tokenize(argv, flags) function
  • src/arglite/help.py — Rich help table rendering
  • src/arglite/exceptions.pyParseError and RequirementError
  • src/arglite/__init__.py — updated exports
  • src/arglite/code.py — deleted
  • src/arglite/argument.py — deleted
  • pyproject.toml — removed vurze and runtime twine; added requires-python and dev dependencies
  • README.md — rewritten for the new API
  • .github/workflows/main.yml — replaced vurze checks with pytest
  • tests/conftest.py — shared parser fixture
  • tests/test_flag.py — tests for Flag conversion
  • tests/test_parse.py — tests for tokenization
  • tests/test_parser.py — tests for Parser orchestration
  • tests/test_help.py — tests for help output

Testing

uv run --with pytest pytest tests/ -v

All 69 tests pass. The package also builds cleanly with uv build.

Breaking changes

This is a major API change. Existing code that relied on parser.required.foo, parser.optional.foo, or implicit reflection will need to be updated to declare flags explicitly and access them as parser.foo.

@dluman
dluman merged commit 5e838ee into main Jul 30, 2026
1 check passed
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