]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/tools/dbg.py
Fixes for wxPopupTransientWindow for wxMSW. Includes Patch #1181190,
[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 from wxPython.tools.dbg import Logger
80 dbg = Logger()
81 dbg('something to print')
82
83 Using this fairly simple mechanism, it is possible to get fairly
84 useful debugging output in a program. Consider the following
85 code example:
86
87 >>> d = {1:'a', 2:'dictionary', 3:'of', 4:'words'}
88 >>> dbg = dbg.Logger('module')
89 >>> dbg(enable=1)
90 module: 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)
107 module: 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
121 class 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; handle unicode as best as possible:)
151 strs = []
152 for arg in args:
153 try:
154 strs.append(str(arg))
155 except:
156 strs.append(repr(arg))
157
158 output = ' '.join(strs)
159 if self.name: output = self.name+': ' + output
160 output = ' ' * 3 * self._indent + output
161
162 if self._wxLog:
163 from wxPython.wx import wxLogMessage # (if not already imported)
164 wxLogMessage(output)
165 else:
166 self._outstream.write(output + '\n')
167 self._outstream.flush()
168 # else do nothing
169
170 # post process args:
171 for kwarg, value in kwargs.items():
172 if kwarg == 'indent':
173 self.SetIndent(value)
174 elif kwarg == 'enable':
175 self.SetEnabled(value)
176 elif kwarg == 'suspend':
177 self.SetSuspend(value)
178 elif kwarg == 'wxlog':
179 self.SetWxLog(value)
180 elif kwarg == 'stream':
181 self.SetStream(value)
182
183 # aliases for the log function
184 dbg = log # backwards compatible
185 msg = log #
186 __call__ = log # this one lets you 'call' the instance directly
187
188
189 def SetEnabled(self, value):
190 if value:
191 old_dbg = self._dbg
192 self._dbg = 1
193 if not old_dbg:
194 self.dbg('dbg enabled')
195 else:
196 if self._dbg:
197 self.dbg('dbg disabled')
198 self._dbg = 0
199
200
201 def SetSuspend(self, value):
202 if value:
203 self._suspend += 1
204 elif self._suspend > 0:
205 self._suspend -= 1
206
207
208 def SetIndent(self, value):
209 if value:
210 self._indent += 1
211 elif self._indent > 0:
212 self._indent -= 1
213
214
215 def SetWxLog(self, value):
216 self._wxLog = value
217
218
219 def SetStream(self, value):
220 if value:
221 self._outstream_stack.append( self._outstream )
222 self._outstream = value
223 elif value is None and len(self._outstream_stack) > 0:
224 self._outstream = self._outstream_stack.pop(-1)
225
226
227 #------------------------------------------------------------
228
229 if __name__ == "__main__":
230 import sys
231 import wx
232
233 wx.Log_SetActiveTarget( wx.LogStderr() )
234 logger = Logger('module')
235 dbg = logger.dbg
236 dbg(enable=1)
237 logger('test __call__ interface')
238 dbg('testing wxLog output to stderr:', wxlog=1, indent=1)
239 dbg('1,2,3...')
240 dbg('testing wx.LogNull:')
241 devnull = wx.LogNull()
242 dbg('4,5,6...') # shouldn't print, according to doc...
243 del devnull
244 dbg('(resuming to wx.LogStdErr)', '7,8,9...', indent=0)
245 dbg('disabling wx.Log output, switching to stderr:')
246 dbg(wxlog=0, stream=sys.stderr)
247 dbg(logger._outstream, 'switching back to stdout:')
248 dbg(stream=None)
249 dbg(logger._outstream )
250 def foo(str):
251 dbg('foo:', indent=1)
252 dbg(str, indent=0)
253 foo('testing dbg inside function')
254 class bar(Logger):
255 def __init__(self, name):
256 Logger.__init__(self, name)
257 def enable(self, value):
258 self.dbg(enable=value)
259 def foo(self, str):
260 self.dbg('foo:', indent=1)
261 self.dbg(str, indent=0)
262 f = bar('class mixin')
263 f.foo("shouldn't print")
264 f.enable(1)
265 f.foo("should print")
266 dbg('test completed.', enable=0)
267 dbg('(double-checking ;-)')
268