from printval import printval
def example():
a = 4
b = 5
printval | a
printval | 3*a*b
(
written to the console:
"a is 4"
"3*a*b is 60"
)
The entire code to make printval work (including expanding structures and classes) is only a few lines long:
import inspect, itertools
class PythonPrintVal(object):
def _output(self, name, val):
# expands a struct to one level.
print('printval| ' + name + ' is ' + repr(val))
if name == 'locals()':
for key in val:
if not key.startswith('_'):
self._output(key, val[key])
elif ' object at 0x' in repr(val):
for propname in dir(val):
if not propname.startswith('_'):
sval = repr(val.__getattribute__(propname))
print(' \t\t.'+propname+' = '+sval)
def __or__(self, val):
# look in the source code for the text
fback = inspect.currentframe().f_back
try:
with open(fback.f_code.co_filename, 'r') as srcfile:
line = next(itertools.islice(srcfile, fback.f_lineno-1, fback.f_lineno))
self._output(line.replace('printval|','',1).strip(), val)
except (StopIteration, IOError):
return self._output('?',val)
printval = PythonPrintVal()
Helpfully, it will enumerate through the fields of a class. And if you use printval| locals(), you get a more-nicely formatted representation of all locals in scope.
It's such a hack to be able to introspect *into the source code*. (In an earlier version, I used the syntax printval.a, so I could create chains like printval.a.b. This method doesn't require looking at source code -- the printval object knows the string 'a' from the parameter passed to __getattribute__, and retrieves its value from the scope of the caller. As a benefit this could work in compiled/frozen apps. The downside is that this doesn't allow expressions like printval|a+4)
I've since learned that other people have had similar ideas; there are modules for ScopeFormatter, Say, and Show.
I've added the source to GitHub here under the GPLv3.