{"id":20630789,"url":"https://github.com/relastle/glacier","last_synced_at":"2025-05-10T23:26:33.217Z","repository":{"id":42086210,"uuid":"289718806","full_name":"relastle/glacier","owner":"relastle","description":":snowflake:  Building Python CLI using docstrings and typehints :snake:","archived":false,"fork":false,"pushed_at":"2025-04-08T04:25:48.000Z","size":134,"stargazers_count":95,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-08T04:34:27.529Z","etag":null,"topics":["asyncio","cli","command-line-tool","docstring","library","productivity","python","python-cli","python3"],"latest_commit_sha":null,"homepage":"https://github.com/relastle/glacier","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/relastle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-23T15:57:42.000Z","updated_at":"2025-04-08T04:25:44.000Z","dependencies_parsed_at":"2025-01-03T05:20:22.342Z","dependency_job_id":"cafcd3fc-615b-4970-8dce-30fd8eb5cf12","html_url":"https://github.com/relastle/glacier","commit_stats":{"total_commits":58,"total_committers":4,"mean_commits":14.5,"dds":0.3448275862068966,"last_synced_commit":"7463fee8044c4e5ef19f946899ef551e37dbaf3e"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/relastle%2Fglacier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/relastle%2Fglacier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/relastle%2Fglacier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/relastle%2Fglacier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/relastle","download_url":"https://codeload.github.com/relastle/glacier/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253481471,"owners_count":21915323,"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":["asyncio","cli","command-line-tool","docstring","library","productivity","python","python-cli","python3"],"created_at":"2024-11-16T14:09:33.046Z","updated_at":"2025-05-10T23:26:33.198Z","avatar_url":"https://github.com/relastle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![image](https://user-images.githubusercontent.com/6816040/91929753-0fe7bf00-ed1a-11ea-8c95-793e2a20fd07.png)\n\n# glacier\n\nglacier is a python CLI building library for minimalists.\n\n\u003ca href=\"https://pypi.org/project/glacier/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/glacier?color=395c81\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/glacier/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/pyversions/glacier?color=395c81\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/glacier/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/l/glacier?color=395c81\"/\u003e\u003c/a\u003e\n\n\u003ca href=\"https://github.com/relastle/glacier/actions?query=workflow%3Apythontests\"\u003e\u003cimg src=\"https://github.com/relastle/glacier/workflows/pythontests/badge.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/relastle/glacier/actions?query=workflow%3Apypi-publish\"\u003e\u003cimg src=\"https://github.com/relastle/glacier/workflows/pypi-publish/badge.svg\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://app.fossa.com/projects/git%2Bgithub.com%2Frelastle%2Fglacier?ref=badge_shield\"\u003e\u003cimg src=\"https://app.fossa.com/api/projects/git%2Bgithub.com%2Frelastle%2Fglacier.svg?type=shield\"/\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/relastle/glacier\"\u003e\u003cimg src=\"https://codecov.io/gh/relastle/glacier/branch/main/graph/badge.svg?token=IBCYZODDY3\"/\u003e\u003c/a\u003e\n\n* [glacier](#glacier)\n  * [Installation](#installation)\n  * [Quick start](#quick-start)\n  * [Basic Usage](#basic-usage)\n     * [CLI without subcommand](#cli-without-subcommand)\n     * [CLI with subcommands](#cli-with-subcommands)\n        * [Pass a list of functions](#pass-a-list-of-functions)\n        * [Pass a dictionary of functions](#pass-a-dictionary-of-functions)\n     * [Async entrypoint support](#async-entrypoint-support)\n     * [Positional argument](#positional-argument)\n     * [Options](#options)\n     * [Default value for optional argument](#default-value-for-optional-argument)\n     * [Help with docstring](#help-with-docstring)\n        * [Google Style](#google-style)\n        * [Numpy Style](#numpy-style)\n        * [reStructuredText Style](#restructuredtext-style)\n     * [Supported types](#supported-types)\n  * [Note](#note)\n     * [Philosophy](#apple-philosophy)\n     * [Warnings](#construction-warnings)\n  * [Related works](#related-works)\n  * [\u003ca href=\"./LICENSE\"\u003eLICENSE\u003c/a\u003e](#license)\n\n## Installation\n\n```python\npip install glacier\n```\n\n## Quick start\n\nYou only have to call `glacier` against the entrypoint function.\n\n```python\nfrom glacier import glacier\n\n\ndef main(name: str, verbose: bool = False) -\u003e None:\n    pass\n\n\nif __name__ == '__main__':\n    glacier(main)\n```\n\nThen, you can see help 🍰.\n\n![quick start help](https://user-images.githubusercontent.com/6816040/92337363-fd87cf80-f0e3-11ea-8902-d0488fbd8547.png)\n\n\n## Basic Usage\n\n### CLI without subcommand\n\nIf you just call `glacier` to a function, it will invoke it as stand-alone CLI\n(like the example in [Quick start](https://github.com/relastle/glacier#quick-start)).\n\n### CLI with subcommands\n\nYou can easily construct CLI with subcommands in the following two ways.\n\n#### Pass a list of functions\n\n```python\nfrom glacier import glacier\n\n\ndef run(name: str, verbose: bool = False) -\u003e None:\n    \"\"\" Run \"\"\"\n    pass\n\n\ndef build(name: str, verbose: bool = False) -\u003e None:\n    \"\"\" Build \"\"\"\n    pass\n\n\ndef test(name: str, verbose: bool = False) -\u003e None:\n    \"\"\" Test \"\"\"\n    return\n\n\nif __name__ == '__main__':\n    glacier([run, build, test])\n```\n\nIf you passes a lift of function, glacier constructs the CLI with subcommands whose names are the same as the declared function names.\nIn this example, the subcommans will be `run`,  `build`, and `test`.\n\n\n![sub commands help](https://user-images.githubusercontent.com/6816040/92397064-108cb500-f161-11ea-9cb2-0f0a1c4da2f5.png)\n\n\n#### Pass a dictionary of functions\n\nYou can easily give the different name as the subcommand name from any declared name of the function.\nJust give a dictionary (key will be a subcommand name).\n\n\n```python\nfrom glacier import glacier\n\n\ndef f1(name: str, verbose: bool = False) -\u003e None:\n    pass\n\n\ndef f2(name: str, verbose: bool = False) -\u003e None:\n    pass\n\n\ndef f3(name: str, verbose: bool = False) -\u003e None:\n    pass\n\n\nif __name__ == '__main__':\n    glacier({\n        'run': f1,\n        'build': f2,\n        'test': f3,\n    })\n```\n\nThis works exactly the same as the previous example.\n\nThis interface makes it very easy to build a simple CLI tool from an existing project.\n\n### Async entrypoint support\n\nYou sometimes want your async function to be a CLI entrypoint.\nOnly you have to do is just passing the async function as if it were `sync` function.\n\nThe example below combine two async functions and a sync function into CLI\nwith nested subcommand structure.\n\n\n```python\nfrom glacier import glacier\n\n\nasync def main() -\u003e None:\n    return\n\n\ndef sub_1() -\u003e None:\n    return\n\n\nasync def sub_2() -\u003e None:\n    return\n\n\nif __name__ == '__main__':\n    glacier({\n        'main': main,\n        'sub': [\n            sub_1,\n            sub_2,\n        ],\n    })\n```\n\n### Positional argument\n\nIf the name of function argument is underscore-prefiexed, it is understood as positional argument.\n\n```python\nfrom glacier import glacier\n\n\ndef all_positional(_a: str, _b: str, _c: str) -\u003e None:\n    print(_a)\n    print(_b)\n    print(_c)\n\n\nif __name__ == '__main__':\n    glacier(all_positional)\n```\n\nThe above example is invoked as follows\n\n```bash\n\u003ccommand_name\u003e \u003cvalue of a\u003e \u003cvalue of b\u003e \u003cvalue of c\u003e\n```\n\n### Options\n\nAll other (non-underscore-prefixed) arguments are understood as options.\n\n```python\nfrom glacier import glacier\n\n\ndef all_options(a: str, b: str, c: str) -\u003e None:\n    print(a)\n    print(b)\n    print(c)\n\n\nif __name__ == '__main__':\n    glacier(all_options)\n```\n\nThe above example is invoked as follows\n\n```bash\n\u003ccommand_name\u003e --a \u003cvalue of a\u003e --b \u003cvalue of b\u003e --c \u003cvalue of c\u003e\n```\n\n### Default value for optional argument\n\nIf you set the default value for function argument, it also defines the default value for CLI option.\n\n\n```python\nfrom glacier import glacier\n\n\ndef default(verbose: bool = False) -\u003e None:\n    print(verbose)\n\n\nif __name__ == '__main__':\n    glacier(default)\n```\n\nThe above example is invoked as follows\n\n```bash\n\u003ccommand_name\u003e # Just call without flag (`False` will be printed)\n```\n\nor\n\n```bash\n\u003ccommand_name\u003e --verbose # Call with flag (`True` will be printed)\n```\n\n### Help with docstring\n\nHelp message for options or command itself can be provided with python docstring.\n\nFollowing style of doctrings are supported\n\n- [Google Style](https://github.com/relastle/glacier#google-style)\n- [Numpy Style](https://github.com/relastle/glacier#numpy-style)\n- [reStructuredText Style](https://github.com/relastle/glacier#restructuredtext-style)\n\nThe functions with docstring below will produce the exact the same help message with fed into `glacier`.\n(You don't need to specify which docstring style is used 😄)\n\n#### Google Style\n\n```python\ndef main_google(\n    _path: str,\n    name: str,\n    verbose: bool = False,\n) -\u003e None:\n    \"\"\"\n    This is my simple entry point of CLI.\n\n    Args:\n        _path: Positional argument representing the target file path.\n        name: Name of this operation.\n        verbose: Verbose output will be shown if set.\n    \"\"\"\n    print(_path)\n    print(name)\n    print(verbose)\n    return\n```\n\n#### Numpy Style\n\n```python\ndef main_numpy(\n    _path: str,\n    name: str,\n    verbose: bool = False,\n) -\u003e None:\n    \"\"\"\n    This is my simple entry point of CLI.\n\n    Parameters\n    ----------\n    _path: str\n        Positional argument representing the target file path.\n    name: str\n        Name of this operation.\n    verbose: bool\n        Verbose output will be shown if set.\n    \"\"\"\n    print(_path)\n    print(name)\n    print(verbose)\n    return\n```\n\n#### reStructuredText Style\n\n\n```python\ndef main_restructured_text(\n    _path: str,\n    name: str,\n    verbose: bool = False,\n) -\u003e None:\n    \"\"\"\n    This is my simple entry point of CLI.\n\n    :param _path: Positional argument representing the target file path.\n    :param name: Name of this operation.\n    :param verbose: Verbose output will be shown if set.\n    \"\"\"\n    print(_path)\n    print(name)\n    print(verbose)\n    return\n```\n\n\n### Supported types\n\n- [x] int\n- [x] str\n- [x] bool\n- [x] Enum\n- [ ] List[int]\n- [ ] List[str]\n\n## Note\n\n### :apple: Philosophy\n\n- This library is made for building CLI quickly especially for personal use, so the features provided by it is not rich.\n\n- If you want to build really user-friend CLI or that in production, I suggest that you use [click](https://click.palletsprojects.com/en/7.x/) (actually glacier uses it internally), or other full-stack CLI builing libraries.\n\n### :construction: Warnings\n\n- Please note that any destructive change (backward incompatible) can be done without any announcement.\n- This plugin is in a very early stage of development.  Feel free to report problems or submit feature requests in [Issues](https://github.com/relastle/glacier/issues).\n\n## Related works\n\n- [google/python-fire: Python Fire is a library for automatically generating command line interfaces (CLIs) from absolutely any Python object.](https://github.com/google/python-fire)\n- [tiangolo/typer: Typer, build great CLIs. Easy to code. Based on Python type hints.](https://github.com/tiangolo/typer)\n\n## [LICENSE](./LICENSE)\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frelastle%2Fglacier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frelastle%2Fglacier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frelastle%2Fglacier/lists"}