show¶
show
provides simple, effective debug printing.
Every
language has features to print text, but they’re seldom optimized for
printing debugging information. show
is. It provides a simple, DRY
mechanism to “show what’s going on”–to identify what
values are associated with program variables in a neat,
labeled fashion. For instance:
from show import show
x = 12
nums = list(range(4))
show(x, nums)
yields:
x: 12 nums: [0, 1, 2, 3]
You could of course get the same output with Python’s standard
print
statement/function:
print("x: {} nums: {}".format(x, nums))
But that’s much more verbose, and unlike show
, it’s fails the
DRY principle.
But while avoiding a few extra characters of typing and a little extra
program complexity is nice–very nice, actually–show
goes well beyond
that. It has methods to show all local variables which have recently
changed, to trace the parameters and return values of function calls, and
other useful information that you simply cannot get without a lot of
needless extra work and a lot of extra lines mucking up your program source.
And if you run show.prettyprint()
, values will be
automagically highlighted with Pygments, which is
very helpful when you’re displaying complex objects, dictionaries and
other structures.
As just one exmaple, if you have a lot of output flowing by, and it’s hard to see your debugging output, just:
show(x, y, z, style='red')
And now you have debug output that clearly stands out from the rest. Or
show.set(style='blue')
and now all your debug output is colorized.
But “debug printing is so very 1989!” you may say. “We now have logging, logging, embedded assertions, unit tests, interactive debuggers. We don’t need debug printing.”
Have to disagree with you there. All those tools are grand, but often the fastest, simplest way to figure out what’s going in a program is to print values and watch what happens the program runs. Having a simple, effective way to do that doesn’t replacing logging, assertsions, unit testing, and debuggers; it’s a effective complement to them. One that is especially useful in two parts of the development process:
- In exploratory programming, where the values coming back from new or external functions (say, some package’s API with which you may not be intimately familiar) aren’t well-known to you.
- In debugging, where the assumptions embedded into the code are clearly, at some level, not being met. Else you wouldn’t need to debug.
In either case, knowing what values are actually happening, and figuring them out without a lot of extra effort or complexity–well, it doesn’t matter how many unit tests or logging statements you have, that’s still of value.
Every
language has features to print text, but they’re seldom optimized for
printing debugging information. show
is. It provides a simple, DRY
mechanism to “show what’s going on.”
Diving In¶
Sometimes programs print so that users can see things, and sometimes they print
so that developers can. show()
is for developers, helping
rapidly print the current state of variables. A simple invocation:
show(x)
replaces require the craptastic repetitiveness of:
print "x: {0}".format(x)
If you’d like to see where the data is being produced,:
show.set(where=True)
show(d)
will turn on location reporting, such as:
__main__():21: d: 'this'
The where
property, along with most every option, can be set
permanently, over the scope of a where
block, or on
a call-by-call basis.
show
is built atop the options module
for configuration management, and the output management of
say. All say
options are available. If you
show()
a literal string, it will be interpolated as it would be in say
:
show("{n} iterations, still running")
yields something like:
14312 iterations, still running
But:
s = '{n} iterations'
show(s)
yields:
s: '{n} iterations'
See say
say for additional detail on its
operation. show
directly supports many say
methods such as
blank_lines
, hr
, sep
, and title
which are meant to simplify
and up-level common formatting tasks.
This Just In¶
A new capability is to differentially set the formatting parameters on a method by method basis. For example, if you want to see separators in green and function call/return annotations in red:
show.sep.set(style='green')
show.inout.set(style='red')
You could long do this on a call-by-call basis, but being able to set the
defaults just for specific methods allows you to get more formatting in
with fewer characters typed. This capability is available on a limited
basis: primarily for format-specific calls (blanklines
, hr
, sep
,
and title
) and for one core inspection call (the inout
decorator).
It will be extended, and mapped back to underlying say
and options
features over time.
Collections and Items¶
The goal of show
is to provide the most useful information possible,
in the quickest and simplest way. Not requiring programmers to explicitly
restate values and names in print statements is the start, but it also
provides some additional functions that provide a bit more semantic value.
For example, say.items()
is designed to make printing collections easy.
It shows not just the values, but also the cardinality (i.e., length) of the
collection:
nums = list(range(4))
show.items(nums)
yields:
nums (4 items): [0, 1, 2, 3]
Object Properties¶
show.props(x)
shows the properties of object x
. (“Properties” here
is generic language for “values” or “attributes” associated with
an object, and isn’t used in the technical sense of Python properties.)
Properties will be listed alphabetically, but with those starting with underscores
(_
), usually indicating “private” data, sorted after those that are
conventionally considered public.
If x
has real @property
members, those too displayed. However, other class
attributes that x
rightfully inherits, but that are not directly present in the
x
instance, will not be displayed.
An optional second parameter can determine which properties are shown. E.g.:
show.props(x, 'name,age')
Or if you prefer the keyword syntax, this is equivalent to:
show(x, props='name,age')
Or if you’d like all properties except a few:
show.props(x, omit='description blurb')
Wax On, Wax Off¶
Often it’s convenient to only display debugging information under some conditions,
but not others,
such as when a debug
flag is set. That often leads to multi-line conditionals
such as:
if debug:
print "x:", x, "y:", y, "z:", z
With show
it’s a bit easier. There’s a keyword argument, also called
show
, that controls whether anything is shown. If it’s truthy, it shows;
falsy, ad it doesn’t:
show(x, y, z, show=debug)
You can set the show flag more globally:
show.set(show=False)
You can also make multiple show
instances that can be separately controlled:
show_verbose = show.clone()
show_verbose.set(show=verbose_flag)
show_verbose(x, y, z)
For a more fire-and-forget experience, try setting visibility with a lambda parameter:
debug = True
show.set(show=lambda: debug)
Then, whenever debug
is truthy, values will be shown. When debug
is
falsy, values will not be shown.
When you really, truly want show
‘s output to
disappear, and want to minimize
overhead, but don’t want to
change your source code (lest you need those debug printing statements again
shortly), try:
show = noshow
This one line will replace the show
object (and any of its clones) with
parallel NoShow
objects that simply don’t do anything or print any output.
Note
This assignment should be done in a global context. If done inside a
function, you’ll need to add a corresponding global show
declaration.
As an alternative, you can:
from show import show
from show import noshow as show
Then comment out the``noshow`` line for debugging, or the show
line for production
runs.
Note
A little care is required to configure global non-showing behavior
if you’re using show
‘s function decorators such as @show.inout
.
Decorators are evaluated earlier in program execution than the “main flow”
of program execution, so it’s a good idea to define the lambda or noshow
control of visibility at the top of your program.
How Things Are Shown¶
By default, show
uses Python’s repr()
function to format
values. You may prefer some other kind of representation or formatting,
however. For example, the pprint
module pretty-prints data structures.
You can set it to be the default formatter:
from pprint import pformat
show.set(fmtfunc=pformat)
Or to configure separate data and code formatters:
show.set(fmtfunc=lambda x: pformat(x, indent=4, width=120, depth=5))
show.set(fmtcode=lambda x: highlight(x, ...))
As a convenience, the show.prettyprint()
configures
pygments
and
pprint
in concert to more attractively display text on
ANSI terminals. Just run it once after importing show
.
It also takes indent
, depth
, and width
options
for pformat
and the style
(style name) option for pygments
.
Some style names to try:
# monokai manni rrt perldoc borland colorful default
# murphy vs trac tango fruity autumn bw emacs vim pastie
# friendly native
Where Am I?¶
In addition to the where
parameter that may be turned on for each
show
call (or in general), there is a method:
show.where()
intended to display a “flag” or indicator in output where particular debugging output originated. You may not want location on all the time, but an occasional oriented signpost can help.
What’s Changed¶
show.changed()
will display the value of local variables. When invoked again, only those
variables that have changed (since the last show.changed()
in the same context)
will be displayed. For example:
def f():
x = 4
show.changed()
x += 1
retval = x * 3
show.changed()
return retval
When run will display:
x: 4
x: 5 retval: 15
You may omit
some local variables if you like.
By default, those starting with underscores (_
) will be omitted, as
will those containing functions, methods, builtins, and other parts Python
program infrastructure. If you’d like to add those, or global variables into
the mix, that’s easily done:
show.changed(_private, MY_GLOBAL_VAR)
Will start watching those.
Function Call and Return¶
It’s often helpful to know what a function’s parameters and corresponding return values were, and it can be annoying to manually print them out. No matter. Show has two decorators to make this easy:
@show.inout
def g(a):
b = 3
a += b
return a
g(4)
Displays:
g(a=4)
g(a=4) -> 7
The first line indicates the function being called. Additional debugging
or program output may follow it.
The second line here is displayed when the
function returns. It reminds us what the parameters were, and then shows
what return value resulted. If you like, you can specify the styling
of these calls, e.g. with @show.inout(style='red')
.
While printing both the call entry and exit is often helpful, especially
if many lines of output (or potential program crashes) may intervene. But
in cases where a more compact, “only the results, please” print is desired,
show takes a parameter that will show only function returns:
@show.inout(only="out")
. Function calls sans
returns will be show if only='in'
.
You may find it useful that inout
is an individually-styleable method.
To highlight function entry and exit points, try:
show.inout.set(style='red')
Note
The @show.retval
decorator has been deprecated, and will soon
be removed. Please shift to @show.inout
variants instead.
Discovering What’s There¶
It’s often helpful to figure out “what am I dealing with here? what attributes
or methods or properties are available to me?” This is where show.dir
comes
into play. You could do show(dir(x))
, but show.dir(x)
will provides more
information, and does so more compactly. It also allows you to filter out the
often huge hubbub of some objects. By default, it doesn’t show any attributes
starting with double underscore __
. You can control what’s omitted with the
omit
keyword argument. show.dir(x, omit=None)
shows everything, while
show.dir(x, omit='_* proxy*')
omits all the methods starting with an
underscore or the word “proxy.”
Interactive Limitations¶
As of version 1.4, show
has good support for IPython, either running in a
terminal window or in a Jupyter Notebook.
It’s support for the plain interactive Python REPL is much weaker. Call it experimental. It works well at the interactive prompt, and within imported modules. It cannot, however, be used within functions and classes defined within the interactive session. This is a result of how Python supports–or rather, fails to support–introspection for interactively-defined code. Whether this is a hard limit, or something that can be worked around over time, remains to be seen. (See e.g. this discussion).
Python under Windows does not support readline the same way it is supported on
Unix, Linux, and Mac OS X. Experimental support is provided for the use of
pyreadline
under Windows to correct this variance. This feature is yet
untested. Works/doesn’t work reports welcome!
If you want to work interactively, strongly advise you do so uner IPython not the stock REPL. Even better, in Jupyter Notebook, which is an excellent work environment in a way that the stock REPL never will be.
API¶
Debugging print features.
-
class
show.core.
NoShow
(**kwargs)¶ A Show variant that shows nothing. Maintains just enough context to respond as a real Show would. Any clones will also be ``NoShow``s–again, to retain similarity. Designed to squelch all output in efficient way, but not requiring any changes to the source code. Maintains just enough context to
-
blank_lines
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
changed
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
clone
(**kwargs)¶ Create a child instance whose options are chained to this instance’s options (and thence to Show.options). kwargs become the child instance’s overlay options. Because of how the source code is parsed, clones must be named via simple assignment.
-
dir
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
hr
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
inout
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
items
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
locals
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
props
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
retval
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
title
(*args, **kwargs)¶ Fake entry point. Does nothing, returns immediately.
-
-
class
show.core.
Show
(**kwargs)¶ Show objects print debug output in a ‘name: value’ format that is convenient for discovering what’s going on as a program runs.
-
arg_format
(name, value, caller, opts)¶ Format a single argument. Strings returned formatted.
-
arg_format_dir
(name, value, caller, opts)¶ Format a single argument to show items of a collection.
-
arg_format_items
(name, value, caller, opts)¶ Format a single argument to show items of a collection.
-
arg_format_props
(name, value, caller, opts, ignore_funky=True)¶ Format a single argument to show properties.
-
call_location
(caller)¶ Create a call location string indicating where a show() was called.
-
changed
(*args, **kwargs)¶ Show the local variables, then again only when changed.
-
clone
(**kwargs)¶ Create a child instance whose options are chained to this instance’s options (and thence to Show.options). kwargs become the child instance’s overlay options. Because of how the source code is parsed, clones must be named via simple assignment.
-
code_repr
(code)¶ Return a formatted string for code. If there are any internal brace characters, they are doubled so that they are not interpreted as format template characters when the composed string is eventually output by
say
.
-
dir
(*args, **kwargs)¶ Show the attributes possible for the given object(s)
-
get_arg_tuples
(caller, values)¶ Return a list of argument (name, value) tuples. :caller: The calling frame. :values: The with the given values.
-
inout
(*dargs, **dkwargs)¶ Show arguments to a function. Decorator itself may take arguments, or not. Whavevs.
-
items
(*args, **kwargs)¶ Show items of a collection.
-
locals
(*args, **kwargs)¶ Show all local vars, plus any other values mentioned.
-
method_push
(base_options, method_name, kwargs)¶ Transitional helper function to simplify the grabbing of method-specific arguments. Will be phased out as the learnings about method arguments are piped back into Options and a more long-term API is completed.
-
pprint
(*args, **kwargs)¶ Show the objects as displayed by pprint. Not well integrated as yet. Just a start.
-
prettyprint
(mode='ansi', sep=' ', indent=4, width=120, depth=5, style='friendly')¶ Convenience method to turn on pretty-printing. Mode can be text or ansi.
-
props
(*args, **kwargs)¶ Show properties of objects.
-
retval
(func)¶ Decorator that shows arguments to a function, and return value, once the function is complete. Similar to inout, but only displays once function has returned.
-
set
(**kwargs)¶ Change the values of the show.
-
value_repr
(value)¶ Return a
repr()
string for value that has any brace characters (e.g. fordict
–and in Python 3,set`--literals) doubled so that they are not interpreted as format template characters when the composed string is eventually output by ``say
.
-
where
(*args, **kwargs)¶ Show where program execution currently is. Can be used with normal output, but generally is intended as a marker.
-
Notes¶
show
is in its early days. Over time, it will provide additional context-specific output helpers. For example, the “diff” views ofpy.test
seem a high-value enhancement.show
depends on introspection, with its various complexities and limitations. It assumes that all objects are new-style classes, and that your program has not excessively fiddled with class data. Diverge from these assumptions, and all bets are off.- Automated multi-version testing managed with the wonderful pytest and tox. Successfully packaged for, and tested against, most late-model versions of Python: 2.7, 3.3, 3.4, 3.5, and 3.6, as well as PyPy 5.6.0 (based on 2.7.12) and PyPy3 5.5.0 (based on 3.3.5).
- The author, Jonathan Eunice or @jeunice on Twitter welcomes your comments and suggestions.
Installation¶
To install or upgrade to the latest version:
pip install -U show
To easy_install
under a specific Python version (3.4 in this example):
python3.4 -m easy_install --upgrade show
(You may need to prefix these with sudo
to authorize
installation. In environments without super-user privileges, you may want to
use pip
‘s --user
option, to install only for a single user, rather
than system-wide.)
Testing¶
If you wish to run the module tests locally, you’ll need to install
pytest
and tox
. For full testing, you will also need pytest-cov
and coverage
. Then run one of these commands:
tox # normal run - speed optimized
tox -e py27 # run for a specific version only (e.g. py27, py34)
tox -c toxcov.ini # run full coverage tests
The provided tox.ini
and toxcov.ini
config files do not define
a preferred package index / repository. If you want to use them with
a specific (presumably local) index, the -i
option will come in
very handy:
tox -i INDEX_URL
Change Log¶
1.4.7 (March 9, 2017)
Bumped test coverage to 80%. In the process, discovered and fixed some bugs withshow.props
. Most things that can be basically unit-tested, are. Largest remaining test coverage gaps concern operation under different I/O managers–esp. IPython and the standard Python REPL–that will require integration testing.
1.4.6 (March 1, 2017)
Quashed second bug related to IPython and its %run command, especially as used by the Enthought Canopy IDE.
1.4.5 (March 1, 2017)
Fixed problem with IPython when program run with the %run command. Fix esp. important for users of Enthought Canopy IDE, which uses this mode of execution extensively.
1.4.4 (February 19, 2017)
Tweakshow.prettyprint()
to not automatically multi-line all show output. If you want multi-line output, either setshow.prettyprint(sep='\m')
to globalize that preference, or useshow(..., sep='\n)
each time you want multi-line.
1.4.3 (February 2, 2017)
Bug fix: When show.set(prefix=...) or other settings were used, duplicate behaviors could occur, like two prefix strings printing, not just one. Also, support for Python 2.6 has been restored. Not that you should still be using that antiquated buggy. But if you are, show will once again work for you, given removal of the preventing dependency (stuf
).
1.4.2 (January 30, 2017)
Fixed bug when location display is active (e.g. after show.set(where=True)) in IPython. Now correctly identifies what cell code was executed in.
1.4.0 (January 27, 2017)
Finally have good support for IPython, either in a Notebook or in a terminal/console. Suddenly, interactive use does not minimize show’s usefulness (though the standard REPL still has glitches).
1.3.2 (January 26, 2017)
Fixes nasty packaging bug (failure to bundle astor sub-package into source distributions) that didn’t show up in testing.
1.3.0 (January 25, 2017)
Python 3.5 and 3.6 now pass formal verification. This required embedding a ‘nightly’ build of astor 0.6 that has not yet made it to PyPI. Given the shift from codegen to newer astor AST-to-source library, bumping minor version.
1.2.7 (January 23, 2017)
Updated dependencies to support Python 3.5 and 3.6. These versions do not yet pass formal validation, but they do seem to work in informal testing. This is the start of a push to fully support these most recent Python implementations, and to improve support for interactive code (REPL or Jupyter Notebook).
1.2.6 (September 1, 2015)
Tweaks and testing for new version of underlyingoptions
module that returns operation to Python 2.6.
1.2.4 (August 26, 2015)
Major documentation reorg.
1.2.3 (August 25, 2015)
Code cleanups and additional testing. Test coverage now 77%.
1.2.1 (August 21, 2015)
Added ability to give
@show.inout
decorator its own parameters. Deprecated@show.retval
, which is now redunant with@show.inout(only=''out'')
.Test coverage bumped to 71%.
1.2.0 (August 18, 2015)
Addedshow.where()
as a marker for “where am I now?” Improved docs, esp. forwhere
,inout
, andretval
methods. Improved testing. Now at 67% line coverage.
1.1.1 (August 17, 2015)
Updated testing strategy to integrate automated test coverage metrics. Immediate test and code improvements as a result. Initial coverage was 53%. Releasing now at 57%.
Clearly backed out Python 3.5 support for the moment. The AST
Call
signature has changed notably. Will need to deep-dive to fix that.
1.1.0 (August 16, 2015)
Fixed problem with underlyingsay
object interactions. Some doc and testing tweaks.
1.0.4 (July 22, 2015)
Updated config, docs, and testing matrix.
1.0.2 (September 16, 2013)
Improved pretty printing of code snippets for
@show.inout
and@show.retval
decorators.Made
show
also accept lambdas to link to variable values.Added
noshow
object for easy turning off of showing.General cleanups. Tightened imports. Tweaked docs. Switched to
FmtException
fromsay>=1.0.4
, and separated extensions into own module.Drove version information into
version.py
1.0.1 (September 9, 2013)
Moved main documentation to Sphinx format in ./docs, and hosted the long-form documentation on readthedocs.org. README.rst now an abridged version/teaser for the module.
1.0.0 (September 9, 2013)
Improved robustness for interactive use. If names cannot be detected, still gives value result with
?
pseudo-name.Improved type names for
show.dir
andshow.props
Improved
show.inout
with full call string on function return. A bit verbose in small tests, but too easy to lose “what was this called with??” context in real-scale usage unless there is clear indication of how the function was called.Improved omission of probably useless display properties via
omit
keyword.Began to add support for showing properties even when proxied through another object. Currently limited to selected SQLAlchemy and Flask proxies. More to come.
Cleaned up source for better (though still quite imperfect), PEP8 conformance
Bumped version number to 1.0 as part of move to semantic versioning, or at least enough of it so as to not screw up Python installation procedures (which don’t seem to understand 0.401 is a lesser version that 0.5, because 401 > 5).
Probably several other things I’ve now forgotten.