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