#=======#=======#=======#=======#=======#=======#=======#=======#====== # -*- coding: utf-8 -*- # the above line is there in case Python2 (Python2.7) is used. #=======#=======#=======#=======#=======#=======#=======#=======#====== # debugtoys is a set of 4 functions that are useful for manually # debugging a Python script, with an easy way to enable or disable # their actions. when disabled, these functions do nothing and will # immediately return to the caller with no useful value. the set_up() # method returns a 4-tuple containing references to the four functions. #-------#-------#-------#-------#-------#-------#-------#-------#------ # debugging is on unless turned off by setting environment variables # 'debugtoysoff' or 'DEBUGTOYSOFF' to any value. to turn debugging # off in the code that imports this module, set one of those # environment variables to any string value. #-------#-------#-------#-------#-------#-------#-------#-------#------ # Python3 and Python2.7 (versions released from 2008) are supported. # other versions may also work. #-------#-------#-------#-------#-------#-------#-------#-------#------ # pf() like pr() below, but with arg1.format(arg2,arg3,...) # pr() do printing just like print() then flush the file it used. # pv() print variables, given their names, or report if undefined. # sl() sleep a specified time after flushing stdout or file= file. #-------#-------#-------#-------#-------#-------#-------#-------#------ # the pv() function is particularly helpful. call pv() with one or # more arguments naming variables to be printed. its name and value # will be printed if the variable is defined in the caller's local or # global namespace if the variable is not defined then a one line # message is produced explaining that the variable is not defined. # to help with debugging, pv() includes the line number of the source # code it came from on each line of output. if the arument string begins with # a '#' character, then the remainder of the string is a commwnt,not a # variable. a prefix for each line may be given as a named option on # the call to pv() or given in an argument as a string that begins # with the '=' character. #=======#=======#=======#=======#=======#=======#=======#=======#====== from os import environ from sys import stderr,stdout _debugging = True for name in ('debugtoysoff','debugtoysdisable','nodebugtoys'): if name.lower() in environ: _debugging = False if name.upper() in environ: _debugging = False if _debugging: from sys import stderr,stdout # flush multiple files def _flush(*a): stderr.flush() stdout.flush() for f in a: f.flush() return # like pr() but with formatting def pf(fs,*args,**opts): opts['file'] = opts.get('file',stderr) if opts.get('file',None)!=stdout: _flush() try: print(fs.format(*args),**opts) _flush(opts['file']) except BrokenPipeError: pass return # do printing just like print() then flush the file it used def pr(*args,**opts): opts['file'] = opts.get('file',stderr) if opts.get('file',None)!=stdout: _flush() try: print(*args,**opts) opts['file'].flush() except BrokenPipeError: pass return # print variables, given their names, or report if undefined def pv(*args,**opts): from inspect import currentframe opts['file'],c = opts.get('file',stderr),opts.pop('prefix','') try: if not args: # if nothing to do print(**opts) _flush(opts['file']) return 0 f = currentframe() if 'f_back' not in dir(f): print(**opts) _flush(opts['file']) return 0 f = f.f_back l,g = f.f_locals,f.f_globals if 'f_lineno' in dir(f): p='line '+repr(f.f_lineno)+': ' else: p='unknown line: ' _flush() # synchronize outputs for name in args: _flush(opts['file']) s = name.split('.') n = s.pop(0) if n[:1] in '!': # use what follows as a var name n = n[1:] # should this still do all the checks below? elif n[:1] in '+=:': # use what follows as a prefix c = n[1:] continue elif n[:1] is '#': # use what follows as a comment print(p+c+'............. ', n[1:], **opts) continue elif not n: # null variable name print(p+c+'............. ', s[0], **opts) _flush(opts['file']) continue elif n in l: # var is local o,v = p+c+'... local var:',l[n] elif n in g: # var is global o,v = p+c+'.. global var:',g[n] else: # not local or global print(p+c+'............. ', str(name), 'not assigned in local or global namespace', **opts) _flush(opts['file']) continue while s: m = s.pop(0) if m in dir(v): v,n = getattr(v,m),n+'.'+m else: break if isinstance(v,str) and len(v)<11: x=['['+hex(ord(x)+256)[-2:]+']' for x in v] print(o,repr(n),'=',repr(v),' '.join(x),**opts) else: print(o,repr(n),'=',repr(v),**opts) _flush(opts['file']) continue except BrokenPipeError: pass return # sleep a specified time after flushing standard files def sl(sec,**opts): from time import sleep opts.get('file',stdout).flush() _flush() sleep(float(sec)) return else: def pf(*a,**o): return # define the "do nothing" version of pf() def pr(*a,**o): return # define the "do nothing" version of pr() def pv(*a,**o): return # define the "do nothing" version of pv() def sl(*a,**o): return # define the "do nothing" version of sl()