drycli


Namedrycli JSON
Version 0.0.3 PyPI version JSON
download
home_pageNone
SummaryDRY (Don't Repeat Yourself) CLI - Define your CLI once with Pydantic models
upload_time2025-09-04 22:05:46
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords cli pydantic click dry configuration type-safe
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # drycli

**DRY (Don't Repeat Yourself) CLI** - Define your CLI once with Pydantic models.

[![PyPI version](https://badge.fury.io/py/drycli.svg)](https://badge.fury.io/py/drycli)
[![Python versions](https://img.shields.io/pypi/pyversions/drycli.svg)](https://pypi.org/project/drycli/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## ๐Ÿšง Under Development

This package is currently being developed. The namespace is reserved and the initial release is coming soon!

## What is drycli?

`drycli` is a modern Python library that eliminates repetition in CLI development. Define your configuration schema **once** using Pydantic models and automatically get:

โœ… **Type-safe command-line interfaces** - Full IDE support with autocomplete
โœ… **Automatic validation** - Helpful error messages with constraint checking
โœ… **Environment variable support** - Auto-discovery and type conversion
โœ… **Configuration file loading** - JSON/YAML with proper precedence
โœ… **Value source tracking** - Know where each setting came from
โœ… **Auto-generated help text** - Documentation from your model
โœ… **Constraint integration** - Pydantic validators become CLI help
โœ… **Zero boilerplate** - No duplicate definitions

## Preview: How It Will Work

```python
from typing import Annotated
from pydantic import Field
import click
from drycli import DryModel, click_config, AutoOption

class ServerConfig(DryModel):
    """Configuration for the server."""

    # This creates --port with validation and help text automatically
    port: Annotated[int, AutoOption] = Field(
        default=8000,
        ge=1,
        le=65535,
        description="Port number to bind to"
    )

    # This creates --host
    host: Annotated[str, AutoOption] = Field(
        default="localhost",
        description="Host address to bind to"
    )

    # This creates --debug/--no-debug flag
    debug: Annotated[bool, AutoOption] = Field(
        default=False,
        description="Enable debug mode"
    )

# Just one decorator and you get everything!
@click.command()
@click_config(ServerConfig)
def serve(config: ServerConfig) -> None:
    """Start the server with the given configuration."""
    print(f"Starting server on {config.host}:{config.port}")
    if config.debug:
        print("Debug mode enabled")

    # config is fully typed - your IDE knows all the fields!
    # You also get source tracking:
    # print(f"Port from: {config.source.port}")  # "cli", "env", "config", or "default"

if __name__ == "__main__":
    serve()
```

This single definition gives you:

- `--port` option with integer validation (1-65535)
- `--host` option with string type
- `--debug/--no-debug` boolean flag
- `--config` option for JSON/YAML files
- `--help` with auto-generated documentation
- Environment variables: `MYAPP_PORT`, `MYAPP_HOST`, `MYAPP_DEBUG`
- Full type safety and IDE support

## The Problem drycli Solves

**Before** (traditional approach):

```python
# Define your data model
class ServerConfig:
    def __init__(self, port: int, host: str, debug: bool):
        self.port = port
        self.host = host
        self.debug = debug

# Define your CLI (duplicate information!)
@click.command()
@click.option('--port', type=int, default=8000, help='Port number')
@click.option('--host', type=str, default='localhost', help='Host address')
@click.option('--debug/--no-debug', default=False, help='Debug mode')
def serve(port: int, host: str, debug: bool):
    config = ServerConfig(port, host, debug)  # Manual construction
    # No validation, no environment variables, no config files...
```

**After** (with drycli):

```python
class ServerConfig(DryModel):
    port: Annotated[int, AutoOption] = Field(default=8000, ge=1, le=65535, description="Port number")
    host: Annotated[str, AutoOption] = Field(default="localhost", description="Host address")
    debug: Annotated[bool, AutoOption] = Field(default=False, description="Debug mode")

@click.command()
@click_config(ServerConfig)
def serve(config: ServerConfig):  # Fully typed, automatically constructed!
    # Everything just works - validation, env vars, config files, help text
```

## Why Choose drycli?

- **๐ŸŽฏ DRY Principle**: Define once, use everywhere
- **๐Ÿ”’ Type Safety**: Full mypy and IDE support
- **โšก Zero Boilerplate**: No duplicate CLI definitions
- **๐ŸŒ Environment Ready**: Automatic env var support
- **๐Ÿ“ Config Files**: JSON/YAML loading with precedence
- **๐Ÿ” Source Tracking**: Know where values came from
- **๐Ÿ“š Self-Documenting**: Help text from your models
- **โœ… Validation**: Pydantic's powerful validation built-in

## Comparison with Existing Tools

| Feature | drycli | pydanclick | typer | argparse + pydantic |
|---------|--------|------------|-------|---------------------|
| Type Safety | โœ… | โœ… | โœ… | โš ๏ธ Manual |
| Zero Duplication | โœ… | โœ… | โŒ | โŒ |
| Environment Variables | โœ… | โŒ | โŒ | โš ๏ธ Manual |
| Config Files | โœ… | โŒ | โŒ | โš ๏ธ Manual |
| Source Tracking | โœ… | โŒ | โŒ | โŒ |
| Constraint Integration | โœ… | โš ๏ธ Limited | โŒ | โš ๏ธ Manual |
| Auto Help Generation | โœ… | โœ… | โœ… | โš ๏ธ Manual |

## Coming Soon

- ๐Ÿ“ฆ **Initial Release** - Core functionality
- ๐Ÿ“– **Full Documentation** - Comprehensive guides and API reference
- ๐Ÿงช **Examples** - Real-world usage patterns
- ๐Ÿ”Œ **Plugins** - Extensions for popular frameworks
- ๐ŸŽจ **Rich Integration** - Beautiful terminal output
- ๐Ÿ“Š **Advanced Features** - Nested configs, custom validators, and more

## Development Status

- [x] Namespace reserved on PyPI
- [x] API design finalized
- [ ] Core implementation
- [ ] Test suite
- [ ] Documentation
- [ ] Examples
- [ ] Initial release

## Stay Updated

- โญ **Star this repo** to get notified of updates
- ๐Ÿ‘€ **Watch releases** for the latest versions
- ๐Ÿ› **Open issues** for feature requests or bug reports

## Contributing

Interested in contributing? Great! The package is in early development and contributions are welcome.

## License

MIT License - see [LICENSE](LICENSE) file for details.

---

**drycli** - Because your CLI definitions should be as DRY as your code! ๐Ÿœ๏ธ
# Trigger build

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "drycli",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "cli, pydantic, click, dry, configuration, type-safe",
    "author": null,
    "author_email": "Tyler House <26489166+tahouse@users.noreply.github.com>",
    "download_url": "https://files.pythonhosted.org/packages/ee/41/c91d88b12f579d13df42fa94935f0d5332588cced129463266b42e888bd5/drycli-0.0.3.tar.gz",
    "platform": null,
    "description": "# drycli\n\n**DRY (Don't Repeat Yourself) CLI** - Define your CLI once with Pydantic models.\n\n[![PyPI version](https://badge.fury.io/py/drycli.svg)](https://badge.fury.io/py/drycli)\n[![Python versions](https://img.shields.io/pypi/pyversions/drycli.svg)](https://pypi.org/project/drycli/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## \ud83d\udea7 Under Development\n\nThis package is currently being developed. The namespace is reserved and the initial release is coming soon!\n\n## What is drycli?\n\n`drycli` is a modern Python library that eliminates repetition in CLI development. Define your configuration schema **once** using Pydantic models and automatically get:\n\n\u2705 **Type-safe command-line interfaces** - Full IDE support with autocomplete\n\u2705 **Automatic validation** - Helpful error messages with constraint checking\n\u2705 **Environment variable support** - Auto-discovery and type conversion\n\u2705 **Configuration file loading** - JSON/YAML with proper precedence\n\u2705 **Value source tracking** - Know where each setting came from\n\u2705 **Auto-generated help text** - Documentation from your model\n\u2705 **Constraint integration** - Pydantic validators become CLI help\n\u2705 **Zero boilerplate** - No duplicate definitions\n\n## Preview: How It Will Work\n\n```python\nfrom typing import Annotated\nfrom pydantic import Field\nimport click\nfrom drycli import DryModel, click_config, AutoOption\n\nclass ServerConfig(DryModel):\n    \"\"\"Configuration for the server.\"\"\"\n\n    # This creates --port with validation and help text automatically\n    port: Annotated[int, AutoOption] = Field(\n        default=8000,\n        ge=1,\n        le=65535,\n        description=\"Port number to bind to\"\n    )\n\n    # This creates --host\n    host: Annotated[str, AutoOption] = Field(\n        default=\"localhost\",\n        description=\"Host address to bind to\"\n    )\n\n    # This creates --debug/--no-debug flag\n    debug: Annotated[bool, AutoOption] = Field(\n        default=False,\n        description=\"Enable debug mode\"\n    )\n\n# Just one decorator and you get everything!\n@click.command()\n@click_config(ServerConfig)\ndef serve(config: ServerConfig) -> None:\n    \"\"\"Start the server with the given configuration.\"\"\"\n    print(f\"Starting server on {config.host}:{config.port}\")\n    if config.debug:\n        print(\"Debug mode enabled\")\n\n    # config is fully typed - your IDE knows all the fields!\n    # You also get source tracking:\n    # print(f\"Port from: {config.source.port}\")  # \"cli\", \"env\", \"config\", or \"default\"\n\nif __name__ == \"__main__\":\n    serve()\n```\n\nThis single definition gives you:\n\n- `--port` option with integer validation (1-65535)\n- `--host` option with string type\n- `--debug/--no-debug` boolean flag\n- `--config` option for JSON/YAML files\n- `--help` with auto-generated documentation\n- Environment variables: `MYAPP_PORT`, `MYAPP_HOST`, `MYAPP_DEBUG`\n- Full type safety and IDE support\n\n## The Problem drycli Solves\n\n**Before** (traditional approach):\n\n```python\n# Define your data model\nclass ServerConfig:\n    def __init__(self, port: int, host: str, debug: bool):\n        self.port = port\n        self.host = host\n        self.debug = debug\n\n# Define your CLI (duplicate information!)\n@click.command()\n@click.option('--port', type=int, default=8000, help='Port number')\n@click.option('--host', type=str, default='localhost', help='Host address')\n@click.option('--debug/--no-debug', default=False, help='Debug mode')\ndef serve(port: int, host: str, debug: bool):\n    config = ServerConfig(port, host, debug)  # Manual construction\n    # No validation, no environment variables, no config files...\n```\n\n**After** (with drycli):\n\n```python\nclass ServerConfig(DryModel):\n    port: Annotated[int, AutoOption] = Field(default=8000, ge=1, le=65535, description=\"Port number\")\n    host: Annotated[str, AutoOption] = Field(default=\"localhost\", description=\"Host address\")\n    debug: Annotated[bool, AutoOption] = Field(default=False, description=\"Debug mode\")\n\n@click.command()\n@click_config(ServerConfig)\ndef serve(config: ServerConfig):  # Fully typed, automatically constructed!\n    # Everything just works - validation, env vars, config files, help text\n```\n\n## Why Choose drycli?\n\n- **\ud83c\udfaf DRY Principle**: Define once, use everywhere\n- **\ud83d\udd12 Type Safety**: Full mypy and IDE support\n- **\u26a1 Zero Boilerplate**: No duplicate CLI definitions\n- **\ud83c\udf0d Environment Ready**: Automatic env var support\n- **\ud83d\udcc1 Config Files**: JSON/YAML loading with precedence\n- **\ud83d\udd0d Source Tracking**: Know where values came from\n- **\ud83d\udcda Self-Documenting**: Help text from your models\n- **\u2705 Validation**: Pydantic's powerful validation built-in\n\n## Comparison with Existing Tools\n\n| Feature | drycli | pydanclick | typer | argparse + pydantic |\n|---------|--------|------------|-------|---------------------|\n| Type Safety | \u2705 | \u2705 | \u2705 | \u26a0\ufe0f Manual |\n| Zero Duplication | \u2705 | \u2705 | \u274c | \u274c |\n| Environment Variables | \u2705 | \u274c | \u274c | \u26a0\ufe0f Manual |\n| Config Files | \u2705 | \u274c | \u274c | \u26a0\ufe0f Manual |\n| Source Tracking | \u2705 | \u274c | \u274c | \u274c |\n| Constraint Integration | \u2705 | \u26a0\ufe0f Limited | \u274c | \u26a0\ufe0f Manual |\n| Auto Help Generation | \u2705 | \u2705 | \u2705 | \u26a0\ufe0f Manual |\n\n## Coming Soon\n\n- \ud83d\udce6 **Initial Release** - Core functionality\n- \ud83d\udcd6 **Full Documentation** - Comprehensive guides and API reference\n- \ud83e\uddea **Examples** - Real-world usage patterns\n- \ud83d\udd0c **Plugins** - Extensions for popular frameworks\n- \ud83c\udfa8 **Rich Integration** - Beautiful terminal output\n- \ud83d\udcca **Advanced Features** - Nested configs, custom validators, and more\n\n## Development Status\n\n- [x] Namespace reserved on PyPI\n- [x] API design finalized\n- [ ] Core implementation\n- [ ] Test suite\n- [ ] Documentation\n- [ ] Examples\n- [ ] Initial release\n\n## Stay Updated\n\n- \u2b50 **Star this repo** to get notified of updates\n- \ud83d\udc40 **Watch releases** for the latest versions\n- \ud83d\udc1b **Open issues** for feature requests or bug reports\n\n## Contributing\n\nInterested in contributing? Great! The package is in early development and contributions are welcome.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n---\n\n**drycli** - Because your CLI definitions should be as DRY as your code! \ud83c\udfdc\ufe0f\n# Trigger build\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "DRY (Don't Repeat Yourself) CLI - Define your CLI once with Pydantic models",
    "version": "0.0.3",
    "project_urls": {
        "Documentation": "https://github.com/tahouse/drycli#readme",
        "Homepage": "https://github.com/tahouse/drycli",
        "Issues": "https://github.com/tahouse/drycli/issues",
        "Repository": "https://github.com/tahouse/drycli"
    },
    "split_keywords": [
        "cli",
        " pydantic",
        " click",
        " dry",
        " configuration",
        " type-safe"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c215029aadd240e318c06f5a712866e27951c1a8a587593184599845ab8b2725",
                "md5": "e8cd83c20938b86fda2d167403dc5743",
                "sha256": "37f07e1a5d3862258ee1158d1759a2eb40eb360eb09b22ccbce9ee30dc6fd27c"
            },
            "downloads": -1,
            "filename": "drycli-0.0.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e8cd83c20938b86fda2d167403dc5743",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 7462,
            "upload_time": "2025-09-04T22:05:42",
            "upload_time_iso_8601": "2025-09-04T22:05:42.236347Z",
            "url": "https://files.pythonhosted.org/packages/c2/15/029aadd240e318c06f5a712866e27951c1a8a587593184599845ab8b2725/drycli-0.0.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ee41c91d88b12f579d13df42fa94935f0d5332588cced129463266b42e888bd5",
                "md5": "351a5fd6490cd60a40a79b70740311e3",
                "sha256": "5f86efebd922590ae6b62aebb8c4261e701f46563275cc82e25cf1dccc9e3503"
            },
            "downloads": -1,
            "filename": "drycli-0.0.3.tar.gz",
            "has_sig": false,
            "md5_digest": "351a5fd6490cd60a40a79b70740311e3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 13378,
            "upload_time": "2025-09-04T22:05:46",
            "upload_time_iso_8601": "2025-09-04T22:05:46.731077Z",
            "url": "https://files.pythonhosted.org/packages/ee/41/c91d88b12f579d13df42fa94935f0d5332588cced129463266b42e888bd5/drycli-0.0.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-04 22:05:46",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "tahouse",
    "github_project": "drycli#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "drycli"
}
        
Elapsed time: 1.47592s