{"id":23344430,"url":"https://github.com/rec/runs","last_synced_at":"2025-04-10T02:33:16.809Z","repository":{"id":57463249,"uuid":"310845757","full_name":"rec/runs","owner":"rec","description":"🏃 Run a block of text as a subprocess 🏃","archived":false,"fork":false,"pushed_at":"2024-02-14T14:47:16.000Z","size":63,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-05-01T19:21:00.716Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/rec.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG","contributing":null,"funding":"FUNDING.yml","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},"funding":{"github":"rec"}},"created_at":"2020-11-07T13:00:32.000Z","updated_at":"2023-12-13T13:32:04.000Z","dependencies_parsed_at":"2024-01-25T11:35:58.515Z","dependency_job_id":"b6113698-e763-4e85-8323-5ea38ae2061f","html_url":"https://github.com/rec/runs","commit_stats":{"total_commits":37,"total_committers":1,"mean_commits":37.0,"dds":0.0,"last_synced_commit":"55926dd520bbbd2def30684ce4b2aef8a24ebbc0"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rec%2Fruns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rec%2Fruns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rec%2Fruns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rec%2Fruns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rec","download_url":"https://codeload.github.com/rec/runs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248144313,"owners_count":21054907,"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-12-21T06:26:38.047Z","updated_at":"2025-04-10T02:33:16.782Z","avatar_url":"https://github.com/rec.png","language":"Python","funding_links":["https://github.com/sponsors/rec"],"categories":[],"sub_categories":[],"readme":"🏃 runs: a better subprocess 🏃\n---------------------------------------------------------------------\n\n``runs`` has improved versions of ``call()``, ``check_call()``, ``check_output()``,\nand ``run()`` from Python's ``subprocess`` module that handle multiple commands and\nblocks of text, fix some defects, and add some features.\n\n.. code-block:: python\n\n    import runs\n\n    runs('''\n        ls\n        df -k  # or perhaps -h?\n        echo 'Done and done'\n    ''')\n\n---\n\n``subprocess`` is essential but:\n\n* You can only run one command at a time\n\n* Commands to subprocess must be either a sequence of strings or a string,\n  depending on whether ``shell=True`` or not\n\n* Results are returned by default as bytes and not strings\n\n---\n\nThe ``runs`` functions let you run a block of text as a sequence of subprocess\ncalls.\n\n``runs`` provides call-compatible replacements for the functions\n``subprocess.call()``, ``subprocess.check_call()``, ``subprocess.check_output()``,\nand ``subprocess.run()``\n\nEach replacement function takes a block of text, which is split into individual\ncommand lines, or a list of commands, and returns a list of values, one for\neach command.  A block of text can contain line continuations, and comments,\nwhich are ignored.\n\nThe replacement functions also add optional logging, error handling,\nand lazy evaluation, and use UTF-8 encoding by default.\n\nThe module ``runs`` is callable - ``runs()`` is a synonym for ``runs.run()``.\n\nEXAMPLES:\n\n.. code-block:: python\n\n    # ``runs()`` or ``runs.run()`` writes to stdout and stderr just as if you'd run\n    # the commands from the terminal\n\n    import runs\n\n    runs('echo \"hello, world!\"')  # prints hello, world!\n\n    # runs.check_output() returns a list, one string result for each command\n\n    results = check_output('''\n        echo  line   one  # Here's line one.\n        echo   'line \"  two  \"'  # and two!\n    ''')\n    assert results == ['line one', 'line \"  two  \"']\n\n    # Line continuations work too, either single or double\n    runs('''\n        ls -cail\n\n        # One command that takes many lines.\n        g++ -DDEBUG  -O0 -g -std=c++17 -pthread -I ./include -lm -lstdc++ \\\n          -Wall -Wextra -Wno-strict-aliasing -Wpedantic \\\\\n          -MMD -MP -MF -c src/tests.cpp -o build/./src/tests.cpp.o\n\n        echo DONE\n     ''')\n\nNOTES:\n\nExactly like ``subprocess``, ``runs`` differs from the shell in a few ways, so\nyou can't just paste your shell scripts in:\n\n* Redirection doesn't work.\n\n.. code-block:: python\n\n    result = runs.check_output('echo foo \u003e bar.txt')\n    assert result == ['foo \u003e bar.txt\\n']\n\n* Pipes don't work.\n\n.. code-block:: python\n\n    result = runs.check_output('echo foo | wc')\n    assert result == ['foo | wc \\n']\n\n*  Environment variables are not expanded in command lines\n\n.. code-block:: python\n\n    result = runs.check_output('echo $FOO', env={'FOO': 'bah!'})\n    assert result == ['$FOO\\n']\n\nEnvironment variables are exported to the subprocess, absolutely,\nbut no environment variable expension happens on command lines.\n\nAPI\n===\n\n``runs()``\n~~~~~~~~~~\n\n.. code-block:: python\n\n  runs(\n       commands,\n       *args,\n       iterate=False,\n       encoding='utf8',\n       on_exception=None,\n       echo=False,\n       **kwargs,\n  )\n\n(`runs.py, 186-200 \u003chttps://github.com/rec/runs/blob/master/runs.py#L186-L200\u003e`_)\n\nCall ``subprocess.run()`` on each command.\nReturn a list of ``subprocess.CompletedProcess`` instances.\nSee the help for ``subprocess.run()`` for more information.\n\nArguments:\n  commands:\n    A string, which gets split into lines on line endings, or a list of\n    strings.\n\n  args:\n    Positional arguments to ``subprocess.run()`` (but prefer keyword\n    arguments!)\n\n  on_exception:\n    If ``on_exception`` is ``False``, the default, exceptions from\n    ``subprocess.run()`` are raised as usual.\n\n    If ``on_exception`` is True, they are ignored.\n\n    If ``on_exception`` is a callable, the line that caused the exception is\n    passed to it.\n\n    If ``on_exception`` is a string, the line causing the exception\n    is printed, prefixed with that string.\n\n  echo:\n    If ``echo`` is ``False``, the default, then commands are silently executed.\n    If ``echo`` is ``True``, commands are printed prefixed with ``$``\n    If ``echo`` is a string, commands are printed prefixed with that string\n    If ``echo`` is callable, then each command is passed to it.\n\n  iterate:\n    If ``iterate`` is ``False``, the default, then a list of results is\n    returned.\n\n    Otherwise an iterator of results which is returned, allowing for lazy\n    evaluation.\n\n  encoding:\n    Like the argument to ``subprocess.run()``, except the default  is\n    ``'utf8'``\n\n  kwargs:\n    Named arguments passed on to ``subprocess.run()``\n\n``runs.call()``\n~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n  runs.call(\n       commands,\n       *args,\n       iterate=False,\n       encoding='utf8',\n       on_exception=None,\n       echo=False,\n       **kwargs,\n  )\n\n(`runs.py, 186-200 \u003chttps://github.com/rec/runs/blob/master/runs.py#L186-L200\u003e`_)\n\nCall ``subprocess.call()`` on each command.\nReturn a list of integer returncodes, one for each command executed.\nSee the help for ``subprocess.call()`` for more information.\n\nArguments:\n  commands:\n    A string, which gets split into lines on line endings, or a list of\n    strings.\n\n  args:\n    Positional arguments to ``subprocess.call()`` (but prefer keyword\n    arguments!)\n\n  on_exception:\n    If ``on_exception`` is ``False``, the default, exceptions from\n    ``subprocess.call()`` are raised as usual.\n\n    If ``on_exception`` is True, they are ignored.\n\n    If ``on_exception`` is a callable, the line that caused the exception is\n    passed to it.\n\n    If ``on_exception`` is a string, the line causing the exception\n    is printed, prefixed with that string.\n\n  echo:\n    If ``echo`` is ``False``, the default, then commands are silently executed.\n    If ``echo`` is ``True``, commands are printed prefixed with ``$``\n    If ``echo`` is a string, commands are printed prefixed with that string\n    If ``echo`` is callable, then each command is passed to it.\n\n  iterate:\n    If ``iterate`` is ``False``, the default, then a list of results is\n    returned.\n\n    Otherwise an iterator of results which is returned, allowing for lazy\n    evaluation.\n\n  encoding:\n    Like the argument to ``subprocess.call()``, except the default  is\n    ``'utf8'``\n\n  kwargs:\n    Named arguments passed on to ``subprocess.call()``\n\n``runs.check_call()``\n~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n  runs.check_call(\n       commands,\n       *args,\n       iterate=False,\n       encoding='utf8',\n       on_exception=None,\n       echo=False,\n       **kwargs,\n  )\n\n(`runs.py, 186-200 \u003chttps://github.com/rec/runs/blob/master/runs.py#L186-L200\u003e`_)\n\nCall ``subprocess.check_call()`` on each command.\nIf any command has a non-zero returncode, raise ``subprocess.CallProcessError``.\n\nSee the help for ``subprocess.check_call()`` for more information.\n\nArguments:\n  commands:\n    A string, which gets split into lines on line endings, or a list of\n    strings.\n\n  args:\n    Positional arguments to ``subprocess.check_call()`` (but prefer keyword\n    arguments!)\n\n  on_exception:\n    If ``on_exception`` is ``False``, the default, exceptions from\n    ``subprocess.check_call()`` are raised as usual.\n\n    If ``on_exception`` is True, they are ignored.\n\n    If ``on_exception`` is a callable, the line that caused the exception is\n    passed to it.\n\n    If ``on_exception`` is a string, the line causing the exception\n    is printed, prefixed with that string.\n\n  echo:\n    If ``echo`` is ``False``, the default, then commands are silently executed.\n    If ``echo`` is ``True``, commands are printed prefixed with ``$``\n    If ``echo`` is a string, commands are printed prefixed with that string\n    If ``echo`` is callable, then each command is passed to it.\n\n  iterate:\n    If ``iterate`` is ``False``, the default, then a list of results is\n    returned.\n\n    Otherwise an iterator of results which is returned, allowing for lazy\n    evaluation.\n\n  encoding:\n    Like the argument to ``subprocess.check_call()``, except the default  is\n    ``'utf8'``\n\n  kwargs:\n    Named arguments passed on to ``subprocess.check_call()``\n\n``runs.check_output()``\n~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n  runs.check_output(\n       commands,\n       *args,\n       iterate=False,\n       encoding='utf8',\n       on_exception=None,\n       echo=False,\n       **kwargs,\n  )\n\n(`runs.py, 186-200 \u003chttps://github.com/rec/runs/blob/master/runs.py#L186-L200\u003e`_)\n\nCall ``subprocess.check_output()`` on each command.\nIf a command has a non-zero exit code, raise a ``subprocess.CallProcessError``.\nOtherwise, return the results as a list of strings, one for each command.\nSee the help for ``subprocess.check_output()`` for more information.\n\nArguments:\n  commands:\n    A string, which gets split into lines on line endings, or a list of\n    strings.\n\n  args:\n    Positional arguments to ``subprocess.check_output()`` (but prefer keyword\n    arguments!)\n\n  on_exception:\n    If ``on_exception`` is ``False``, the default, exceptions from\n    ``subprocess.check_output()`` are raised as usual.\n\n    If ``on_exception`` is True, they are ignored.\n\n    If ``on_exception`` is a callable, the line that caused the exception is\n    passed to it.\n\n    If ``on_exception`` is a string, the line causing the exception\n    is printed, prefixed with that string.\n\n  echo:\n    If ``echo`` is ``False``, the default, then commands are silently executed.\n    If ``echo`` is ``True``, commands are printed prefixed with ``$``\n    If ``echo`` is a string, commands are printed prefixed with that string\n    If ``echo`` is callable, then each command is passed to it.\n\n  iterate:\n    If ``iterate`` is ``False``, the default, then a list of results is\n    returned.\n\n    Otherwise an iterator of results which is returned, allowing for lazy\n    evaluation.\n\n  encoding:\n    Like the argument to ``subprocess.check_output()``, except the default  is\n    ``'utf8'``\n\n  kwargs:\n    Named arguments passed on to ``subprocess.check_output()``\n\n(automatically generated by `doks \u003chttps://github.com/rec/doks/\u003e`_ on 2020-12-16T13:48:26.579866)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frec%2Fruns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frec%2Fruns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frec%2Fruns/lists"}