Skip to content

Commit 82f4441

Browse files
author
Jeff Weiss
committed
Add depth control
1 parent 8f3c412 commit 82f4441

File tree

2 files changed

+60
-31
lines changed

2 files changed

+60
-31
lines changed

function_trace/__init__.py

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
'''
3030

3131
from contextlib import contextmanager, closing
32-
from inspect import isclass
32+
from inspect import isclass, ismethod
3333
import threading
3434
import os
3535

@@ -59,8 +59,36 @@ def format_output(self, level, returnval):
5959
class Tracer(threading.local):
6060
def __init__(self, formatter=None):
6161
self.level = 0
62+
self.max_depth = None
6263
self.formatter = formatter or Formatter()
6364

65+
def trace(self, f, args, kwargs, additional_depth=None):
66+
prev_max = self.max_depth
67+
try:
68+
if additional_depth is not None: # None means unlimited
69+
total_depth = self.level + additional_depth
70+
if self.max_depth is not None:
71+
self.max_depth = min(self.max_depth, total_depth)
72+
else:
73+
self.max_depth = total_depth
74+
if (self.max_depth is None or (self.level < self.max_depth)):
75+
self.trace_in(f, args, kwargs)
76+
self.level += 1
77+
78+
try:
79+
r = f(*args, **kwargs)
80+
except Exception as e:
81+
r = e # print the exception as the return val
82+
raise
83+
finally:
84+
self.level -= 1
85+
self.trace_out(r)
86+
return r
87+
else:
88+
return f(*args, **kwargs)
89+
finally:
90+
self.max_depth = prev_max
91+
6492
def close(self):
6593
pass
6694

@@ -69,18 +97,11 @@ class StdoutTracer(Tracer):
6997
def __init__(self):
7098
super(StdoutTracer, self).__init__()
7199

72-
def trace(self, f, *args, **kwargs):
100+
def trace_in(self, f, args, kwargs):
73101
print self.formatter.format_input(self.level, f, args, kwargs)
74-
self.level += 1
75-
try:
76-
r = f(*args, **kwargs)
77-
except Exception as e:
78-
r = e # print the exception as the return val
79-
raise
80-
finally:
81-
self.level -= 1
82-
print self.formatter.format_output(self.level, r)
83-
return r
102+
103+
def trace_out(self, r):
104+
print self.formatter.format_output(self.level, r)
84105

85106

86107
class PerThreadFileTracer(Tracer):
@@ -91,26 +112,19 @@ def __init__(self, filename=None):
91112
os.makedirs(d)
92113
self.outputfile = open(filename, 'w')
93114

94-
def trace(self, f, *args, **kwargs):
115+
def trace_in(self, f, *args, **kwargs):
95116
self.outputfile.write(self.formatter.format_input(self.level, f, args, kwargs) + "\n")
96-
self.level += 1
97-
try:
98-
r = f(*args, **kwargs)
99-
except Exception as e:
100-
r = e # print the exception as the return val
101-
raise
102-
finally:
103-
self.level -= 1
104-
self.outputfile.write(self.formatter.format_output(self.level, r) + "\n")
105-
return r
117+
118+
def trace_out(self, r):
119+
self.outputfile.write(self.formatter.format_output(self.level, r) + "\n")
106120

107121
def close(self):
108122
self.outputfile.close()
109123

110124

111-
def add_trace(f, tracer):
125+
def add_trace(f, tracer, depth=None):
112126
def traced_fn(*args, **kwargs):
113-
return tracer.trace(f, *args, **kwargs)
127+
return tracer.trace(f, args, kwargs, additional_depth=depth)
114128
traced_fn.trace = True # set flag so that we don't add trace more than once
115129
return traced_fn
116130

@@ -125,22 +139,37 @@ def traceable(f):
125139
and not getattr(f, 'trace', None) # already being traced
126140

127141

142+
def _get_func(m):
143+
'''Returns function given a function or method'''
144+
if ismethod(m):
145+
return m.im_func
146+
else:
147+
return m
148+
149+
128150
@contextmanager
129-
def trace_on(objs, include_hidden=False, tracer=None, skip=None):
151+
def trace_on(objs, include_hidden=False, tracer=None, depths=None):
130152
tracer = tracer or StdoutTracer()
131153
origs = {}
132-
skip = skip or []
154+
depths = depths or {}
155+
156+
# converts methods to functions, since that is what's in __dict__
157+
f_depths = {}
158+
for (k, v) in depths.items():
159+
f_depths[_get_func(k)] = v
160+
depths = f_depths
161+
133162
for o in objs:
134163
replacements = {}
135164
for k in o.__dict__.keys():
136165
v = o.__dict__[k]
137166
if traceable(v) and getattr(v, '__name__', None) is not '__repr__' \
138-
and v not in skip \
167+
and (v not in depths or depths[v] >= 0) \
139168
and (include_hidden or
140169
not (include_hidden or k.startswith("_"))):
141170
replacements[k] = v
142-
# print "Replacing: " + k
143-
setattr(o, k, add_trace(v, tracer))
171+
# print "Replacing: %s %s , depth %s" % (k, v, depths.get(v, None))
172+
setattr(o, k, add_trace(v, tracer, depth=depths.get(v, None)))
144173
origs[o] = replacements
145174
# print origs
146175
with closing(tracer):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from setuptools import setup, find_packages
22
setup(
33
name="function_trace",
4-
version="0.3",
4+
version="1.0",
55
packages=find_packages(),
66

77
# metadata for upload to PyPI

0 commit comments

Comments
 (0)