]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/tools/dbg.py
More lib and demo patches to drop the wx prefix (Jeff has been busy!)
[wxWidgets.git] / wxPython / wx / tools / dbg.py
CommitLineData
d14a1e28
RD
1#----------------------------------------------------------------------------
2# Name: dbg.py
3# RCS-ID: $Id$
4# Author: Will Sadkin
5# Email: wsadkin@nameconnector.com
6# Created: 07/11/2002
7# Copyright: (c) 2002 by Will Sadkin, 2002
8# License: wxWindows license
9#----------------------------------------------------------------------------
d4b73b1b
RD
10# 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)
11#
12# o V2.5 compatability update
13#
1fded56b 14
d14a1e28
RD
15"""
16This module provides a useful debugging framework that supports
17showing nesting of function calls and allows a program to contain
18lots of debugging print statements that can easily be turned on
19or off to debug the code. It also supports the ability to
20have each function indent the debugging statements contained
21within it, including those of any other function called within
22its scope, thus allowing you to see in what order functions are
23being called, and from where.
24
25This capability is particularly useful in wxPython applications,
26where exactly events occur that cause functions to be called is
27not entirely clear, and because wxPython programs can't be run
28from inside other debugging environments that have their own
29message loops.
30
31This module defines a Logger class, responsible for managing
32debugging output. Each Logger instance can be given a name
33at construction; if this is done, '<name>:' will precede each
34logging output made by that Logger instance.
35
36The log() function this class provides takes a set of positional
37arguments that are printed in order if debugging is enabled
38(just like print does), followed by a set of keyword arguments
39that control the behavior of the log() function itself on subsequent
40calls. The current keyword arguments are:
41
42indent
43 When set to a value of 1, this increments the current
44 indentation level, causing all subsequent dbg() outputs to be
45 indented by 3 more spaces. When set to a value of 0,
46 this process is reversed, causing the indent to decrease by
47 3 spaces. The default indentation level is 0.
48
49enable
50 When set to a value of 1, this turns on dbg() output for
51 for program importing this module, until told to do otherwise.
52 When set to a value of 0, dbg output is turned off. (dbg
53 output is off by default.)
54
55suspend
56 When set to a value of 1, this increments the current
57 "suspension" level. This makes it possible for a function
58 to temporarily suspend its and any of its dependents'
59 potential outputs that use the same Logger instance.
60 When set to a value of 0, the suspension level is
61 decremented. When the value goes back to 0, potential
62 logging is resumed (actual output depends on the
63 "enable" status of the Logger instance in question.)
64
65wxlog
66 When set to a value of 1, the output will be sent to the
67 active wxLog target.
68
69stream
70 When set to a non-None value, the current output stream
71 (default of sys.stdout) is pushed onto a stack of streams,
72 and is replaced in the dbg system with the specified stream.
73 When called with a value of None, the previous stream will
74 be restored (if stacked.) If set to None without previously
75 changing it will result in no action being taken.
76
77You can also call the log function implicitly on the Logger
78instance, ie. you can type:
79 from wxPython.tools.dbg import Logger
80 dbg = Logger()
81 dbg('something to print')
82
83Using this fairly simple mechanism, it is possible to get fairly
84useful debugging output in a program. Consider the following
85code example:
86
87>>> d = {1:'a', 2:'dictionary', 3:'of', 4:'words'}
88>>> dbg = dbg.Logger('module')
89>>> dbg(enable=1)
90module: dbg enabled
91>>> def foo(d):
92... dbg('foo', indent=1)
93... bar(d)
94... dbg('end of foo', indent=0)
95...
96>>> def bar(d):
97... dbg('bar', indent=1)
98... dbg('contents of d:', indent=1)
99... l = d.items()
100... l.sort()
101... for key, value in l:
102... dbg('%d =' % key, value)
103... dbg(indent=0)
104... dbg('end of bar', indent=0)
105...
106>>> foo(d)
107module: foo
108 module: bar
109 module: contents of d:
110 module: 1 = a
111 module: 2 = dictionary
112 module: 3 = of
113 module: 4 = words
114 module: end of bar
115 module: end of foo
116>>>
117
118"""
119
120
121class Logger:
122 def __init__(self, name=None):
123 import sys
124 self.name = name
125 self._indent = 0 # current number of indentations
126 self._dbg = 0 # enable/disable flag
127 self._suspend = 0 # allows code to "suspend/resume" potential dbg output
128 self._wxLog = 0 # use wxLogMessage for debug output
129 self._outstream = sys.stdout # default output stream
130 self._outstream_stack = [] # for restoration of streams as necessary
131
132
133 def IsEnabled():
134 return self._dbg
135
136 def IsSuspended():
137 return _suspend
138
139
140 def log(self, *args, **kwargs):
141 """
142 This function provides a useful framework for generating
143 optional debugging output that can be displayed at an
144 arbitrary level of indentation.
145 """
146 if not self._dbg and not 'enable' in kwargs.keys():
147 return
148
149 if self._dbg and len(args) and not self._suspend:
150 # (emulate print functionality)
151 strs = [str(arg) for arg in args]
152 output = ' '.join(strs)
153 if self.name: output = self.name+': ' + output
154 output = ' ' * 3 * self._indent + output
155
156 if self._wxLog:
157 from wxPython.wx import wxLogMessage # (if not already imported)
158 wxLogMessage(output)
159 else:
160 self._outstream.write(output + '\n')
161 self._outstream.flush()
162 # else do nothing
163
164 # post process args:
165 for kwarg, value in kwargs.items():
166 if kwarg == 'indent':
167 self.SetIndent(value)
168 elif kwarg == 'enable':
169 self.SetEnabled(value)
170 elif kwarg == 'suspend':
171 self.SetSuspend(value)
172 elif kwarg == 'wxlog':
173 self.SetWxLog(value)
174 elif kwarg == 'stream':
175 self.SetStream(value)
176
177 # aliases for the log function
178 dbg = log # backwards compatible
179 msg = log #
180 __call__ = log # this one lets you 'call' the instance directly
181
182
183 def SetEnabled(self, value):
184 if value:
185 old_dbg = self._dbg
186 self._dbg = 1
187 if not old_dbg:
188 self.dbg('dbg enabled')
189 else:
190 if self._dbg:
191 self.dbg('dbg disabled')
192 self._dbg = 0
193
194
195 def SetSuspend(self, value):
196 if value:
197 self._suspend += 1
198 elif self._suspend > 0:
199 self._suspend -= 1
200
201
202 def SetIndent(self, value):
203 if value:
204 self._indent += 1
205 elif self._indent > 0:
206 self._indent -= 1
207
208
209 def SetWxLog(self, value):
210 self._wxLog = value
211
212
213 def SetStream(self, value):
214 if value:
215 self._outstream_stack.append( self._outstream )
216 self._outstream = value
217 elif value is None and len(self._outstream_stack) > 0:
218 self._outstream = self._outstream_stack.pop(-1)
219
220
221#------------------------------------------------------------
222
223if __name__ == "__main__":
d4b73b1b
RD
224 import sys
225 import wx
226
227 wx.Log_SetActiveTarget( wx.LogStderr() )
d14a1e28
RD
228 logger = Logger('module')
229 dbg = logger.dbg
230 dbg(enable=1)
231 logger('test __call__ interface')
232 dbg('testing wxLog output to stderr:', wxlog=1, indent=1)
233 dbg('1,2,3...')
d4b73b1b
RD
234 dbg('testing wx.LogNull:')
235 devnull = wx.LogNull()
d14a1e28
RD
236 dbg('4,5,6...') # shouldn't print, according to doc...
237 del devnull
d4b73b1b
RD
238 dbg('(resuming to wx.LogStdErr)', '7,8,9...', indent=0)
239 dbg('disabling wx.Log output, switching to stderr:')
d14a1e28
RD
240 dbg(wxlog=0, stream=sys.stderr)
241 dbg(logger._outstream, 'switching back to stdout:')
242 dbg(stream=None)
243 dbg(logger._outstream )
244 def foo(str):
245 dbg('foo:', indent=1)
246 dbg(str, indent=0)
247 foo('testing dbg inside function')
248 class bar(Logger):
249 def __init__(self, name):
250 Logger.__init__(self, name)
251 def enable(self, value):
252 self.dbg(enable=value)
253 def foo(self, str):
254 self.dbg('foo:', indent=1)
255 self.dbg(str, indent=0)
256 f = bar('class mixin')
257 f.foo("shouldn't print")
258 f.enable(1)
259 f.foo("should print")
260 dbg('test completed.', enable=0)
261 dbg('(double-checking ;-)')
1fded56b 262