Metadata-Version: 2.1
Name: easyproc
Version: 0.6.0
Summary: thin abstraction on subprocess.run to simplify admin scripts
Home-page: https://github.com/ninjaaron/easyproc
Author: Aaron Christianson
Author-email: ninjaaron@gmail.com
License: MPL-2.0
Keywords: shell pipe subprcess process scripting
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3.5

easyproc
========
``easyproc`` is a wrapper on the ``subprocess`` that provides a similar
API, but attempts to reduce some of the boilerplate involved in using
the module.

It's been tested with Python 3.4 and newer, though the ``timeout``
feature is broken in 3.4.

It can be installed with pip.

::

   $ pip install easyproc

It provides the ``Popen`` class and the ``run`` class which function
similarly to those in ``subprocess`` with a few differences:

- All streams default to strings (``subprocess`` uses bytes).
- Error checking is turned on by default. Errors should never pass
  silently. Unless explicitly silenced.
- If a string is passed as the initial argument instead of an iterable
  of arguments, it will be passed to ``shlex.split`` automatically.
- ``stdout`` and ``stderr`` always behave more or less like files. In
  some cases, they are special objects. More later.

The module also provides a few convenience 

.. contents::


Basics (and ``Popen``)
--------------------------
Ok, now for a few examples.

.. code:: python

  >>> import easyproc as ep
  >>> ep.run('ls -lh')
  total 28K
  drwxr-xr-x 2 ninjaaron users 4.0K Aug 23  2017 easyproc.egg-info
  -rw-r--r-- 1 ninjaaron users  11K Aug 24 09:51 easyproc.py
  drwxr-xr-x 2 ninjaaron users 4.0K Aug 24 10:58 __pycache__
  -rw-r--r-- 1 ninjaaron users  983 Aug 24 10:56 README.rst
  -rw-r--r-- 1 ninjaaron users  491 Mar 26 12:53 setup.py
  CompletedProcess(args='ls -lh', returncode=0)
  >>> # ^ shlex.split the arguments.
  ...
  >>> ep.run('ls foo')
  ls: cannot access 'foo': No such file or directory
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/ninjaaron/src/py/easyproc/easyproc.py", line 207, in run
      retcode = mkchecker(cmd, proc, ok_codes)()
    File "/home/ninjaaron/src/py/easyproc/easyproc.py", line 75, in check_code
      output=proc.stdout, stderr=proc.stderr)
  easyproc.CalledProcessError: Command 'ls foo' returned non-zero exit status 2.
  Command 'ls foo' returned non-zero exit status 2.
  >>> # crash when something doesn't work. You can either handle the
  >>> # error or set check=False
  ...
  >>> ep.run('ls foo', check=False)
  ls: cannot access 'foo': No such file or directory
  CompletedProcess(args='ls foo', returncode=None)
  >>>
  >>> # normal concurrent stuff with Popen also works. Unicode defaults.
  >>> proc = ep.Popen('tr a-z A-Z', stdin=ep.PIPE, stdout=ep.PIPE)
  >>> proc.communicate('foo')
  ('FOO', None)
  >>> proc.poll()
  0

So all that stuff should look pretty standard from ``subprocess`` usage.
Aside from the differences mentioned above, ``easyproc.Popen`` is more
or less identical to ``subprocess.Popen``, so consult the `API docs`_
for more info.

.. _API docs:
  https://docs.python.org/3/library/subprocess.html#popen-constructor

Output Streams
--------------
As seen above, the ``run`` function works similarly to the
``subprocess`` equivalent. However, when you capture the output, you
don't get text on the ``.stdout`` and ``.strerr`` attributes. The proper
way to think of Unix command output is not blocks of text, but rather
streams of lines, like a text file. (These lines may contain fields, but
that isn't the concern of ``easyproc``).

For this reason, process output is a ``ProcStream`` instance. If you use
``str()`` on it, you get the string of the process output. However, if
you iterate on it, you get lines from the file (with trailing newline
removed). It also has a context manager, but you won't need to access it
directly if you use either of those forms patterns.

.. code:: python

  >>> import easyproc as ep
  >>> procstream = ep.run("ls -sh", stdout=ep.PIPE).stdout
  >>> # ^ PIPE constant has same usage as in subprocess
  ... 
  >>> for line in procstream:
  ...     print(repr(line))
  ... 
  'total 48K'
  '4.0K easyproc.egg-info'
  ' 12K easyproc.py'
  ' 20K LICENSE'
  '4.0K __pycache__'
  '4.0K README.rst'
  '4.0K setup.py'
  >>> # the stream is used up after you iterate on it.
  ...
  >>> procstream = ep.run("ls -sh", stdout=ep.PIPE).stdout
  >>> print(procstream)
  total 52K
  4.0K easyproc.egg-info
   12K easyproc.py
   20K LICENSE
  4.0K __pycache__
  8.0K README.rst
  4.0K setup.py
  >>> # print calls str() implicitly.


