Metadata-Version: 2.4
Name: rincorpes-limen
Version: 0.1.1
Summary: CLI framework
License-File: LICENSE
Author: Santiago Rincón
Author-email: rincorpes@gmail.com
Requires-Python: >=3.10,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Provides-Extra: dev
Provides-Extra: docs
Requires-Dist: black (>=24.10,<25.0) ; extra == "dev"
Requires-Dist: furo (>=2024.8,<2025.0) ; extra == "docs"
Requires-Dist: isort (>=5.13,<6.0) ; extra == "dev"
Requires-Dist: mypy (>=1.5,<2.0) ; extra == "dev"
Requires-Dist: myst-parser (>=3.0,<4.0) ; extra == "docs"
Requires-Dist: pylint (>=3.3,<4.0) ; extra == "dev"
Requires-Dist: pytest (>=8.3,<9.0) ; extra == "dev"
Requires-Dist: pytest-cov (>=6.0,<7.0) ; extra == "dev"
Requires-Dist: sphinx (>=7.4,<8.0) ; extra == "docs"
Requires-Dist: sphinx-copybutton (>=0.5,<1.0) ; extra == "docs"
Requires-Dist: sphinx-design (>=0.6,<1.0) ; extra == "docs"
Requires-Dist: tomli (>=2.0,<3.0) ; (python_version < "3.11") and (extra == "docs")
Description-Content-Type: text/markdown

# Limen

> Small Python framework for building structured command line applications.

[![License](https://img.shields.io/github/license/rincorpes/limen)](./LICENSE)
[![PyPI](https://img.shields.io/pypi/v/limen)](https://pypi.org/project/limen/)
[![Docs](https://img.shields.io/badge/docs-in%20repo-blue)](./docs)

Limen provides a lightweight foundation for Python CLIs that need more
structure than a single `argparse` file, but do not want a full application
framework. It gives you a command registry, nested command groups, global flag
handling, reusable parser factories, and a small execution runner that keeps
entrypoint wiring explicit.

The package is aimed at internal tooling, reusable developer CLIs, and
multi-command applications that want a clear boundary between:

- command discovery and registration
- parser construction
- command execution
- process-level concerns such as `--version`, `-v`, and module loading

## Why Limen

Many Python CLIs start as a single script with an `argparse.ArgumentParser`,
then gradually accumulate nested subcommands, reusable flags, dynamic imports,
and command-specific validation. At that point, the parser logic, entrypoint
logic, and command behavior usually become tightly coupled.

Limen gives that growth path a small framework:

- `CommandRegistry` stores command classes with alias support
- `BaseCLIApp` turns registered commands into an `argparse` tree
- `BaseCommand` provides a consistent command execution lifecycle
- `GlobalParserBuilder` defines reusable global flags such as `--version`,
  `-v`, and `--loc`
- `run_cli()` coordinates parsing, global handlers, command loading, and app
  startup in one place

## Highlights

- Nested commands are modeled explicitly through `parent` and `is_group`.
- Registry-backed command lookup is normalized and alias-aware.
- Command arguments are described with `ArgumentType` instead of ad hoc parser
  code spread across modules.
- Boolean flags, environment-backed defaults, JSON coercion, and dashed option
  aliases are supported out of the box.
- Global flags are parsed before the application command tree is built, which
  keeps bootstrap behavior predictable.

## Package layout

The package is intentionally split into small modules by responsibility:

- `limen.base_command` defines the command lifecycle and execution hooks.
- `limen.registry` stores command classes and resolves aliases and parent-child
  relationships.
- `limen.app` builds the parser tree and dispatches commands.
- `limen.runner` coordinates top-level startup flow.
- `limen.global_parser_builder` provides reusable process-wide flags.
- `limen.argument_type`, `limen.config`, and `limen.parser_factory` describe
  parser configuration primitives.

## Installation

Install from PyPI:

```bash
pip install limen
```

For local development with docs and tests:

```bash
poetry install --extras dev --extras docs
```

## Example

```python
from __future__ import annotations

import argparse

from limen.app import BaseCLIApp
from limen.base_command import BaseCommand
from limen.config import CLIConfig
from limen.registry import CommandRegistry
from limen.runner import run_cli


class ProjectCLI(BaseCLIApp):
    def __init__(self, config: CLIConfig):
        super().__init__(config)
        self.build_commands()


@CommandRegistry.implementation("workspace")
class WorkspaceCommand(BaseCommand):
    is_group = True
    summary = "Workspace-level commands."


@CommandRegistry.implementation("workspace_info")
class WorkspaceInfoCommand(BaseCommand):
    name = "info"
    parent = "workspace"
    summary = "Show workspace metadata."

    def execute(self, **_kwargs) -> int:
        print("workspace info")
        return 0


def create_config(global_parser: argparse.ArgumentParser) -> CLIConfig:
    return CLIConfig(
        app_name="project",
        description="Example CLI powered by limen.",
        usage="%(prog)s [options] <command> [<args>]",
        formatter_class=global_parser.formatter_class,
        parents=[global_parser],
    )


def main(argv: list[str] | None = None) -> int:
    return run_cli(
        version="0.1.0",
        app_factory=ProjectCLI,
        config_factory=create_config,
        argv=argv,
    )
```

That gives you commands like:

```bash
project workspace info
project --version
project -vv workspace info
```

## Documentation

Build the local documentation site with:

```bash
poetry run sphinx-build -n -W --keep-going -b html docs/source docs/_build/html
```

## License

This project is distributed under the MIT License. See [LICENSE](./LICENSE) for
the full text.

## Contact

Santiago Rincón  
Email: [rincorpes@gmail.com](mailto:rincorpes@gmail.com)  
GitHub: [@rincorpes](https://github.com/rincorpes)

