Metadata-Version: 2.4
Name: clibind
Version: 0.0.1
Summary: Bridge an argument parser and python callables seamlessly.
Author: r8763754383405076
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# The "clibind" Package

## Introduction

The `clibind` package provides comprehensive utilities that link CLI arguments from an `argparse.ArgumentParser` directly to Python callables (functions or classes). The core operational steps include:
1. Automatically configuring an argument parser based on the parameter signatures of a callable.
2. Building a valid keyword arguments (`kwargs`) dictionary from parsed CLI inputs to dynamically execute the target callable.

---

## Preparation

To ensure `clibind` correctly parses parameter definitions, each tracked argument in your callable must meet at least one of the following criteria:
- Be assigned a explicit default value (other than `None`).
- Have an explicit Python type annotation.

### Supported Types
Currently, only the following core primitives are supported:
- `int`
- `float`
- `bool`
- `str`

*(Note: `Optional[T]` or `T | None` definitions are perfectly supported for the primitive types above).*

### Basic Decorator Configuration
Wrap your target callable object using the `@setup_clibind()` decorator. `setup_clibind` accepts two optional parameters:
- **`help_messages`** *(dict[str, str])*: Map explicit argument names to terminal help string messages.
- **`ignore_patterns`** *(list[str])*: Regex patterns matching arguments that should be skipped by the parser generator. By default, `"self"`, `"cls"`, and any variable names ending with an underscore (`_`) are automatically filtered out.

---

## Important Constraints

When designing your callables for `clibind`, keep the following intentional design constraints in mind:

1. **Parameter Name Collisions**: If you map multiple callables to a single `argparse.ArgumentParser` instance (as shown in the application pattern below), you must ensure that their parameter names do not collide. Registering overlapping argument names to the same parser instance will cause `argparse` to raise an `ArgumentError`. It is the user's responsibility to maintain unique parameter names across combined callables.
2. **Mandatory Boolean Arguments**: Any parameter annotated with the `bool` type that lacks an explicit default value will automatically default to `False` and configure the parser with `action='store_true'`. This design choice ensures seamless integration with standard CLI toggle mechanics. If a strictly required boolean choice is needed, handle it via alternative validation or explicit defaults.

---

## Usage Workflow

- **`setup_clibind`**: Attaches internal tracking metadata and analyzes target function/method definitions. If a class object is decorated, its `__init__` constructor method is evaluated.
- **`callable_to_parser`**: Registers tracked arguments safely onto a provided `argparse.ArgumentParser` instance.
- **`parser_to_callable`**: Extracts the structured keyword arguments (`kwargs`) matching the component signatures from the parsed execution context.

---

## Example

```python
import argparse
import typing
from clibind import setup_clibind, callable_to_parser, parser_to_callable

@setup_clibind(
    help_messages={
        "var_1": "help message for var_1",
        "var_a": "help message for var_a",
        "var_f": "help message for var_f"
    }
)
class TargetClass:
    def __init__(
        self,
        var_to_skip_1_: list,
        var_to_skip_2_: list,
        var_1: int,
        var_2: float,
        var_3: str,
        var_a=1,
        var_b=1.1,
        var_c="",
        var_d1: int | None = None,
        var_d2: typing.Optional[int] = None,
        var_e=True,
        var_f=False,
        var_to_skip_3_=""
    ):
        pass

# Initialize parser instance
parser = argparse.ArgumentParser()
callable_to_parser(obj=TargetClass, parser=parser)

# View generated help documentation
parser.print_help()
```

## Generated Layout

```
usage: main.py [-h] --var-1 INT --var-2 FLOAT --var-3 STR
                       [--var-a INT] [--var-b FLOAT] [--var-c STR]
                       [--var-d1 INT] [--var-d2 INT] [--var-e-toggle]
                       [--var-f-toggle]

options:
  -h, --help           show this help message and exit

arguments for "__main__.TargetClass":
  --var-1 INT          help message for var_1
  --var-2 FLOAT
  --var-3 STR
  --var-a INT          (default: 1) help message for var_a
  --var-b FLOAT        (default: 1.1)
  --var-c STR          (default: '')
  --var-d1 INT         (default: None)
  --var-d2 INT         (default: None)
  --var-e-toggle       (default: True)
  --var-f-toggle       (default: False) help message for var_f
```

## Production / Application Pattern

In extensive systems (such as machine learning training pipelines), this architecture cleans up modular execution flows:

```python
# main.py
import argparse
import experiments.train

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    parser_train = subparsers.add_parser("train")
    experiments.train.setup(parser_train)
    parser_train.set_defaults(func=experiments.train.handle)


# experiments/train.py
from clibind import setup_clibind, callable_to_parser, parser_to_callable

def setup(parser):
    callable_to_parser(read_data, parser)
    callable_to_parser(run, parser)

def handle(args):
    data_reader_args = parser_to_callable(args, read_data)
    run_args = parser_to_callable(args, run)
    
    # Safely forward mapped variables 
    run(data_reader_args_=data_reader_args, **run_args)

@setup_clibind()
def run(data_reader_args_: dict, epochs: int = 10):
    train_ds, val_ds = read_data(**data_reader_args_)
    # Train execution logic...

@setup_clibind()
def read_data(dataset_dir: str):
    # Data extraction logic...
    pass
```
