{"id":15645264,"url":"https://github.com/eonu/feud","last_synced_at":"2025-09-09T18:21:47.047Z","repository":{"id":210398583,"uuid":"726459178","full_name":"eonu/feud","owner":"eonu","description":"Build powerful CLIs with simple idiomatic Python, driven by type hints. Not all arguments are bad.","archived":false,"fork":false,"pushed_at":"2024-12-30T20:02:30.000Z","size":2555,"stargazers_count":60,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-30T02:08:40.280Z","etag":null,"topics":["cli","click","command-line","docstrings","pydantic","python","terminal","typed","typehints"],"latest_commit_sha":null,"homepage":"https://feud.readthedocs.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eonu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-02T13:14:26.000Z","updated_at":"2025-02-01T19:00:56.000Z","dependencies_parsed_at":"2024-10-03T12:09:44.702Z","dependency_job_id":"688cb020-8dac-4e46-a5a3-5ec043eb63d7","html_url":"https://github.com/eonu/feud","commit_stats":{"total_commits":123,"total_committers":3,"mean_commits":41.0,"dds":0.1707317073170732,"last_synced_commit":"ae3a38cd61d6e5c5f943fb91a5ed229ee2bdbf30"},"previous_names":["eonu/feud"],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eonu%2Ffeud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eonu%2Ffeud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eonu%2Ffeud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eonu%2Ffeud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eonu","download_url":"https://codeload.github.com/eonu/feud/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247427006,"owners_count":20937201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","click","command-line","docstrings","pydantic","python","terminal","typed","typehints"],"created_at":"2024-10-03T12:05:30.597Z","updated_at":"2025-04-06T03:11:10.129Z","avatar_url":"https://github.com/eonu.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003e\n    Feud\n  \u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\u003cb\u003eNot all arguments are bad.\u003c/b\u003e\u003c/p\u003e\n\u003c/p\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/eonu/feud/master/docs/source/_static/images/logo/logo.png\" align=\"right\" width=\"100px\"\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eBuild powerful CLIs with simple idiomatic Python, driven by type hints.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://pypi.org/project/feud\"\u003e\n      \u003cimg src=\"https://img.shields.io/pypi/v/feud?logo=pypi\u0026style=flat-square\" alt=\"PyPI\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://pypi.org/project/feud\"\u003e\n      \u003cimg src=\"https://img.shields.io/pypi/pyversions/feud?logo=python\u0026style=flat-square\" alt=\"PyPI - Python Version\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://feud.readthedocs.io/en/latest\"\u003e\n      \u003cimg src=\"https://img.shields.io/readthedocs/feud.svg?logo=read-the-docs\u0026style=flat-square\" alt=\"Read The Docs - Documentation\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://coveralls.io/github/eonu/feud\"\u003e\n      \u003cimg src=\"https://img.shields.io/coverallsCoverage/github/eonu/feud?logo=coveralls\u0026style=flat-square\" alt=\"Coveralls - Coverage\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://raw.githubusercontent.com/eonu/feud/master/LICENSE\"\u003e\n      \u003cimg src=\"https://img.shields.io/pypi/l/feud?style=flat-square\" alt=\"PyPI - License\"/\u003e\n    \u003c/a\u003e\n  \u003c/div\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003csup\u003e\n    \u003ca href=\"#about\"\u003eAbout\u003c/a\u003e ·\n    \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e ·\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e ·\n    \u003ca href=\"#build-status\"\u003eBuild status\u003c/a\u003e ·\n    \u003ca href=\"#documentation\"\u003eDocumentation\u003c/a\u003e ·\n    \u003ca href=\"#related-projects\"\u003eRelated projects\u003c/a\u003e ·\n    \u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e ·\n    \u003ca href=\"#licensing\"\u003eLicensing\u003c/a\u003e\n  \u003c/sup\u003e\n\u003c/p\u003e\n\n## About\n\nDesigning a _good_ CLI can quickly spiral into chaos without the help of\nan intuitive CLI framework.\n\n**Feud builds on [Click](https://click.palletsprojects.com/en/8.1.x/) for\nargument parsing, along with [Pydantic](https://docs.pydantic.dev/latest/)\nfor typing, to make CLI building a breeze.**\n\n## Features\n\n### Simplicity\n\nClick is often considered the defacto command-line building utility for Python –\noffering far more functionality and better ease-of-use than the standard\nlibrary's [`argparse`](https://docs.python.org/3/library/argparse.html).\nDespite this, for even the simplest of CLIs, code written using Click can be\nsomewhat verbose and often requires frequently looking up documentation.\n\nConsider the following example command for serving local files on a HTTP server.\n\n**In red is a typical Click implementation, and in green is the Feud equivalent.**\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**Example**: Command for running a HTTP web server.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```diff\n# serve.py\n\n- import click\n+ import feud\n+ from typing import Literal\n\n- @click.command\n- @click.argument(\"port\", type=int, help=\"Server port.\")\n- @click.option(\"--watch/--no-watch\", type=bool, default=True, help=\"Watch source code for changes.\")\n- @click.option(\"--env\", type=click.Choice([\"dev\", \"prod\"]), default=\"dev\", help=\"Environment mode.\")\n- def serve(port, watch, env):\n+ def serve(port: int, *, watch: bool = True, env: Literal[\"dev\", \"prod\"] = \"dev\"):\n-     \"\"\"Start a local HTTP server.\"\"\"\n+     \"\"\"Start a local HTTP server.\n+\n+     Parameters\n+     ----------\n+     port:\n+         Server port.\n+     watch:\n+         Watch source code for changes.\n+     env:\n+         Environment mode.\n+     \"\"\"\n\nif __name__ == \"__main__\":\n-     serve()\n+     feud.run(serve)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to view the generated help screen.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\nHelp screen for the `serve` command.\n\n```console\n$ python serve.py --help\n\n Usage: serve.py [OPTIONS] PORT\n\n Start a local HTTP server.\n\n╭─ Arguments ────────────────────────────────────────────────────────╮\n│ *  PORT    INTEGER  [required]                                     │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --watch/--no-watch                Watch source code for changes.   │\n│                                   [default: watch]                 │\n│ --env                 [dev|prod]  Environment mode. [default: dev] │\n│ --help                            Show this message and exit.      │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to see usage examples.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\n- `python serve.py 8080`\n- `python serve.py 3000 --watch --env dev`\n- `python serve.py 4567 --no-watch --env prod`\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nThe core design principle behind Feud is to make it as easy as possible\nfor even beginner Python developers to quickly create sophisticated CLIs.\n\nThe above function is written in idiomatic Python, adhering to language\nstandards and using basic core language features such as type hints and\ndocstrings to declare the relevant information about the CLI, but relying \non Feud to carry out the heavy lifting of converting these language elements \ninto a fully-fledged CLI.\n\n#### Grouping commands\n\nWhile a single command is often all that you need, Feud makes it\nstraightforward to logically group together related commands into a _group_\nrepresented by a class with commands defined within it.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**Example**: Commands for creating, deleting and listing blog posts.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\n# post.py\n\nimport feud\nfrom datetime import date\n\nclass Post(feud.Group):\n    \"\"\"Manage blog posts.\"\"\"\n\n    def create(id: int, *, title: str, desc: str | None = None):\n        \"\"\"Create a blog post.\"\"\"\n\n    def delete(*ids: int):\n        \"\"\"Delete blog posts.\"\"\"\n\n    def list(*, between: tuple[date, date] | None = None):\n        \"\"\"View all blog posts, optionally filtering by date range.\"\"\"\n\nif __name__ == \"__main__\":\n    feud.run(Post)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to view the generated help screen.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\nHelp screen for the `post` group.\n\n```console\n$ python post.py --help\n\n Usage: post.py [OPTIONS] COMMAND [ARGS]...\n\n Manage blog posts.\n\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --help      Show this message and exit.                            │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Commands ─────────────────────────────────────────────────────────╮\n│ create   Create a blog post.                                       │\n│ delete   Delete blog posts.                                        │\n│ list     View all blog posts, optionally filtering by date range.  │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\nHelp screen for the `list` command within the `post` group.\n\n```console\n$ python post.py list --help\n\n Usage: post.py list [OPTIONS]\n\n View all blog posts, optionally filtering by date range.\n\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --between    \u003cDATE DATE\u003e...                                        │\n│ --help                       Show this message and exit.           │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to see usage examples.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\n- `python post.py create 1 --title \"My First Post\"`\n- `python post.py create 2 --title \"My First Detailed Post\" --desc \"Hi!\"`\n- `python post.py delete 1 2`\n- `python post.py list`\n- `python post.py list --between 2020-01-30 2021-01-30`\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\nAlternatively, if you already have some functions defined that you would like\nto run as commands, you can simply provide them to `feud.run` and it will\nautomatically generate and run a group with those commands.\n\n```python\n# post.py\n\nimport feud\nfrom datetime import date\n\ndef create_post(id: int, *, title: str, desc: str | None = None):\n    \"\"\"Create a blog post.\"\"\"\n\ndef delete_posts(*ids: int):\n    \"\"\"Delete blog posts.\"\"\"\n\ndef list_posts(*, between: tuple[date, date] | None = None):\n    \"\"\"View all blog posts, optionally filtering by date range.\"\"\"\n\nif __name__ == \"__main__\":\n    feud.run([create_post, delete_posts, list_posts])\n```\n\nYou can also use a `dict` to rename the generated commands:\n\n```python\nfeud.run({\"create\": create_post, \"delete\": delete_posts, \"list\": list_posts})\n```\n\nFor more complex applications, you can also nest commands in sub-groups:\n\n```python\nfeud.run({\"list\": list_posts, \"modify\": [create_post, delete_posts]})\n```\n\nIf commands are defined in another module, you can also\nrun the module directly and Feud will pick up all runnable objects:\n\n```python\nimport post\n\nfeud.run(post)\n```\n\nYou can even call `feud.run()` without providing any object, and it will\nautomatically discover all runnable objects in the current module.\n\n_As you can see, building a CLI using Feud does not require learning many new\nmagic methods or a domain-specific language – you can just use the simple\nPython you know and ❤️!_\n\n#### Registering command sub-groups\n\nGroups can be registered as sub-groups under other groups. This is a common\npattern in CLIs, allowing for interfaces packed with lots of functionality,\nbut still organized in a sensible way.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**Example**: CLI with the following structure for running and managing a blog.\n\n- **`blog`**: Group to manage and serve a blog.\n  - `serve`: Command to run the blog HTTP server.\n  - **`post`**: Sub-group to manage blog posts.\n    - `create`: Command to create a blog post.\n    - `delete`: Command to delete blog posts.\n    - `list`: Command to view all blog posts.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\n# blog.py\n\nimport feud\nfrom datetime import date\nfrom typing import Literal\n\nclass Blog(feud.Group):\n    \"\"\"Manage and serve a blog.\"\"\"\n\n    def serve(port: int, *, watch: bool = True, env: Literal[\"dev\", \"prod\"] = \"dev\"):\n        \"\"\"Start a local HTTP server.\"\"\"\n\nclass Post(feud.Group):\n    \"\"\"Manage blog posts.\"\"\"\n\n    def create(id: int, *, title: str, desc: str | None = None):\n        \"\"\"Create a blog post.\"\"\"\n\n    def delete(*ids: int):\n        \"\"\"Delete blog posts.\"\"\"\n\n    def list(*, between: tuple[date, date] | None = None):\n        \"\"\"View all blog posts, optionally filtering by date range.\"\"\"\n\nBlog.register(Post)\n\nif __name__ == \"__main__\":\n    feud.run(Blog)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to view the generated help screen.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\nHelp screen for the `blog` group.\n\n```console\n$ python blog.py --help\n\n Usage: blog.py [OPTIONS] COMMAND [ARGS]...\n\n Manage and serve a blog.\n\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --help      Show this message and exit.                            │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Command groups ───────────────────────────────────────────────────╮\n│ post         Manage blog posts.                                    │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Commands ─────────────────────────────────────────────────────────╮\n│ serve        Start a local HTTP server.                            │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\nHelp screen for the `serve` command in the `blog` group.\n\n```console\n$ python blog.py serve --help\n\n Usage: blog.py serve [OPTIONS] PORT\n\n Start a local HTTP server.\n\n╭─ Arguments ────────────────────────────────────────────────────────╮\n│ *  PORT    INTEGER  [required]                                     │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --watch/--no-watch                [default: watch]                 │\n│ --env                 [dev|prod]  [default: dev]                   │\n│ --help                            Show this message and exit.      │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\nHelp screen for the `post` sub-group in the `blog` group.\n\n```console\n$ python blog.py post --help\n\n Usage: blog.py post [OPTIONS] COMMAND [ARGS]...\n\n Manage blog posts.\n\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --help      Show this message and exit.                            │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Commands ─────────────────────────────────────────────────────────╮\n│ create   Create a blog post.                                       │\n│ delete   Delete blog posts.                                        │\n│ list     View all blog posts, optionally filtering by date range.  │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\nHelp screen for the `list` command within the `post` sub-group.\n\n```console\n$ python blog.py post list --help\n\n Usage: blog.py post list [OPTIONS]\n\n View all blog posts, optionally filtering by date range.\n\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ --between    \u003cDATE DATE\u003e...                                        │\n│ --help                       Show this message and exit.           │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to see usage examples.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\n- `python blog.py serve 8080 --no-watch --env prod`\n- `python blog.py post create 1 --title \"My First Post!\"`\n- `python blog.py post list --between 2020-01-30 2021-01-30`\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n### Powerful typing\n\nFeud is powered by [Pydantic](https://docs.pydantic.dev/latest/) – a\nvalidation library with extensive support for many data types, including:\n\n- simple types such as integers and dates,\n- complex types such as emails, IP addresses, file/directory paths, database\n  connection strings,\n- constrained types (e.g. positive/negative integers or past/future dates).\n\n[`pydantic-extra-types`](https://github.com/pydantic/pydantic-extra-types) is\nan optional dependency offering additional types such as country names,\npayment card numbers, phone numbers, colours, latitude/longitude and more.\n\nCustom annotated types with user-defined validation functions can also be\ndefined with Pydantic.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**Example**: Command for generating audio samples from text prompts using\na machine learning model, and storing produced audio files in an output\ndirectory.\n\n- **At least one** text prompt must be provided.\n- **No more than five** text prompts can be provided.\n- Each text prompt can have a **maximum of 12 characters**.\n- The model is specified by a path to a **file that must exist**.\n- The output directory is a path to a **folder that must exist**.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\n# generate.py\n\nimport feud\nfrom pydantic import FilePath, DirectoryPath, conlist, constr\n\ndef generate(\n    prompts: conlist(constr(max_length=12), min_length=1, max_length=5),\n    *,\n    model: FilePath,\n    output: DirectoryPath,\n):\n    \"\"\"Generates audio from prompts using a trained model.\"\"\"\n\nif __name__ == \"__main__\":\n    feud.run(generate)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to view the generated help screen.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\nHelp screen for the `generate` command.\n\n```console\n$ python generate.py --help\n\n Usage: generate.py [OPTIONS] [PROMPTS]...\n\n Generates audio from prompts using a trained model.\n\n╭─ Arguments ────────────────────────────────────────────────────────╮\n│ PROMPTS    TEXT                                                    │\n╰────────────────────────────────────────────────────────────────────╯\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ *  --model     FILE       [required]                               │\n│ *  --output    DIRECTORY  [required]                               │\n│    --help                 Show this message and exit.              │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to see usage examples.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\nIf we run the script without prompts, we get an error that at least one prompt\nmust be provided.\n\n```console\n$ python generate.py --model models/real_model.pt --output audio/\n\n Usage: generate.py [OPTIONS] [PROMPTS]...\n\n Try 'generate.py --help' for help\n╭─ Error ──────────────────────────────────────────────────────────────────────╮\n│ 1 validation error for command 'generate'                                    │\n│ [PROMPTS]...                                                                 │\n│   List should have at least 1 item after validation, not 0 [input_value=()]  │\n╰──────────────────────────────────────────────────────────────────────────────╯\n```\n\nIf we provide a prompt longer than 12 characters, we also get an error.\n\n```console\n$ python generate.py \"dog barking\" \"cat meowing\" \"fish blubbing\" --model models/real_model.pt --output audio/\n\n Usage: generate.py [OPTIONS] [PROMPTS]...\n\n Try 'generate.py --help' for help\n╭─ Error ──────────────────────────────────────────────────────────────────────╮\n│ 1 validation error for command 'generate'                                    │\n│ [PROMPTS]... [2]                                                             │\n│   String should have at most 12 characters [input_value='fish blubbing']     │\n╰──────────────────────────────────────────────────────────────────────────────╯\n```\n\n`FilePath` indicates that the file must already exist, so we get an error if we\nprovide a non-existent file.\n\n```console\n$ python generate.py \"dog barking\" \"cat meowing\" --model models/fake_model.pt\n\n Usage: generate.py [OPTIONS] [PROMPTS]...\n\n Try 'generate.py --help' for help\n╭─ Error ──────────────────────────────────────────────────────────────────────╮\n│ Invalid value for '--model': File 'models/fake_model.pt' does not exist.     │\n╰──────────────────────────────────────────────────────────────────────────────╯\n```\n\n`DirectoryPath` indicates that the path must be a directory, so we\nget an error if we provide a file.\n\n```console\n$ python generate.py \"dog barking\" \"cat meowing\" --output audio.txt\n\n Usage: generate.py [OPTIONS] [PROMPTS]...\n\n Try 'generate.py --help' for help\n╭─ Error ──────────────────────────────────────────────────────────────────────╮\n│ Invalid value for '--output': Directory 'audio.txt' is a file.               │\n╰──────────────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n_By relying on Pydantic to handle the hard work of validation, we can contain all\nof the required CLI constraints in a simple function signature, leaving you to focus\non the important part – implementing your commands._\n\n### Highly configurable and extensible\n\nWhile designed to be simpler than Click, this comes with the trade-off that\nFeud is also more opinionated than Click and only directly implements a subset\nof its functionality.\n\nHowever, Feud was designed to allow for Click to seamlessly slot in whenever\nmanual overrides are necessary.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n**Example**: Use [`click.password_option`](https://click.palletsprojects.com/en/8.1.x/api/#click.password_option)\nto securely prompt the user for a password, but still validate based on the\ntype hint (length should be ≥ 10 characters).\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```python\n# login.py\n\nimport feud\nfrom feud import click\nfrom pydantic import constr\n\n@click.password_option(\"--password\", help=\"The user's password (≥ 10 characters).\")\ndef login(*, username: str, password: constr(min_length=10)):\n    \"\"\"Log in as a user.\n\n    Parameters\n    ----------\n    username:\n        The user's username.\n    \"\"\"\n\nif __name__ == \"__main__\":\n    feud.run(login)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to view the generated help screen.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\nHelp screen for the `login` command.\n\n```console\n$ python login.py --help\n\n Usage: login.py [OPTIONS]\n\n Log in as a user.\n\n╭─ Options ──────────────────────────────────────────────────────────╮\n│ *  --username    TEXT  The user's username. [required]             │\n│    --password    TEXT  The user's password (≥ 10 characters).      │\n│    --help              Show this message and exit.                 │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cb\u003eClick here to see usage examples.\u003c/b\u003e\n  \u003c/summary\u003e\n\u003cp\u003e\n\n```console\n$ python login.py --username alice\n\nPassword: ***\nRepeat for confirmation: ***\n\n Usage: login.py [OPTIONS]\n\n Try 'login.py --help' for help\n╭─ Error ────────────────────────────────────────────────────────────╮\n│ 1 validation error for command 'login'                             │\n│ --password                                                         │\n│   String should have at least 10 characters [input_value=hidden]   │\n╰────────────────────────────────────────────────────────────────────╯\n```\n\n\u003c/p\u003e\n\u003c/detail\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n### Integrations\n\nAs Feud commands and groups compile to Click objects under the hood, \nthis opens up the ability to interact with all integrations that Click\nsupports.\n\n- To convert a `feud.Group` into a `click.Group`, use the `.compile()` method defined on `feud.Group`.\n- The `@feud.command` decorator converts a function into a `click.Command`.\n\nOnce you have a `click.Command` or `click.Group` produced by Feud,\nit is possible to use it with Click extensions such as:\n\n- [`click-man`](https://github.com/click-contrib/click-man): Automate generation of manual pages for Click applications.\n- [`click-completion`](https://github.com/click-contrib/click-completion): Add or enhance `bash`, `fish`, `zsh` and `powershell` completion in Click.\n- [`sphinx-click`](https://github.com/click-contrib/sphinx-click): A Sphinx plugin to automatically document Click-based applications.\n\nFor more examples of Click extensions, see the [`click-contrib`](https://github.com/click-contrib/) project.\n\n## Installation\n\nYou can install Feud using `pip`.\n\nThe latest stable version of Feud can be installed with the following command.\n\n```console\npip install \"feud[all]\"\n```\n\nThis installs Feud with the optional dependencies:\n\n- [`rich-click`](https://github.com/ewels/rich-click) (can install individually with `pip install \"feud[rich]\"`)\u003cbr/\u003e\n  _Provides improved formatting for CLIs produced by Feud._\n- [`pydantic-extra-types`](https://github.com/pydantic/pydantic-extra-types) (can install individually with `pip install \"feud[extra-types]\"`)\u003cbr/\u003e\n  _Provides additional types that can be used as type hints for Feud commands._\n- [`email-validator`](https://github.com/JoshData/python-email-validator) (can install individually with `pip install \"feud[email]\"`)\u003cbr/\u003e\n  _Provides Pydantic support for email validation._\n\nTo install Feud without any optional dependencies, simply run `pip install feud`.\n\n### Improved formatting with Rich\n\nBelow is a comparison of Feud with and without `rich-click`.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003e\nWith Rich-formatted output\n\u003c/th\u003e\n\u003cth\u003e\nWithout Rich-formatted output\n\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cimg src=\"/docs/source/_static/images/readme/help-rich.png\"/\u003e\n\u003c/td\u003e\n\u003ctd\u003e\n\u003cimg src=\"/docs/source/_static/images/readme/help-no-rich.png\"/\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cimg src=\"/docs/source/_static/images/readme/error-rich.png\"/\u003e\n\u003c/td\u003e\n\u003ctd\u003e\n\u003cimg src=\"/docs/source/_static/images/readme/error-no-rich.png\"/\u003e\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Build status\n\n| `master`                                                                                                                                                                                       | `dev`                                                                                                                                                                                            |\n| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| [![CircleCI Build (Master)](https://img.shields.io/circleci/build/github/eonu/feud/master?logo=circleci\u0026style=flat-square)](https://app.circleci.com/pipelines/github/eonu/feud?branch=master) | [![CircleCI Build (Development)](https://img.shields.io/circleci/build/github/eonu/feud/dev?logo=circleci\u0026style=flat-square)](https://app.circleci.com/pipelines/github/eonu/feud?branch=master) |\n\n## Documentation\n\n- [API reference](https://feud.readthedocs.io):\n  Library documentation for public modules, classes and functions.\n\n\u003c!--\n- [Official website](https://feud.wiki):\n  High level information about the package.\n- [User guide](https://feud.wiki/guide):\n  Detailed walkthrough of features, with examples of both simple and complex\n  usage patterns.\n--\u003e\n\n## Related projects\n\nFeud either relies heavily on, or was inspired by the following\npackages. It would be greatly appreciated if you also supported the below\nmaintainers and the work they have done that Feud has built upon.\n\n\u003ctable\u003e\n\n\u003ctr\u003e\n  \u003cth\u003eProject\u003c/th\u003e\n  \u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n[**Click**](https://github.com/pallets/click)\n\n\u003csup\u003e\n\nby\u0026nbsp;[@pallets](https://github.com/pallets)\n\n\u003c/sup\u003e\n  \n\u003c/td\u003e\n\u003ctd\u003e\n\nFeud is essentially a wrapper around Click that takes classes and functions\nwith type hints and intelligently 'compiles' them into a ready-to-use Click\ngenerated CLI.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n[**Rich Click**](https://github.com/ewels/rich-click)\n\n\u003csup\u003e\n\nby\u0026nbsp;[@ewels](https://github.com/ewels)\n\n\u003c/sup\u003e\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nA shim around Click that renders help output nicely using\n[Rich](https://github.com/Textualize/rich).\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n[**Pydantic**](https://github.com/pydantic/pydantic)\n\n\u003csup\u003e\n\nby\u0026nbsp;[@samuelcolvin](https://github.com/samuelcolvin)\n\n\u003c/sup\u003e\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nPydantic is a validation package that makes it easy to declaratively validate\ninput data based on type hints.\n\nThe package offers support for common standard library types, plus more complex\ntypes which can also be used as type hints in Feud commands for input validation.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n[**Typer**](https://github.com/tiangolo/typer)\n\n\u003csup\u003e\n\nby\u0026nbsp;[@tiangolo](https://github.com/tiangolo)\n\n\u003c/sup\u003e\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nTyper shares a similar ideology to Feud, in that building CLIs should be\nsimple and not require learning new functions or constantly referring to\nlibrary documentation. Typer is also based on Click.\n\nTyper is a more complete library for building CLIs overall, but currently\nlacks support for more complex types such as those offered by Pydantic.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n[**Thor**](https://github.com/rails/thor)\n\n\u003csup\u003e\n\nby\u0026nbsp;[@rails](https://github.com/rails)\n\n\u003c/sup\u003e\n\n\u003c/td\u003e\n\u003ctd\u003e\n\nThough not a Python package, the highly object-oriented design of Thor (a CLI\nbuilding package in Ruby) – in particular the use of classes to define command\ngroups – greatly influenced the implementation of the `feud.Group` class.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n## Contributing\n\nAll contributions to this repository are greatly appreciated. Contribution guidelines can be found [here](/CONTRIBUTING.md).\n\n\u003e \u003cimg src=\"https://i.postimg.cc/jq3MZSTD/avatar.png\" align=\"left\"/\u003e\n\u003e \u003cb\u003eWe're living in an imperfect world!\u003c/b\u003e\u003cbr/\u003e\n\u003e \u003csup\u003eFeud is still in a test phase, likely with \u003cem\u003elots\u003c/em\u003e of bugs. Please \u003ca href=\"https://github.com/eonu/feud/issues/new/choose\"\u003eleave feedback\u003c/a\u003e if you come across anything strange!\u003c/sup\u003e\n\n## Licensing\n\nFeud is released under the [MIT](https://opensource.org/licenses/MIT) license.\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eFeud\u003c/b\u003e \u0026copy; 2023, Edwin Onuonga - Released under the \u003ca href=\"https://opensource.org/licenses/MIT\"\u003eMIT\u003c/a\u003e license.\u003cbr/\u003e\n  \u003cem\u003eAuthored and maintained by Edwin Onuonga.\u003c/em\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feonu%2Ffeud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feonu%2Ffeud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feonu%2Ffeud/lists"}