{"id":31778100,"url":"https://github.com/candy-kingdom/mints","last_synced_at":"2025-10-10T06:29:11.640Z","repository":{"id":57441888,"uuid":"216380894","full_name":"candy-kingdom/mints","owner":"candy-kingdom","description":"Clean and elegant CLI development kit.","archived":false,"fork":false,"pushed_at":"2020-09-17T12:30:26.000Z","size":252,"stargazers_count":3,"open_issues_count":19,"forks_count":1,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-09-25T09:34:38.499Z","etag":null,"topics":["cli","python"],"latest_commit_sha":null,"homepage":"","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/candy-kingdom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-20T15:10:00.000Z","updated_at":"2024-01-28T04:31:12.000Z","dependencies_parsed_at":"2022-09-26T17:20:55.800Z","dependency_job_id":null,"html_url":"https://github.com/candy-kingdom/mints","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/candy-kingdom/mints","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candy-kingdom%2Fmints","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candy-kingdom%2Fmints/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candy-kingdom%2Fmints/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candy-kingdom%2Fmints/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/candy-kingdom","download_url":"https://codeload.github.com/candy-kingdom/mints/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candy-kingdom%2Fmints/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002960,"owners_count":26083489,"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","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","python"],"created_at":"2025-10-10T06:29:07.965Z","updated_at":"2025-10-10T06:29:11.634Z","avatar_url":"https://github.com/candy-kingdom.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mints\n\n[![PyPI version](https://badge.fury.io/py/mints.svg)](https://badge.fury.io/py/mints)\n[![Build](https://github.com/candy-kingdom/mints/workflows/Build/badge.svg)](https://github.com/candy-kingdom/mints/actions)\n\n_Clean and elegant CLI development kit._\n\n## Overview\n\nMints is a microframework that allows building declarative and nice-looking CLI apps.\nUnlike [Click](https://click.palletsprojects.com/en/7.x/) or [Plac](https://micheles.github.io/plac/), it utilizes [function annotations](https://www.python.org/dev/peps/pep-3107/) more than [decorators](https://www.python.org/dev/peps/pep-0318/).\n\nHere is a quick example:\n```py\n# say.py\n\nfrom mints import cli, Arg, Flag, Opt\n\n@cli\ndef say(phrase: Arg('a phrase to print'),\n        caps:   Flag('whether to print phrase in upper-case'),\n        times:  Opt[int]('how many times to print') = 1):\n    \"\"\"Prints a phrase specified number of times.\"\"\"\n\n    for _ in range(times):\n        print(phrase.upper() if caps else phrase)\n\nif __name__ == '__main__':\n    cli()\n```\n\nAnd what we get in the command line:\n```\n$ python3 say.py \"Hi!\" \nHi!\n```\n```\n$ python3 say.py \"Hi!\" --caps\nHI!\n```\n```\n$ python3 say.py \"Hi!\" --times 3\nHi!\nHi!\nHi!\n```\n```\n$ python3 say.py --help\nusage: say [-h] [--caps] [--times TIMES] phrase\n\nPrints a phrase specified number of times.\n\npositional arguments:\n  phrase         a phrase to print\n\noptional arguments:\n  -h, --help     show this help message and exit\n  --caps         whether to print phrase in upper-case\n  --times TIMES  how many times to print\n```\n\n## Install\n\n```\n$ pip install mints\n```\n\n## Getting started\n\n_Note: the examples are not [PEP 8](https://www.python.org/dev/peps/pep-0008/#blank-lines) compatible: one blank line is used instead of two to separate top-level definitions._\n\nIn general, writing a CLI app is very similar to writing a regular function.\nThis is also true for Mints.\n\nConsider the following example:\n```py\n# say.py\n\nfrom mints import cli, Arg\n\n@cli\ndef say(phrase: Arg):\n    print(phrase)\n\nif __name__ == '__main__':\n    cli()\n```\n\nThe script can be executed as a command-line app:\n```\n$ python3 say.py \"Hello, world!\"\nHello, world!\n```\n\nThe main idea is very simple: you use the `cli` decorator to wrap a function that acts as an entry point of the application (`say` function from the example above), and then call the `cli()` to make things running.\n\nIn the next sections you'll find out how to build more complex apps in Mints.\n\n### Parameters\n\n#### `Arg`\n\n`Arg` is an annotation for positional arguments.\n\nPositional arguments in CLIs work in the same way as in programming languages.\n\nConsider the following function:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(x: Arg, y: Arg):\n    print(x, y)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1 2\n1 2\n```\n\nNote: it's not possible to execute the script without an argument.\n```\n$ python test.py 1\nusage: test [-h] x y\ntest: error: the following arguments are required: y\n```\n\nTo address this issue, you could provide a default value to the argument:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(x: Arg, y: Arg = 2):\n    print(x, y)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1\n1 2\n```\n\n#### `Flag`\n\n`Flag` is an annotation for flags.\n\nFlags are boolean arguments that represent an on/off behavior.\nUnlike positional arguments, they should be specified in the command line only with a special syntax.\n\nHere is an example of a flag:\n```py\n# test.py\n\nfrom mints import cli, Flag\n\n@cli\ndef test(some: Flag):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py --some\nTrue\n```\n```\n$ python test.py\nFalse\n```\n\n#### `Opt`\n\n`Opt` is an annotation for options.\n\nOptions are simply flags with values (or arguments with names).\n\nThat's how you use the `Opt`:\n```py\n# test.py\n\nfrom mints import cli, Opt\n\n@cli\ndef test(some: Opt):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py --some 1\n1\n```\n\nNote: it's not possible to _not_ specify the option by default, as it was for flags.\n```\n$ python test.py --some\nusage: test [-h] --some SOME\ntest: error: the following arguments are required: --some\n```\n\nYou still could provide a default value:\n```py\n# test.py\n\nfrom mints import cli, Opt\n\n@cli\ndef test(some: Opt = 1):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py\n1\n```\n\n### Help page\n\nEach CLI in Mints has a built-in help page, which is automatically generated.\n\nConsider the following example:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(some: Arg):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py --help\nusage: test [-h] some \n\npositional arguments:\n  some\n\noptional arguments:\n  -h, --help  show this help message and exit\n```\n\nNote the lack of the program description as well as the `some` argument description.\n\nTo override the description of the program, put a simple doc-comment to a CLI function.\nTo assign a description to an argument, instantiate an annotation with the `description` argument (it always comes first).\n\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(some: Arg('some argument')):\n    \"\"\"A simple demonstration program.\"\"\"\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py --help\nusage: test [-h] some \n\nA simple demonstration program.\n\npositional arguments:\n  some        some argument\n\noptional arguments:\n  -h, --help  show this help message and exit\n```\n\n### Short name\n\nUsually, both flags and options come with a shortcut syntax.\nFor example, instead of writing:\n```\n$ python test.py --some 1\n```\n\nOne could write:\n```\n$ python test.py -s 1\n```\n\nTo define a shortcut letter for a flag or an option, the `short` parameter of either `Flag` or `Opt` should be used:\n```py\n# test.py\n\nfrom mints import cli, Flag\n\n@cli\ndef test(some: Flag(short='s')):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py -s\nTrue\n```\n\n### Prefix\n\nFlags and options are usually called with the `-` prefix (in short and long variations).\nTo override this behavior, the `prefix` parameter of either `Flag` or `Opt` should be used.\n```py\n# test.py\n\nfrom mints import cli, Flag\n\n@cli\ndef test(some: Flag(prefix='+')):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py ++some\nTrue\n```\n\n### Types\n\nBy default, an argument that is passed from the CLI is of `str` type if it's annotated with either `Opt` or `Arg`, and of `bool` if it's annotated with `Flag`.\n\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(some: Arg):\n    print(type(some))\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1\n\u003cclass 'str'\u003e\n```\n\n#### Default types\n\nTo parse a primitive type that is supported by the [`argparse`](https://docs.python.org/3/library/argparse.html#type), use the following syntax:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(some: Arg[int]):\n    print(type(some))\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1\n\u003cclass 'int'\u003e\n```\n\n#### User-defined types\n\nTo parse a custom type, register a parser function just for that.\n\nYou could use either the `parse` decorator:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n# User-defined type.\nclass Custom:\n    def __init__(self, x):\n        self.property = x\n\n# A parser for user-defined type.\n@cli.parse\ndef custom(x: str) -\u003e Custom:\n    return Custom(x)\n      \n@cli\ndef test(some: Arg[Custom]):\n    print(some.property)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1\n1\n```\n\nOr the `add_parser` function:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\nclass Custom:        \n    def __init__(self, x):\n        self.property = x\n\n@cli\ndef test(some: Arg[Custom]):\n    print(some.property)\n\nif __name__ == '__main__':\n    cli.add_parser(Custom)\n    cli()\n```\n```\n$ python test.py 1\n1\n```\n\n### Variable arguments\n\nVariable arguments are also supported through the standard `List` type:\n```py\n# test.py\n\nfrom typing import List\n\nfrom mints import cli, Arg\n\n@cli\ndef test(some: Arg[List[int]]):\n    print(some)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1 2 3\n[1, 2, 3]\n```\n\nNote that lists are _non-greedy_:\n```py\n# test.py\n\nfrom mints import cli, Arg\n\n@cli\ndef test(x: Arg[int], y: Arg[List[int]], z: Arg[int]):\n    print(x, y, z)\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python test.py 1 2 3 4\n1 [2, 3] 4\n```\n\nConsider checking the [rolling dices](https://github.com/candy-kingdom/cli/blob/develop/examples/roll.py) example with a more realistic use case.\n\n### Commands\n\nComplex command line interfaces like `git` have several subcommands, e.g., `git status`, `git pull`, `git push`, etc.\nThese subcommands act as separate CLIs and, thus, should be defined as separate functions in Mints.\n\nConsider the following example as a mock of `git` CLI:\n```py\n# git.py\n\nfrom mints import cli, Flag\n\n@cli\ndef git():\n    ...\n\n@git.command\ndef pull(rebase: Flag):\n    if rebase:\n        print('pulling with rebase')\n    else:\n        print('pulling')\n\n@git.command\ndef push():\n    print('pushing')\n\nif __name__ == '__main__':\n    cli()\n```\n```\n$ python git.py pull\npulling\n```\n```\n$ python git.py pull --rebase\npulling with rebase\n```\n```\n$ python git.py push\npushing\n```\n\nSometimes it's needed to have a deeper hierarchy of subcommands.\nFor example, the `dotnet` CLI tool allows calling [`dotnet tool install ...`](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install).\n\nIn Mints, this could be implemented in a natural way:\n```py\n# dotnet.py\n\nfrom mints import cli\n\n@cli\ndef dotnet():\n    ...\n\n@dotnet.command\ndef tool():\n    ...\n\n@tool.command\ndef install():\n    ...\n\nif __name__ == '__main__':\n    cli()\n```\n\n## Learn more\n\nLearn more by looking at our carefully prepared [examples](https://github.com/candy-kingdom/mints/blob/master/examples/).\n\n## License\n\nThe package is licensed under the [MIT](https://github.com/candy-kingdom/mints/blob/master/LICENSE) license.\n\n## Contributing\n\nBefore creating an issue or submitting a patch, check out our [contribution guildelines](https://github.com/candy-kingdom/mints/blob/master/CONTRIBUTING).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcandy-kingdom%2Fmints","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcandy-kingdom%2Fmints","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcandy-kingdom%2Fmints/lists"}