{"id":21071497,"url":"https://github.com/igrek51/nuclear","last_synced_at":"2025-05-16T05:31:31.582Z","repository":{"id":46946975,"uuid":"115726463","full_name":"igrek51/nuclear","owner":"igrek51","description":"Binding glue for CLI Python applications","archived":false,"fork":false,"pushed_at":"2024-08-02T10:27:34.000Z","size":2601,"stargazers_count":16,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-02T09:13:30.466Z","etag":null,"topics":["argparse","arguments","cli","command-line","parser","python"],"latest_commit_sha":null,"homepage":"https://igrek51.github.io/nuclear/","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/igrek51.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":"2017-12-29T13:56:44.000Z","updated_at":"2024-08-16T23:03:02.000Z","dependencies_parsed_at":"2023-07-16T16:53:19.371Z","dependency_job_id":"f7c81566-4322-4a88-ac7a-8f9c2b59667d","html_url":"https://github.com/igrek51/nuclear","commit_stats":{"total_commits":325,"total_committers":4,"mean_commits":81.25,"dds":"0.25230769230769234","last_synced_commit":"d2f54693e521cffa7e266e41307caf35fcb226ff"},"previous_names":["igrek51/cliglue"],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrek51%2Fnuclear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrek51%2Fnuclear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrek51%2Fnuclear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrek51%2Fnuclear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igrek51","download_url":"https://codeload.github.com/igrek51/nuclear/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254474168,"owners_count":22077232,"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":["argparse","arguments","cli","command-line","parser","python"],"created_at":"2024-11-19T18:53:24.145Z","updated_at":"2025-05-16T05:31:26.559Z","avatar_url":"https://github.com/igrek51.png","language":"Python","readme":"# ☢️ Nuclear\n[![GitHub version (latest SemVer)](https://img.shields.io/github/v/tag/igrek51/nuclear?label=github\u0026sort=semver)](https://github.com/igrek51/nuclear)\n[![PyPI](https://img.shields.io/pypi/v/nuclear)](https://pypi.org/project/nuclear)\n[![Github Pages](https://img.shields.io/badge/docs-github.io-blue)](https://igrek51.github.io/nuclear)\n[![Documentation Status](https://readthedocs.org/projects/nuclear-py/badge/?version=latest)](https://nuclear-py.readthedocs.io/en/latest/?badge=latest)\n[![codecov](https://codecov.io/gh/igrek51/nuclear/branch/master/graph/badge.svg)](https://codecov.io/gh/igrek51/nuclear)\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/igrek51/nuclear/test.yml?branch=master\u0026label=tests)](https://github.com/igrek51/nuclear/actions?query=workflow%3Atest)\n\n\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://github.com/igrek51/nuclear\"\u003eGitHub\u003c/a\u003e\n    -\n    \u003ca href=\"https://pypi.org/project/nuclear\"\u003ePyPI\u003c/a\u003e\n    -\n    \u003ca href=\"https://igrek51.github.io/nuclear\"\u003eDocumentation\u003c/a\u003e\n\u003c/div\u003e\n\n*Nuclear* is a binding glue for CLI applications.\nIt consists of tools for building CLI applications in Python, including:\n\n- [CLI parser](https://igrek51.github.io/nuclear/quick-start/) for building nested CLI commands\n- [Sublog](https://igrek51.github.io/nuclear/sublog/) - contextual logger\n- [Shell utilities](https://igrek51.github.io/nuclear/shell/)\n\n\n# CLI Demo\n```python\nfrom nuclear import CliBuilder\n\ncli = CliBuilder()\n\n@cli.add_command('hello')\ndef say_hello(name: str, decode: bool = False, repeat: int = 1):\n    \"\"\"\n    Say hello\n    :param decode: Decode name as base64\n    \"\"\"\n    message = f\"I'm a {b64decode(name).decode() if decode else name}!\"\n    print(' '.join([message] * repeat))\n\n@cli.add_command('calculate', 'factorial')\ndef calculate_factorial(n: int):\n    \"\"\"Calculate factorial\"\"\"\n    print(reduce(lambda x, y: x * y, range(1, n + 1)))\n\n@cli.add_command('calculate', 'primes')\ndef calculate_primes(n: int):\n    \"\"\"List prime numbers using Sieve of Eratosthenes\"\"\"\n    print(sorted(reduce((lambda r, x: r - set(range(x**2, n, x)) if (x in r) else r), range(2, n), set(range(2, n)))))\n\ncli.run()\n```\n\n![](https://github.com/igrek51/nuclear/blob/master/docs/demo/demo-live.gif?raw=true)\n\nSee [demo.py](https://github.com/igrek51/nuclear/blob/master/docs/demo/demo-decorator.py) for a complete example.\n\n# Installation\n```bash\npython3 -m pip install --upgrade nuclear\n```\n\nYou need Python 3.8 or newer.\n\n\n# 📜 Sublog\n**Sublog** is a *nuclear*'s contextual logging system that allows you to:\n  \n- display variables besides log messages: `logger.debug('message', airspeed=20)`,\n- wrap errors with context: `with add_context('ignition')`,\n- catch errors and show traceback in a concise, pretty format: `with error_handler()`.\n\n```python\nfrom nuclear.sublog import logger, error_handler, add_context\n\nwith error_handler():\n    logger.debug('checking engine', temperature=85.0, pressure='12kPa')\n    with add_context('ignition', request=42):\n        logger.info('ignition ready', speed='zero')\n        with add_context('liftoff'):\n            raise RuntimeError('explosion')\n```\n\n![sublog demo](https://github.com/igrek51/nuclear/blob/master/docs/img/sublog-demo.png?raw=true)\n\n## Context logger\nUse `nuclear.sublog.logger` to log message with a pretty format out of the box.\n\nPass additional context variables as keyword arguments to display them in the log message.\n\n```python\nfrom nuclear.sublog import logger\n\nlogger.info('info log')\nlogger.debug('debug log', var1=1, var2='two')\nlogger.info('not great not terrible', radioactivity=3.6)\nlogger.error('this is bad')\nlogger.exception(RuntimeError('this is worse'))\n```\n\n## Error handler\nUse `nuclear.sublog.error_handler` to catch errors and show traceback in a concise, pretty format.\n\n```python\nfrom nuclear.sublog import error_handler\n\nwith error_handler():\n    raise RuntimeError('explosion')\n```\n\n## Wrapping context\nUse `nuclear.sublog.add_context` to wrap code with additional context information.\nThis will be included in in the log message, if an error occurs.\n\n```python\nfrom nuclear.sublog import add_context\n\nwith add_context('reloading plugins'):\n    with add_context('loading config'):\n        raise RuntimeError('file is missing')\n```\nThis will produce an error with the following message:\n```\nreloading plugins: loading config: file is missing\n```\n\nNote that while each individual part of the message may not provide a comprehensive explanation of the error,\nwhen combined, the whole message becomes highly informative.\nThis is the core principle behind enriching errors with context.\n\n\n# 🐌 Shell utilities\n\n*Nuclear* provides utilities for running system shell commands.\n\nBasic usage:\n\n```python\nfrom nuclear import shell\n\nwindow_id: str = shell('xdotool getactivewindow')\n```\n\n`shell` function captures the stdout \u0026 stderr output of the shell command and returns it as a string.\nIt may also print live stdout in real time (line by line) and capture output in case of errors.\n\nIt has a lot of possibilities thanks to its parameters:\n\n* `cmd: str` - shell command to run\n* `workdir: Optional[Path] = None` - working directory for the command\n* `print_stdout: bool = False` - whether to print live stdout in real time (line by line) from a subprocess\n* `print_log: bool = False` - whether to print a log message about running the command\n* `raw_output: bool = False` - whether to let subprocess manage stdout/stderr on its own instead of capturing it\n* `independent: bool = False` - whether to start an independent process that can outlive the caller process\n* `output_file: Optional[Path] = None` - optional file to write the output in real time\n\nIt returns the stdout of the command combined with stderr.\nIn case of non-zero command exit code, `shell` raises `CommandError` exception.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figrek51%2Fnuclear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figrek51%2Fnuclear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figrek51%2Fnuclear/lists"}