# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['shellous']

package_data = \
{'': ['*']}

install_requires = \
['immutables>=0.16,<0.17']

setup_kwargs = {
    'name': 'shellous',
    'version': '0.8.0',
    'description': 'Async Processes and Pipelines',
    'long_description': 'Async Processes and Pipelines\n=============================\n\n[![docs](https://img.shields.io/badge/-documentation-informational)](https://byllyfish.github.io/shellous/shellous.html) [![PyPI](https://img.shields.io/pypi/v/shellous)](https://pypi.org/project/shellous/) [![CI](https://github.com/byllyfish/shellous/actions/workflows/ci.yml/badge.svg)](https://github.com/byllyfish/shellous/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/byllyfish/shellous/branch/main/graph/badge.svg?token=W44NZE89AW)](https://codecov.io/gh/byllyfish/shellous)\n\nshellous provides a concise API for running subprocesses using asyncio. It is \nsimilar to and inspired by [sh](https://pypi.org/project/sh/).\n\n```python\nimport asyncio\nimport shellous\n\nsh = shellous.context()\n\nasync def main():\n    result = await (sh("ls") | sh("grep", "README"))\n    print(result)\n\nasyncio.run(main())\n```\n\nBenefits\n--------\n\n- Run programs asychronously in a single line.\n- Easily capture output or redirect stdin, stdout and stderr to files, memory buffers or loggers.\n- Easily construct [pipelines](https://en.wikipedia.org/wiki/Pipeline_(Unix)) and use [process substitution](https://en.wikipedia.org/wiki/Process_substitution).\n- Run a program with a pseudo-terminal (pty).\n- Runs on Linux, MacOS, FreeBSD and Windows.\n\nRequirements\n------------\n\n- Requires Python 3.9 or later.\n- Requires an asyncio event loop.\n- Process substitution requires a Unix system with /dev/fd support.\n- Pseudo-terminals require a Unix system. Pty\'s do not work on [uvloop](https://github.com/MagicStack/uvloop).\n\nBasic Usage\n-----------\n\nStart the asyncio REPL by typing `python3 -m asyncio`, and import the **shellous** module:\n\n```python-repl\n>>> import shellous\n```\n\nBefore we can do anything else, we need to create a **context**. Store the context in a \nshort variable name like `sh` because we\'ll be typing it a lot.\n\n```python-repl\n>>> sh = shellous.context()\n```\n\nNow, we\'re ready to run our first command. Here\'s one that runs `echo "hello, world"`.\n\n```python-repl\n>>> await sh("echo", "hello, world")\n\'hello, world\\n\'\n```\n\nThe first argument is the program name. It is followed by zero or more separate arguments.\n\nA command does not run until you `await` it. Here, we create our own echo command with "-n"\nto omit the newline. Note, `echo("abc")` is the same as `echo -n "abc"`.\n\n```python-repl\n>>> echo = sh("echo", "-n")\n>>> await echo("abc")\n\'abc\'\n```\n\n\nResults and Exit Codes\n----------------------\n\nWhen you `await` a command, it captures the standard output and returns it. You can optionally have the\ncommand return a `Result` object. The `Result` object will contain more information about the command \nexecution including the `exit_code`. To return a result object, set `return_result` option to `True`.\n\n```python-repl\n>>> await echo("abc").set(return_result=True)\nResult(output_bytes=b\'abc\', exit_code=0, cancelled=False, encoding=\'utf-8\', extra=None)\n```\n\nThe above command had an exit_code of 0.\n\nIf a command exits with a non-zero exit code, it raises a `ResultError` exception that contains\nthe `Result` object.\n\n```python-repl\n>>> await sh("cat", "does_not_exist")\nTraceback (most recent call last):\n  ...\nshellous.result.ResultError: Result(output_bytes=b\'\', exit_code=1, cancelled=False, encoding=\'utf-8\', extra=None)\n```\n\n\nRedirecting Standard Input\n--------------------------\n\nYou can change the standard input of a command by using the `|` operator.\n\n```python-repl\n>>> cmd = "abc" | sh("wc", "-c")\n>>> await cmd\n\'       3\\n\'\n```\n\nTo redirect stdin using a file\'s contents, use a `Path` object from `pathlib`.\n\n```python-repl\n>>> from pathlib import Path\n>>> cmd = Path("LICENSE") | sh("wc", "-l")\n>>> await cmd\n\'     201\\n\'\n```\n\n\nRedirecting Standard Output\n---------------------------\n\nTo redirect standard output, use the `|` operator.\n\n```python-repl\n>>> output_file = Path("/tmp/output_file")\n>>> cmd = sh("echo", "abc") | output_file\n>>> await cmd\n\'\'\n>>> output_file.read_bytes()\nb\'abc\\n\'\n```\n\nTo redirect standard output with append, use the `>>` operator.\n\n```python-repl\n>>> cmd = sh("echo", "def") >> output_file\n>>> await cmd\n\'\'\n>>> output_file.read_bytes()\nb\'abc\\ndef\\n\'\n```\n\n\nRedirecting Standard Error\n--------------------------\n\nBy default, standard error is not captured. To redirect standard error, use the `stderr`\nmethod.\n\n```python-repl\n>>> cmd = sh("cat", "does_not_exist").stderr(shellous.STDOUT)\n>>> await cmd.set(exit_codes={0,1})\n\'cat: does_not_exist: No such file or directory\\n\'\n```\n\nYou can redirect standard error to a file or path. \n\nTo redirect standard error to the hosting program\'s `sys.stderr`, use the INHERIT redirect\noption.\n\n```python-repl\n>>> cmd = sh("cat", "does_not_exist").stderr(shellous.INHERIT)\n>>> await cmd\ncat: does_not_exist: No such file or directory\nTraceback (most recent call last):\n  ...\nshellous.result.ResultError: Result(output_bytes=b\'\', exit_code=1, cancelled=False, encoding=\'utf-8\', extra=None)\n```\n\n\nPipelines\n---------\n\nYou can create a pipeline by combining commands using the `|` operator.\n\n```python-repl\n>>> pipe = sh("ls") | sh("grep", "README")\n>>> await pipe\n\'README.md\\n\'\n```\n\nProcess Substitution (Unix Only)\n--------------------------------\n\nYou can pass a shell command as an argument to another.\n\n```python-repl\n>>> cmd = sh("grep", "README", sh("ls"))\n>>> await cmd\n\'README.md\\n\'\n```\n\nUse ~ to write to a command instead.\n\n```python-repl\n>>> buf = bytearray()\n>>> cmd = sh("ls") | sh("tee", ~sh("grep", "README") | buf) | shellous.DEVNULL\n>>> await cmd\n\'\'\n>>> buf\nbytearray(b\'README.md\\n\')\n```\n\nAsync With & For\n----------------\n\nYou can loop over a command\'s output by using the context manager as an iterator.\n\n```python-repl\n>>> async with pipe as run:\n...   async for line in run:\n...     print(line.rstrip())\n... \nREADME.md\n```\n\n> <span style="font-size:1.5em;">⚠️ </span> You can also acquire an async iterator directly from\n> the command or pipeline object. This is discouraged because you will have less control over the final\n> clean up of the command invocation than with a context manager.\n\n```python-repl\n>>> async for line in pipe:   # Use caution!\n...   print(line.rstrip())\n... \nREADME.md\n```\n\nYou can use `async with` to interact with the process streams directly. You have to be careful; you\nare responsible for correctly reading and writing multiple streams at the same time.\n\n```python-repl\n>>> async with pipe as run:\n...   data = await run.stdout.readline()\n...   print(data)\n... \nb\'README.md\\n\'\n```\n\nIncomplete Results\n------------------\n\nWhen a command is cancelled, shellous normally cleans up after itself and re-raises a `CancelledError`.\n\nYou can retrieve the partial result by setting `incomplete_result` to True. Shellous will return a\n`ResultError` when the specified command is cancelled.\n\n```python-repl\n>>> sleep = sh("sleep", 60).set(incomplete_result=True)\n>>> t = asyncio.create_task(sleep.coro())\n>>> t.cancel()\nTrue\n>>> await t\nTraceback (most recent call last):\n  ...\nshellous.result.ResultError: Result(output_bytes=b\'\', exit_code=-15, cancelled=True, encoding=\'utf-8\', extra=None)\n```\n\nWhen you use `incomplete_result`, your code should respect the `cancelled` attribute in the Result object. \nOtherwise, your code may swallow the CancelledError.\n\nPseudo-Terminal Support (Unix Only)\n-----------------------------------\n\nTo run a command through a pseudo-terminal, set the `pty` option to True. Alternatively, you can pass\na function to configure the tty mode and size.\n\n```python-repl\n>>> ls = sh("ls").set(pty=shellous.canonical(cols=40, rows=10, echo=False))\n>>> await ls("README.md", "CHANGELOG.md")\n\'CHANGELOG.md\\tREADME.md\\r\\n\'\n```\n\nContext Objects\n---------------\n\nYou can specify shared command settings in a context object. Context objects are immutable,\nso you must store the result of your changes in a new variable.\n\n```python-repl\n>>> auditor = lambda phase, info: print(phase, info["runner"].name)\n>>> sh1 = sh.set(audit_callback=auditor)\n```\n\nNow all commands run with `sh1` will log their progress using the audit callback.\n\n```python-repl\n>>> await sh1("echo", "goodbye")\nstart echo\nstop echo\n\'goodbye\\n\'\n```\n',
    'author': 'Bill Fisher',
    'author_email': 'william.w.fisher@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/byllyfish/shellous',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.9,<4.0',
}


setup(**setup_kwargs)
