- Notifications
You must be signed in to change notification settings - Fork4
Context manager for mocking/wrapping stdin/stdout/stderr
License
bskinn/stdio-mgr
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Current Development Version:
Most Recent Stable Release:
Info:
Have a CLI Python application?
Want to automate testing of the actual console input & outputof your user-facing components?
stdio Manager can help.
While some functionality here is more or less duplicative ofredirect_stdout
andredirect_stderr
incontextlib
within the standard library,it provides (i) a much more concise way to mock bothstdout
andstderr
at the same time,and (ii) a mechanism for mockingstdin
, which is not available incontextlib
.
First, install:
$ pip install stdio-mgr
Then use!
All of the below examples assumestdio_mgr
has alreadybeen imported via:
>>> from stdio_mgr import stdio_mgr
Mockstdout
:
>>> 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_.closedTrue
By defaultprint
appends a newlineafter each argument, which is whyout_cap
is'foobar\n'
and not just'foobar'
.
As currently implemented,stdio_mgr
closes all three mocked streamsupon exiting the managed context.
Mockstderr
:
>>> 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...'
Mockstdin
:
The simulated user input has to be pre-loaded to the mocked stream.Be sure to include newlines in the input to correspond toeach mocked Enterkeypress!Otherwise,input
will hang, waiting for a newlinethat will never come.
If the entirety of the input is known in advance,it can just be provided as an argument tostdio_mgr
.Otherwise,.append()
mocked input toin_
within the managed context as needed:
>>> 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 suppressesprint
ing of the return valuefrom thein_.append()
call--otherwise, it would be interleavedinout_cap
, since this example is shown for an interactive context.For non-interactive execution, as withunittest
,pytest
, etc.,these 'muting' assignments should not be necessary.
Both the'??? '
prompts forinput
and the mocked input stringsare echoed toout_
, mimicking what a CLI user would see.
A subtlety: While the trailing newline on, e.g.,'foobar\n'
is strippedbyinput
, it isretained inout_
.This is becausein_
tees the content read from it toout_
before that content is passed toinput
.
Want to modify internalprint
callswithin a function or method?
In addition to mocking,stdio_mgr
can also be used towrap functions that directly output tostdout
/stderr
. Astdout
example:
>>> 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 onPyPI(pip install stdio-mgr
).
Source onGitHub. Bug reportsand feature requests are welcomed at theIssues page there.
Copyright (c) 2018-2019 Brian Skinn
License: The MIT License. SeeLICENSE.txtfor full license terms.
About
Context manager for mocking/wrapping stdin/stdout/stderr