Name | drycli JSON |
Version |
0.0.3
JSON |
| download |
home_page | None |
Summary | DRY (Don't Repeat Yourself) CLI - Define your CLI once with Pydantic models |
upload_time | 2025-09-04 22:05:46 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | MIT |
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.
[](https://badge.fury.io/py/drycli)
[](https://pypi.org/project/drycli/)
[](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[](https://badge.fury.io/py/drycli)\n[](https://pypi.org/project/drycli/)\n[](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"
}