X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8b9a4190f70909de9568f45389e7aa3ecbc66b8a..6ea1280cb72eca20ee34d0c76e660d5cc0a482b7:/wxPython/wx/tools/dbg.py diff --git a/wxPython/wx/tools/dbg.py b/wxPython/wx/tools/dbg.py index a09a0facc6..5c791f8864 100644 --- a/wxPython/wx/tools/dbg.py +++ b/wxPython/wx/tools/dbg.py @@ -1,8 +1,262 @@ +#---------------------------------------------------------------------------- +# Name: dbg.py +# RCS-ID: $Id$ +# Author: Will Sadkin +# Email: wsadkin@nameconnector.com +# Created: 07/11/2002 +# Copyright: (c) 2002 by Will Sadkin, 2002 +# License: wxWindows license +#---------------------------------------------------------------------------- +# 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) +# +# o V2.5 compatability update +# -"""Renamer stub: provides a way to drop the wx prefix from wxPython objects.""" +""" +This module provides a useful debugging framework that supports +showing nesting of function calls and allows a program to contain +lots of debugging print statements that can easily be turned on +or off to debug the code. It also supports the ability to +have each function indent the debugging statements contained +within it, including those of any other function called within +its scope, thus allowing you to see in what order functions are +being called, and from where. + +This capability is particularly useful in wxPython applications, +where exactly events occur that cause functions to be called is +not entirely clear, and because wxPython programs can't be run +from inside other debugging environments that have their own +message loops. + +This module defines a Logger class, responsible for managing +debugging output. Each Logger instance can be given a name +at construction; if this is done, ':' will precede each +logging output made by that Logger instance. + +The log() function this class provides takes a set of positional +arguments that are printed in order if debugging is enabled +(just like print does), followed by a set of keyword arguments +that control the behavior of the log() function itself on subsequent +calls. The current keyword arguments are: + +indent + When set to a value of 1, this increments the current + indentation level, causing all subsequent dbg() outputs to be + indented by 3 more spaces. When set to a value of 0, + this process is reversed, causing the indent to decrease by + 3 spaces. The default indentation level is 0. + +enable + When set to a value of 1, this turns on dbg() output for + for program importing this module, until told to do otherwise. + When set to a value of 0, dbg output is turned off. (dbg + output is off by default.) + +suspend + When set to a value of 1, this increments the current + "suspension" level. This makes it possible for a function + to temporarily suspend its and any of its dependents' + potential outputs that use the same Logger instance. + When set to a value of 0, the suspension level is + decremented. When the value goes back to 0, potential + logging is resumed (actual output depends on the + "enable" status of the Logger instance in question.) + +wxlog + When set to a value of 1, the output will be sent to the + active wxLog target. + +stream + When set to a non-None value, the current output stream + (default of sys.stdout) is pushed onto a stack of streams, + and is replaced in the dbg system with the specified stream. + When called with a value of None, the previous stream will + be restored (if stacked.) If set to None without previously + changing it will result in no action being taken. + +You can also call the log function implicitly on the Logger +instance, ie. you can type: + from wxPython.tools.dbg import Logger + dbg = Logger() + dbg('something to print') + +Using this fairly simple mechanism, it is possible to get fairly +useful debugging output in a program. Consider the following +code example: + +>>> d = {1:'a', 2:'dictionary', 3:'of', 4:'words'} +>>> dbg = dbg.Logger('module') +>>> dbg(enable=1) +module: dbg enabled +>>> def foo(d): +... dbg('foo', indent=1) +... bar(d) +... dbg('end of foo', indent=0) +... +>>> def bar(d): +... dbg('bar', indent=1) +... dbg('contents of d:', indent=1) +... l = d.items() +... l.sort() +... for key, value in l: +... dbg('%d =' % key, value) +... dbg(indent=0) +... dbg('end of bar', indent=0) +... +>>> foo(d) +module: foo + module: bar + module: contents of d: + module: 1 = a + module: 2 = dictionary + module: 3 = of + module: 4 = words + module: end of bar + module: end of foo +>>> + +""" + + +class Logger: + def __init__(self, name=None): + import sys + self.name = name + self._indent = 0 # current number of indentations + self._dbg = 0 # enable/disable flag + self._suspend = 0 # allows code to "suspend/resume" potential dbg output + self._wxLog = 0 # use wxLogMessage for debug output + self._outstream = sys.stdout # default output stream + self._outstream_stack = [] # for restoration of streams as necessary + + + def IsEnabled(): + return self._dbg + + def IsSuspended(): + return _suspend + + + def log(self, *args, **kwargs): + """ + This function provides a useful framework for generating + optional debugging output that can be displayed at an + arbitrary level of indentation. + """ + if not self._dbg and not 'enable' in kwargs.keys(): + return + + if self._dbg and len(args) and not self._suspend: + # (emulate print functionality) + strs = [str(arg) for arg in args] + output = ' '.join(strs) + if self.name: output = self.name+': ' + output + output = ' ' * 3 * self._indent + output + + if self._wxLog: + from wxPython.wx import wxLogMessage # (if not already imported) + wxLogMessage(output) + else: + self._outstream.write(output + '\n') + self._outstream.flush() + # else do nothing + + # post process args: + for kwarg, value in kwargs.items(): + if kwarg == 'indent': + self.SetIndent(value) + elif kwarg == 'enable': + self.SetEnabled(value) + elif kwarg == 'suspend': + self.SetSuspend(value) + elif kwarg == 'wxlog': + self.SetWxLog(value) + elif kwarg == 'stream': + self.SetStream(value) + + # aliases for the log function + dbg = log # backwards compatible + msg = log # + __call__ = log # this one lets you 'call' the instance directly + + + def SetEnabled(self, value): + if value: + old_dbg = self._dbg + self._dbg = 1 + if not old_dbg: + self.dbg('dbg enabled') + else: + if self._dbg: + self.dbg('dbg disabled') + self._dbg = 0 + + + def SetSuspend(self, value): + if value: + self._suspend += 1 + elif self._suspend > 0: + self._suspend -= 1 + + + def SetIndent(self, value): + if value: + self._indent += 1 + elif self._indent > 0: + self._indent -= 1 + + + def SetWxLog(self, value): + self._wxLog = value + + + def SetStream(self, value): + if value: + self._outstream_stack.append( self._outstream ) + self._outstream = value + elif value is None and len(self._outstream_stack) > 0: + self._outstream = self._outstream_stack.pop(-1) + + +#------------------------------------------------------------ + +if __name__ == "__main__": + import sys + import wx + + wx.Log_SetActiveTarget( wx.LogStderr() ) + logger = Logger('module') + dbg = logger.dbg + dbg(enable=1) + logger('test __call__ interface') + dbg('testing wxLog output to stderr:', wxlog=1, indent=1) + dbg('1,2,3...') + dbg('testing wx.LogNull:') + devnull = wx.LogNull() + dbg('4,5,6...') # shouldn't print, according to doc... + del devnull + dbg('(resuming to wx.LogStdErr)', '7,8,9...', indent=0) + dbg('disabling wx.Log output, switching to stderr:') + dbg(wxlog=0, stream=sys.stderr) + dbg(logger._outstream, 'switching back to stdout:') + dbg(stream=None) + dbg(logger._outstream ) + def foo(str): + dbg('foo:', indent=1) + dbg(str, indent=0) + foo('testing dbg inside function') + class bar(Logger): + def __init__(self, name): + Logger.__init__(self, name) + def enable(self, value): + self.dbg(enable=value) + def foo(self, str): + self.dbg('foo:', indent=1) + self.dbg(str, indent=0) + f = bar('class mixin') + f.foo("shouldn't print") + f.enable(1) + f.foo("should print") + dbg('test completed.', enable=0) + dbg('(double-checking ;-)') -from wx import _rename -from wxPython.tools import dbg -_rename(globals(), dbg.__dict__, modulename='tools.dbg') -del dbg -del _rename