Welcome, guest | Sign In | My Account | Store | Cart

Nasty way to get your debugging functions available everywhere.

Python, 110 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
__all__=[]
_rgb_files = [open('/tmp/rgb_debug.txt','w')]
import sys, time
_rgb_auto_flush=1
def rgb_time(offs=1):
    t = time.time()
    if offs: return t-_rgb_t0
    return t
_rgb_t0=rgb_time(0)
def rgb_debug(*msg,**kwds):
    msg = ' '.join(map(str,msg)) + '\n'
    for f in _rgb_files:
        f.write(msg)
        if _rgb_auto_flush:
            flush = getattr(f,'flush',None)
            if flush: flush()
def rgb_pprint(*obj):
    if obj:
        if isinstance(obj[0],(str,unicode)):
            rgb_debug(obj[0])
            obj = obj[1:]
        import pprint
        for o in obj:
            for f in _rgb_files:
                pprint.pprint(o,f)

def rgb_stack(*msg,**kwds):
    import inspect
    rgb_debug(*msg)
    i = 1
    while 1:
        f = sys._getframe(i)
        if f.f_globals.get('_rgb_t0',None) is not _rgb_t0: break
        i += 1
    F = inspect.stack()
    frameCount = kwds.get('_frameCount',0) 
    showLocals = kwds.get('_showLocals',0) 
    if not frameCount:
        F = F[i:]
    else:
        F = F[i:i+frameCount]
    for f in F:
        rgb_debug('file:',f[1],'line:',f[2],'in',f[3])
        for l in f[4]: rgb_debug(l)
        if showLocals:
            rgb_debug('    locals=%r' % f[0].f_locals)

class _RGB_Wrapper(object):
    def __init__(self,func,funcname=None,show=0,show_kwds={}):
        self.func = func
        self.funcname = funcname or func.__name__
        if not callable(show):
            show=show and rgb_stack or rgb_debug
        self.show = show
        self.show_kwds= show_kwds
        self.called = 0
    def __call__(self,*args,**kwds):
        func = self.func
        if not self.called:
            self.called = 1
            try:
                self.show('%s(*%r,**%r) called' % (self.funcname,args,kwds),**self.show_kwds)
            finally:
                self.called = 0
        return func(*args,**kwds)

def rgb_wrap(func,show=1,funcname=None,show_kwds={}):
    return _RGB_Wrapper(func,funcname,show,show_kwds=show_kwds)

class rgb_watch_writes:
    def __init__(self,f,cb,*cbargs):
        self._f = f
        self._cb = cb
        self._cbargs = cbargs
    def write(self,msg):
        self._cb(*((msg,)+self._cbargs))
        self._f.write(msg)
    def __getattr__(self,a):
        return getattr(self._f,a)

def rgb_print_exc(*msg):
    if msg: rgb_debug(*msg)
    import traceback
    for f in _rgb_files: traceback.print_exc(file=f)
    if _rgb_auto_flush: rgb_flush()

def rgb_add_file(f):
    if f not in _rgb_files: _rgb_files.append(f)

def rgb_auto_flush(v=1):
    _rgb_auto_flush=v

def rgb_flush():
    for f in _rgb_files:
        flush = getattr(f,'flush',None)
        if flush: flush()
    
import __builtin__
__builtin__.rgb_debug = rgb_debug
__builtin__.rgb_stack = rgb_stack
__builtin__.rgb_pprint = rgb_pprint
__builtin__.rgb_time = rgb_time
__builtin__.rgb_print_exc = rgb_print_exc
__builtin__.rgb_auto_flush = rgb_auto_flush
__builtin__.rgb_flush = rgb_flush
__builtin__.rgb_wrap = rgb_wrap
__builtin__.rgb_add_file = rgb_add_file
__builtin__.rgb_watch_writes = rgb_watch_writes
rgb_debug.__module__=sys.modules['rgb_debug']
sys.modules['rgb_debug'] = rgb_debug

I often have to debug applications run by other processes eg apache. I want to put print statements in, but they're not allowed in the process context. This recipe allows me to do

import rgb_debug rgb_debug('this is a debug',i,j)

and get the values dumped out into a file called /tmp/rgb_debug.txt; the recipe uses __builtin__ to make the various functions immediately available everywhere in the application. A typical usage might be to insert the import rgb_debug into the start cgi_script and then the functions can be used elsewhere without doing imports.

Another usage is to find out where a particular print gets done eg

import rgb_debug def print_cb(msg): if msg!='debug:': rgb_stack('found garbage:'+msg) os._exit(1) sys.stdout = rgb_watch_writes(sys.stdout,print_cb)

this finds out where you left that print "debug:".

I suppose this kind of code will eventually get disallowed as it really abuses some common structures. I can't remember where the original idea came from, but I think it might have been John J Lee.

2 comments

Robin Becker (author) 15 years, 8 months ago  # | flag

Moronically I got the syntax wrong for the examples eg

#typical usage
import rgb_debug
rgb_debug('this is a debug',...) #call something we just imported weird huh?

#find out who prints something starting with 'debug:'
import rgb_debug
def print_cb(msg):
    if msg.startswith('debug:'):
        rgb_stack('found garbage:'+msg)
        os._exit(1)
sys.stdout = rgb_watch_writes(sys.stdout,print_cb)
kay schluehr 15 years, 8 months ago  # | flag

In EasyExtend I've written following function that maps module attributes into builtins.

def publish_as_builtin(module):
    import __builtin__
    if hasattr(module,"__publish__"):
        for name in module.__publish__:
            __builtin__.__dict__[name] = module.__dict__[name]

The __publish__ attribute is somewhat analog to the __all__ module attribute. It is more declarative which means that control logic is avoided in the module scope. Otherwise one has to care for the function to be actually called. This can be achieved within an import_hook for example.