{"id":16959498,"url":"https://github.com/crdoconnor/commandlib","last_synced_at":"2025-03-22T14:30:52.055Z","repository":{"id":62563941,"uuid":"50774314","full_name":"crdoconnor/commandlib","owner":"crdoconnor","description":"Command runner with clean API.","archived":false,"fork":false,"pushed_at":"2023-09-17T09:47:14.000Z","size":113,"stargazers_count":21,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-17T19:48:01.643Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://hitchdev.com/commandlib/","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/crdoconnor.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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":"2016-01-31T12:54:19.000Z","updated_at":"2024-10-27T12:11:46.000Z","dependencies_parsed_at":"2024-06-21T07:12:19.675Z","dependency_job_id":"e3708b10-395b-4688-8e0c-bcee817843aa","html_url":"https://github.com/crdoconnor/commandlib","commit_stats":{"total_commits":94,"total_committers":1,"mean_commits":94.0,"dds":0.0,"last_synced_commit":"749e55e810f0e6dfa7dcc6afc51df6c7b7ec10ad"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crdoconnor%2Fcommandlib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crdoconnor%2Fcommandlib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crdoconnor%2Fcommandlib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crdoconnor%2Fcommandlib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crdoconnor","download_url":"https://codeload.github.com/crdoconnor/commandlib/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244971690,"owners_count":20540832,"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":[],"created_at":"2024-10-13T22:45:08.158Z","updated_at":"2025-03-22T14:30:51.765Z","avatar_url":"https://github.com/crdoconnor.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CommandLib\n\nCommandlib is a dependencyless library for calling external UNIX commands\n(e.g. in build scripts) in a clean, readable way.\n\nUsing method chaining, you can build up Command objects that run in a specific\ndirectory, with specified [environment variables](https://hitchdev.com/commandlib/using/alpha/environment-variables)\nand [PATHs](https://hitchdev.com/commandlib/using/alpha/add-directory-to-path), etc.\n\nFor simplicity's sake, the library itself only runs commands in a blocking\nway (all commands run to completion before continuing), although it contains\nhooks to run non-blocking via either [icommandlib](https://github.com/crdoconnor/icommandlib)\nor [pexpect](https://pexpect.readthedocs.io/en/stable/).\n\n\n\nPretend 'django/manage.py':\n```bash\n# Pretend django \"manage.py\" that just prints out arguments:\nimport sys ; sys.stdout.write(' '.join(sys.argv[1:]))\n\n```\n\n```python\nfrom commandlib import Command\n\n# Create base command\npython = Command(\"python\")\n\n# Create command \"python manage.py\" that runs in the django directory\nmanage = python(\"manage.py\").in_dir(\"django\")\n\n# Build even more specific command\ndev_manage = manage.with_trailing_args(\"--settings\", \"local_settings.py\")\n\n```\n\n\n\n```python\n# Run combined command\ndev_manage(\"runserver\", \"8080\").run()\n\n```\n\nWill output:\n```\nrunserver 8080 --settings local_settings.py\n```\n\n\n\n\n\n\n## Install\n\n```sh\n$ pip install commandlib\n```\n\n## Docs\n\n- [Easily invoke commands from the current virtualenv (python_bin)](https://hitchdev.com/commandlib/using/alpha/)\n- [Run commmands interactively using icommandlib or pexpect](https://hitchdev.com/commandlib/using/alpha/)\n- [Piping data out to string or file (.piped)](https://hitchdev.com/commandlib/using/alpha/)\n- [Easily invoke commands from one directory (CommandPath)](https://hitchdev.com/commandlib/using/alpha/)\n- [Add directory to PATH (with_path)](https://hitchdev.com/commandlib/using/alpha/)\n- [Piping data in from string or file (.piped)](https://hitchdev.com/commandlib/using/alpha/)\n- [Run command and don't raise exception on nonzero exit code (ignore_errors())](https://hitchdev.com/commandlib/using/alpha/)\n- [Change your command's environment variables (with_env)](https://hitchdev.com/commandlib/using/alpha/)\n- [Capture output (.output())](https://hitchdev.com/commandlib/using/alpha/)\n\n\n## Why?\n\nCommandlib avoids the tangle of messy code that you would\nget using the subprocess library directly (Popen, call, check_output(), .communicate(), etc.)\nand the [confusion that results](https://stackoverflow.com/questions/89228/calling-an-external-command-in-python).\n\nIt's a [heavily dogfooded](https://hitchdev.com/principles/extreme-dogfooding) library. For humans. Because who else?\n\n## Is subprocess really that bad?\n\nThe code will likely be longer and messier. For example, from [stack overflow](https://stackoverflow.com/questions/2231227/python-subprocess-popen-with-a-modified-environment):\n\n```python\nimport subprocess, os\nprevious_directory = os.getcwd()\n\nos.chdir(\"command_directory\")\nmy_env = os.environ.copy()\nmy_env[\"PATH\"] = \"/usr/sbin:/sbin:\" + my_env[\"PATH\"]\nsubprocess.Popen(my_command, env=my_env)\nos.chdir(previous_directory)\n```\n\nEquivalent:\n\n```python\nfrom commandlib import Command\n\nCommand(my_command).with_path(\"/usr/sbin:/sbin:\").in_dir(\"command_directory\").run()\n```\n\n## Why not use Delegator instead (Kenneth Reitz's 'subprocesses for humans')?\n\nKenneth Reitz (author of requests \"urllib2/3 for humans\"), wrote a similarly inspired \"subprocess for humans\"\ncalled [envoy](https://github.com/kennethreitz/envoy). That is now deprecated and there is now a replacement called [delegator](https://github.com/kennethreitz/delegator.py), which is a very thin\nwrapper around subprocess.\n\nFeatures delegator has which commandlib does not:\n\n* Delegator can chain commands, much like bash does (delegator.chain('fortune | cowsay')). Commandlib doesn't do that because while dogfooding the library I never encountered a use case where I found this to be necessary. You can, however, easily get the output of one command using .output() as a string and feed it into another using piped.from_string(string).\n\n* Delegator runs subprocesses in both a blocking and nonblocking way (using pexpect). commandlib only does blocking by itself but if you pip install pexpect or icommandlib it can run via either one of them.\n\n* Runs on windows\n\nFeatures which both have:\n\n* Ability to set environment variables.\n* Ability to run pexpect process from command object.\n\nFeatures which only commandlib has:\n\n* Ability to set PATH easily.\n* Ability call code from within the current virtualenv easily.\n* Ability to pipe in strings or files and easily pipe out to strings or file (or file handles).\n* Hook to easily run commands in from the current virtualenv.\n\n## Why not use other tools?\n\n* os.system(*) - only capable of running very simple bash commands.\n\n* [sh](https://amoffat.github.io/sh/) - uses a lot of magic. Attempts to make python more like shell rather than making running commands more pythonic.\n\n* [plumbum](https://plumbum.readthedocs.io/en/latest/]) - similar to amoffat's sh, tries to make a sort of \"bash inside python\". Also has a weird way of building commands from dict syntax (grep[\"-v\", \"\\\\.py\"]).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrdoconnor%2Fcommandlib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrdoconnor%2Fcommandlib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrdoconnor%2Fcommandlib/lists"}