diff --git a/.gitignore b/.gitignore index 888bfe9..4f62cab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ __pycache__ .pytest_cache +.mypy_cache +.ruff_cache .venv .pdm-python .pdm-build + +dist diff --git a/README.md b/README.md index 97ec8b9..9639ea1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ # tee-subprocess ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/thearchitector/tee-subprocess/ci.yaml?label=tests&style=flat-square) +![PyPI - Downloads](https://img.shields.io/pypi/dw/tee-subprocess?style=flat-square) A subprocess replacement with tee support for both synchronous and asynchronous contexts. -## Usage +Supports Python 3.8+. + +## Example Just import the `run` function and use it as you would use `subprocess.run`. ```python -from subprocess_tee import run +from tee_subprocess import run process = run(["python", "--version"], tee=True, text=True, capture_output=True) # ==> Python 3.11.2 @@ -17,11 +20,11 @@ print(process.stdout) # ==> Python 3.11.2 ``` -Changing `stdout` and `stderr` changes the location to which the `tee` occurs. You can supply any of the defined options in `subprocess` or `asyncio.subprocess`(`STDOUT`, `DEVNULL`, etc), as well as a writable text or binary file object. +Changing `stdout` and `stderr` changes the location to which the `tee` occurs. You can supply any of the defined options in `subprocess` or `asyncio.subprocess` (`STDOUT`, `DEVNULL`, etc), as well as a writable text or binary file object; if providing a text file object, you must specify `text = True`. ### Async -Internally, `subprocess_tee` utilizes `asyncio` to concurrently output and capture the subprocess logs. If an event loop is already running, `run` will return an awaitable coroutine. Otherwise, it will call `asyncio.run` for you. Practically, this means you can just treat `run` as a coroutine if you're in an async content; if you're not, just call it synchronously. +Internally, `tee_subprocess` utilizes `asyncio` to concurrently output and capture the subprocess logs. If an event loop is already running, `run` will return an awaitable coroutine. Otherwise, it will call `asyncio.run` for you. Practically, this means you can just treat `run` as a coroutine if you're in an async content; if you're not, just call it synchronously. ```python async def main(): @@ -33,6 +36,10 @@ async def main(): asyncio.run(main()) ``` +## Alternatives + +[subprocess-tee](https://github.com/pycontribs/subprocess-tee), the motivation for this library, has the same objective but fails to accommodate asynchronous applications and non-shell invocations. This library supports asynchronous contexts as well as direct, non-shell, program execution ("list-style" vs. "shell-style"). + ## License MIT License diff --git a/pyproject.toml b/pyproject.toml index 3219571..3c724e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "tee-subprocess" -version = "1.0.0" +version = "1.0.0.post0" description = "A subprocess replacement with tee support for both synchronous and asynchronous contexts." authors = [ {name = "Elias Gabriel",email = "me@eliasfgabriel.com"}, @@ -16,7 +16,6 @@ documentation = "https://github.com/thearchitector/tee-subprocess" repository = "https://github.com/thearchitector/tee-subprocess" changelog = "https://github.com/thearchitector/tee-subprocess/blob/main/CHANGELOG.md" - [tool.pdm.dev-dependencies] dev = [ "pytest>=7.4.3", diff --git a/tee_subprocess/py.typed b/tee_subprocess/py.typed new file mode 100644 index 0000000..e69de29