{"id":21768984,"url":"https://github.com/apakottur/shpyx","last_synced_at":"2025-10-10T22:43:46.747Z","repository":{"id":43092779,"uuid":"340332359","full_name":"Apakottur/shpyx","owner":"Apakottur","description":"Run shell commands in Python","archived":false,"fork":false,"pushed_at":"2025-08-11T16:56:01.000Z","size":189,"stargazers_count":17,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-25T05:24:36.037Z","etag":null,"topics":["bash","python","script","shell","terminal"],"latest_commit_sha":null,"homepage":"https://github.com/Apakottur/shpyx","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/Apakottur.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,"zenodo":null}},"created_at":"2021-02-19T10:18:28.000Z","updated_at":"2025-08-12T10:33:58.000Z","dependencies_parsed_at":"2025-04-13T16:33:03.435Z","dependency_job_id":"e641ac65-1637-4806-b6e5-12f9f8f3f679","html_url":"https://github.com/Apakottur/shpyx","commit_stats":{"total_commits":39,"total_committers":1,"mean_commits":39.0,"dds":0.0,"last_synced_commit":"a0a6abccf253cff9e04a3dd7514a7601c82555f5"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Apakottur/shpyx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Apakottur%2Fshpyx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Apakottur%2Fshpyx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Apakottur%2Fshpyx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Apakottur%2Fshpyx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Apakottur","download_url":"https://codeload.github.com/Apakottur/shpyx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Apakottur%2Fshpyx/sbom","scorecard":{"id":15087,"data":{"date":"2025-08-11","repo":{"name":"github.com/Apakottur/shpyx","commit":"7736a4f5f2c21646684236792f0f398ef4d452e4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.8,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/linters.yaml:1","Warn: no topLevel permission defined: .github/workflows/mypy.yaml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Warn: no topLevel permission defined: .github/workflows/unit_tests.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yaml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/linters.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linters.yaml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/linters.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/linters.yaml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/linters.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/mypy.yaml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/mypy.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/mypy.yaml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/mypy.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/unit_tests.yaml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/unit_tests.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/unit_tests.yaml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/Apakottur/shpyx/unit_tests.yaml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/mypy.yaml:28","Warn: pipCommand not pinned by hash: .github/workflows/release.yml:24","Warn: pipCommand not pinned by hash: .github/workflows/unit_tests.yaml:29","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   3 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T15:35:18.216Z","repository_id":43092779,"created_at":"2025-08-14T15:35:18.216Z","updated_at":"2025-08-14T15:35:18.216Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279005463,"owners_count":26083902,"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":["bash","python","script","shell","terminal"],"created_at":"2024-11-26T14:07:16.928Z","updated_at":"2025-10-10T22:43:46.714Z","avatar_url":"https://github.com/Apakottur.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/Apakottur/shpyx/blob/main/shpyx.png?raw=true\" /\u003e\n\u003c/p\u003e\n\n[![PyPI](https://img.shields.io/pypi/v/shpyx?logo=pypi\u0026logoColor=white\u0026style=for-the-badge)](https://pypi.org/project/shpyx/)\n[![Downloads](https://img.shields.io/pypi/dm/shpyx?logo=pypi\u0026logoColor=white\u0026style=for-the-badge)](https://pypi.org/project/shpyx/)\n[![Python](https://img.shields.io/pypi/pyversions/shpyx?logo=pypi\u0026logoColor=white\u0026style=for-the-badge)](https://pypi.org/project/shpyx/)\n\n**shpyx** is a simple, lightweight and typed library for running shell commands in Python.\n\nUse `shpyx.run` to run a shell command in a subprocess:\n\n```python\n\u003e\u003e\u003e import shpyx\n\u003e\u003e\u003e shpyx.run(\"echo 1\")\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n\u003e\u003e\u003e shpyx.run(\"echo 1\").return_code\n0\n\u003e\u003e\u003e shpyx.run(\"echo 1\").stdout\n'1\\n'\n\u003e\u003e\u003e shpyx.run(\"echo 1\").stderr\n''\n```\n\n## Installation\n\nInstall with `pip`:\n\n```shell\npip install shpyx\n```\n\n## How Tos\n\n### Run a command\n\nIn string format:\n\n```python\n\u003e\u003e\u003e shpyx.run(\"echo 1\")\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n```\n\nIn list format:\n\n```python\n\u003e\u003e\u003e shpyx.run([\"echo\", [\"1\"])\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n```\n\n### Run a command and print live output\n\n```python\n\u003e\u003e\u003e shpyx.run(\"echo 1\", log_output=True)\n1\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n```\n\n### Run a command with shell specific logic\n\nWhen the argument to `run` is a string, an actual shell is created in the subprocess and shell logic can be used.\nFor example, the pipe operator can be used in bash/sh:\n\n```python\n\u003e\u003e\u003e shpyx.run(\"seq 1 5 | grep '2'\")\nShellCmdResult(cmd=\"seq 1 5 | grep '2'\", stdout='2\\n', stderr='', all_output='2\\n', return_code=0)\n```\n\n### Create a custom runner\n\nUse a custom runner to override the execution defaults, and not have to pass them to every call.\n\nFor example, we can change the default value of `log_cmd`, so that all commands are logged:\n\n```python\n\u003e\u003e\u003e shpyx.run(\"echo 1\")\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n\n\u003e\u003e\u003e shpyx.run(\"echo 1\", log_cmd=True)\nRunning: echo 1\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n\n\u003e\u003e\u003e runner = shpyx.Runner(log_cmd=True)\n\u003e\u003e\u003e runner.run(\"echo 1\")\nRunning: echo 1\nShellCmdResult(cmd='echo 1', stdout='1\\n', stderr='', all_output='1\\n', return_code=0)\n```\n\n### Propagating terminal control sequences\n\nNote: as of now this is only supported for Unix environments.\n\nSome commands, like `psql`, might output special characters used for terminal management like cursor movement and\ncolors. For example, the `psql` command is used to start an interactive shell against a Postgres DB:\n\n```python\nshpyx.run(f\"psql -h {host} -p {port} -U {user} -d {database}\", log_output=True)\n```\n\nHowever, the above call will not work as good as running `psql` directly, due to terminal control sequences not being\nproperly propagated. To make it work well, we need to use the [script](https://man7.org/linux/man-pages/man1/script.1.html)\nutility which will properly propagate all control sequences:\n\n```python\n# Linux:\nshpyx.run(f\"script -q -c 'psql -h {host} -p {port} -U {user} -d {database}'\", log_output=True)\n# MacOS:\nshpyx.run(f\"script -q psql -h {host} -p {port} -U {user} -d {database}\", log_output=True)\n\n```\n\nshpyx provides a keyword argument that does this wrapping automatically, `unix_raw`:\n\n```python\nshpyx.run(f\"psql -h {host} -p {port} -U {user} -d {database}\", log_output=True, unix_raw=True)\n```\n\nThe flag is disabled by default, and should only be used for interactive commands like `psql`.\n\n## API Reference\n\nThe following arguments are supported by `Runner`:\n\n| Name                 | Description                                                                | Default |\n| -------------------- | -------------------------------------------------------------------------- | ------- |\n| `log_cmd`            | Log the executed command.                                                  | `False` |\n| `log_output`         | Log the live output of the command (while it is being executed).           | `False` |\n| `verify_return_code` | Raise an exception if the shell return code of the command is not `0`.     | `True`  |\n| `verify_stderr`      | Raise an exception if anything was written to stderr during the execution. | `False` |\n| `use_signal_names`   | Log the name of the signal corresponding to a non-zero error code.         | `True`  |\n\nThe following arguments are supported by `run`:\n\n| Name                 | Description                                                                | Default                  |\n| -------------------- | -------------------------------------------------------------------------- | ------------------------ |\n| `log_cmd`            | Log the executed command.                                                  | `Runner default`         |\n| `log_output`         | Log the live output of the command (while it is being executed).           | `Runner default`         |\n| `verify_return_code` | Raise an exception if the shell return code of the command is not `0`.     | `Runner default`         |\n| `verify_stderr`      | Raise an exception if anything was written to stderr during the execution. | `Runner default`         |\n| `use_signal_names`   | Log the name of the signal corresponding to a non-zero error code.         | `Runner default`         |\n| `env`                | Environment variables to set during the execution of the command.          | `Same as parent process` |\n| `exec_dir`           | Custom path to execute the command in (defaults to current directory).     | `Same as parent process` |\n| `unix_raw`           | (UNIX ONLY) Whether to use the `script` Unix utility to run the command.   | `False`                  |\n\n## Implementation details\n\n`shpyx` is a wrapper around the excellent [subprocess](https://docs.python.org/3/library/subprocess.html) module, aiming\nto concentrate all the different API functions (`Popen`/`communicate`/`poll`/`wait`) into a single function - `shpyx.run`.\n\nWhile the core API logic is fully supported on both Unix and Windows systems, there is some OS specific code for minor quality-of-life\nimprovements.\nFor example, on non Windows systems, [fcntl](https://docs.python.org/3/library/fcntl.html) is used to configure the subprocess to\nalways be incorruptible (which means one can CTRL-C out of any command).\n\n## Security\n\nThe call to `subprocess.Popen` uses `shell=True` when the input to `run` is a string (to support shell logic like bash piping).\nThis means that an actual system shell is being created, and the subprocess has the permissions of the main Python process.\n\nIt is therefore recommended not pass any untrusted input to `shpyx.run`.\n\nFor more info, see [security considerations](https://docs.python.org/3/library/subprocess.html#security-considerations).\n\n## Useful links\n\nRelevant Python libraries:\n\n- [subprocess](https://docs.python.org/3/library/subprocess.html)\n- [shlex](https://docs.python.org/3/library/shlex.html)\n\nOther 3rd-party libraries for running shell commands in Python:\n\n- [sarge](https://github.com/vsajip/sarge)\n- [sh](https://github.com/amoffat/sh)\n- [invoke](https://github.com/pyinvoke/invoke)\n\n## Contributing\n\nTo contribute simply open a PR with your changes.\n\nTests, linters and type checks are run in CI through GitHub Actions.\n\n### Running checks locally\n\nTo run checks locally, start by installing all the development dependencies:\n\n```shell\npoetry install\n```\n\nTo run the linters use `pre-commit`:\n\n```shell\npre-commit run -a\n```\n\nTo run the unit tests use `pytest`:\n\n```shell\npytest -c tests/pytest.ini tests\n```\n\nTo run type checks use `mypy`:\n\n```shell\nmypy --config-file shpyx/mypy.toml shpyx tests\n```\n\nTo trigger a deployment of a new version upon merge, bump the version number in `pyproject.toml`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapakottur%2Fshpyx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapakottur%2Fshpyx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapakottur%2Fshpyx/lists"}