stdin
/stdout
/stderr
Current Development Version:
.. image:: https://travis-ci.org/bskinn/stdio-mgr.svg?branch=master :target: https://travis-ci.org/bskinn/stdio-mgr
.. image:: https://codecov.io/gh/bskinn/stdio-mgr/branch/master/graph/badge.svg :target: https://codecov.io/gh/bskinn/stdio-mgr
Most Recent Stable Release:
.. image:: https://img.shields.io/pypi/v/stdio_mgr.svg :target: https://pypi.org/project/stdio-mgr
.. image:: https://img.shields.io/pypi/pyversions/stdio-mgr.svg
Info:
.. image:: https://img.shields.io/github/license/mashape/apistatus.svg :target: https://github.com/bskinn/stdio-mgr/blob/stable/LICENSE.txt
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/ambv/black
Have a CLI Python application?
Want to automate testing of the actual console input & output of your user-facing components?
stdio Manager
can help.
While some functionality here is more or less duplicative of
redirect_stdout
and redirect_stderr
in contextlib
within the standard library <https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout>
__,
it provides (i) a much more concise way to mock both stdout
and stderr
at the same time,
and (ii) a mechanism for mocking stdin
, which is not available in contextlib
.
First, install:
.. code::
$ pip install stdio-mgr
Then use!
All of the below examples assume stdio_mgr
has already
been imported via:
.. code::
>>> from stdio_mgr import stdio_mgr
Mock stdout
\ :
.. code::
>>> with stdio_mgr() as (in_, out_, err_):
... print('foobar')
... out_cap = out_.getvalue().replace(os.linesep, '\n')
>>> out_cap
'foobar\n'
>>> in_.closed and out_.closed and err_.closed
True
By default print
appends a newline <https://docs.python.org/3/library/functions.html#print>
__
after each argument, which is why out_cap
is 'foobar\n'
and not just 'foobar'
.
As currently implemented, stdio_mgr
closes all three mocked streams
upon exiting the managed context.
Mock stderr
\ :
.. code ::
>>> import warnings
>>> with stdio_mgr() as (in_, out_, err_):
... warnings.warn("foo has no bar")
... err_cap = err_.getvalue().replace(os.linesep, '\n')
>>> err_cap
'...UserWarning: foo has no bar\n...'
Mock stdin
\ :
The simulated user input has to be pre-loaded to the mocked stream.
Be sure to include newlines in the input to correspond to
each mocked Enter
keypress!
Otherwise, input
will hang, waiting for a newline
that will never come.
If the entirety of the input is known in advance,
it can just be provided as an argument to stdio_mgr
.
Otherwise, .append()
mocked input to in_
within the managed context as needed:
.. code::
>>> with stdio_mgr('foobar\n') as (in_, out_, err_):
... print('baz')
... in_cap = input('??? ')
...
... _ = in_.append(in_cap[:3] + '\n')
... in_cap2 = input('??? ')
...
... out_cap = out_.getvalue().replace(os.linesep, '\n')
>>> in_cap
'foobar'
>>> in_cap2
'foo'
>>> out_cap
'baz\n??? foobar\n??? foo\n'
The _ =
assignment suppresses print
\ ing of the return value
from the in_.append()
call--otherwise, it would be interleaved
in out_cap
, since this example is shown for an interactive context.
For non-interactive execution, as with unittest
, pytest
, etc.,
these 'muting' assignments should not be necessary.
Both the '??? '
prompts for input
and the mocked input strings
are echoed to out_
, mimicking what a CLI user would see.
A subtlety: While the trailing newline on, e.g., 'foobar\n'
is stripped
by input
, it is retained in out_
.
This is because in_
tees the content read from it to out_
before that content is passed to input
.
Want to modify internal print
calls
within a function or method?
In addition to mocking, stdio_mgr
can also be used to
wrap functions that directly output to stdout
/stderr
. A stdout
example:
.. code::
>>> def emboxen(func):
... def func_wrapper(s):
... from stdio_mgr import stdio_mgr
...
... with stdio_mgr() as (in_, out_, err_):
... func(s)
... content = out_.getvalue()
...
... max_len = max(map(len, content.splitlines()))
... fmt_str = '| {{: <{0}}} |\n'.format(max_len)
...
... newcontent = '=' * (max_len + 4) + '\n'
... for line in content.splitlines():
... newcontent += fmt_str.format(line)
... newcontent += '=' * (max_len + 4)
...
... print(newcontent)
...
... return func_wrapper
>>> @emboxen
... def testfunc(s):
... print(s)
>>> testfunc("""\
... Foo bar baz quux.
... Lorem ipsum dolor sit amet.""")
===============================
| Foo bar baz quux. |
| Lorem ipsum dolor sit amet. |
===============================
Available on PyPI <https://pypi.python.org/pypi/stdio-mgr>
__
(pip install stdio-mgr
).
Source on GitHub <https://github.com/bskinn/stdio-mgr>
. Bug reports
and feature requests are welcomed at the
Issues <https://github.com/bskinn/stdio-mgr/issues>
page there.
Copyright (c) 2018-2019 Brian Skinn
License: The MIT License. See LICENSE.txt <https://github.com/bskinn/stdio-mgr/blob/stable/LICENSE.txt>
__
for full license terms.