Wiki

Clone wiki

decotrace / Home

decotrace

decotrace provides a function, method, and class decorator which sends messages about call arguments, return values, and exceptions to an arbitrary output function.

The default output function is to write to sys.stderr, but a common use case is to provide a logging context.

Installation

$ pip install decotrace

Example Usage

>>> from decotrace import traced
>>> @traced
... def f(x):
...   return (x, x)
... 
>>> f(42)
f(42)...
f(42) -> (42, 42)
(42, 42)

logging

Using custom logging output:

>>> import sys, logging, decotrace
>>> logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
>>> traced = decotrace.TraceContext(logging.getLogger('-decotrace-').debug)
>>> @traced
... def f(x):
...   return x * 2
... 
>>> f(7)
DEBUG:-decotrace-:f(7)...
DEBUG:-decotrace-:f(7) -> 14
14
>>> f(x='blah')
DEBUG:-decotrace-:f(x='blah')...
DEBUG:-decotrace-:f(x='blah') -> 'blahblah'
'blahblah'

Stack Depth

Each TraceContext tracks stack depth and indents messages accordingly:

>>> @traced
... def fact(n):
...   if n == 0:
...     return 1
...   else:
...     return n * fact(n-1)
... 
>>> fact(4)
DEBUG:-decotrace-:fact(4)...
DEBUG:-decotrace-:  fact(3)...
DEBUG:-decotrace-:    fact(2)...
DEBUG:-decotrace-:      fact(1)...
DEBUG:-decotrace-:        fact(0)...
DEBUG:-decotrace-:        fact(0) -> 1
DEBUG:-decotrace-:      fact(1) -> 1
DEBUG:-decotrace-:    fact(2) -> 2
DEBUG:-decotrace-:  fact(3) -> 6
DEBUG:-decotrace-:fact(4) -> 24
24

Class Decoration

>>> class A (object):
...   def do_a(self, x):
...     return ('a', x)
...   def do_b(self, y):
...     return ('b', y)
... 
>>> @traced
... class B (A):
...   def do_b(self, y):
...     return A.do_b(self, (y, y))
... 
>>> class C (B):
...   def do_a(self, x):
...     return ('C.do_a', x)
...   def do_b(self, y):
...     return B.do_b(self, y*23)
... 
>>> b=B()
>>> b.do_a('foo')
('a', 'foo')
>>> b.do_b('bar')
DEBUG:-decotrace-:B[@b7208f0c].do_b('bar')...
DEBUG:-decotrace-:B[@b7208f0c].do_b('bar') -> ('b', ('bar', 'bar'))
('b', ('bar', 'bar'))
>>> c = C()
>>> c.do_a(13)
('C.do_a', 13)
>>> c.do_b(7)
DEBUG:-decotrace-:C[@b7208f0c].do_b(161)...
DEBUG:-decotrace-:C[@b7208f0c].do_b(161) -> ('b', (161, 161))
('b', (161, 161))

Exploring Existing APIs

Using traced as a class decorator is often useful for quickly seeing how a given API works:

>>> import urllib2
>>> TracedOpenerDirector = traced(urllib2.OpenerDirector)
>>> urllib2.install_opener(TracedOpenerDirector())
DEBUG:-decotrace-:OpenerDirector[@b7208f0c].__init__()...
DEBUG:-decotrace-:OpenerDirector[@b7208f0c].__init__() -> None
>>> urllib2.urlopen('http://www.google.com')
DEBUG:-decotrace-:OpenerDirector[@b7208f0c].open('http://www.google.com', None, <object object at 0xb746c5b8>)...
DEBUG:-decotrace-:  OpenerDirector[@b7208f0c]._open(<urllib2.Request instance at 0xb71a7f4c>, None)...
DEBUG:-decotrace-:    OpenerDirector[@b7208f0c]._call_chain({}, 'default', 'default_open', <urllib2.Request instance at 0xb71a7f4c>)...
DEBUG:-decotrace-:    OpenerDirector[@b7208f0c]._call_chain({}, 'default', 'default_open', <urllib2.Request instance at 0xb71a7f4c>) -> None
DEBUG:-decotrace-:    OpenerDirector[@b7208f0c]._call_chain({}, 'http', 'http_open', <urllib2.Request instance at 0xb71a7f4c>)...
DEBUG:-decotrace-:    OpenerDirector[@b7208f0c]._call_chain({}, 'http', 'http_open', <urllib2.Request instance at 0xb71a7f4c>) -> None
DEBUG:-decotrace-:    OpenerDirector[@b7208f0c]._call_chain({}, 'unknown', 'unknown_open', <urllib2.Request instance at 0xb71a7f4c>)...
DEBUG:-decotrace-:    OpenerDirector[@b7208f0c]._call_chain({}, 'unknown', 'unknown_open', <urllib2.Request instance at 0xb71a7f4c>) -> None
DEBUG:-decotrace-:  OpenerDirector[@b7208f0c]._open(<urllib2.Request instance at 0xb71a7f4c>, None) -> None
DEBUG:-decotrace-:OpenerDirector[@b7208f0c].open('http://www.google.com', None, <object object at 0xb746c5b8>) -> None

Updated