Package: debugprint
debugprintA light zero-dependency module for nicely printing things for debuggingdebugprintis a slightly more featureful Python clone of the node.jsdebugmodule. It behaves almost exactly as node.js debug does, with the following exceptions:Python imports behave differently from node.jsrequiresodebuginstances are created differentlyYou can define custom format functions in debugprintYou can add captions to print-outs in debugprintIn node.js debug, you can change the enabled namespace within a module by doingdebug.disable()ordebug.enable('some:namespaces:*')Otherwise, the usage is pretty much exactly the same - make a newDebuginstance for each module, and run the program with theDEBUGenvironment variable set to something sensible (details on settingDEBUGare below).Why usedebugprintinstead of just regularprint?Prints only when theDEBUGenvironment variable is set to a matching value (more on this below) so you don't need to spend time finding and removing print statements prior to releasing for productionPrints tostderrnotstdout- much better for command line tools as it doesn't interfere with piping the data on to other programsProvides the name of the module the statement is being output from - module name is also colour-codedAllows passing an optional captionAutomatically pretty-prints built-in collection typesAllows adding custom format functions to pretty-print custom data types likelxmltrees orpyrsistentimmutable data structures.Example print-outsScalar valuesdebug("Hello, world!")anddebug("Hello, world!", "a caption"):Dictsdebug(example_dict)debug(example_dict, "A pretty-printed nested dict")XMLdebug(example_xml)debugprintcan also pretty-print nested lists, tuples, and mixed JSON by default - essentially anything that can be handled by the standard librarypprintmodule.Installation# via pippipinstalldebugprint# via pipenvpipenvinstalldebugprintUsageImport into every module where you want to do debug printing, then create an instance of theDebugclass for that module.fromdebugprintimportDebug# module names can contain: letters, numbers, underscores, hyphens, and colons# the actual regex is this: /^[A-Za-z0-9:_\-]+$/debug=Debug("my_application:some_subpackage:this_module")# simple printingdebug("some string value")debug(123)debug(False)# pretty printing collectionsdebug([1,2,3])debug({"a":1,"b":2,"c":3})# printing things with captionsdebug(call_this_function_that_returns_a_bool(),"bool returned by this function")debug(some_var,"the value of some_var at this point in the pipeline")Setting theDEBUGenvironment variableBy default,debugprintdoesn't print anything. This is intentional - debug printouts are unnecessary in production and can be actively irritating to users - not to mention, depending on the situation, a potential security risk.In order to getdebugprintto print, you need to run your module/script/application with theDEBUGenvironment variable set.There are two ways to do this in bash:# setting the DEBUG environment variable to "*" as an example$DEBUG="*"python3myscript.py# or$exportDEBUG="*"$python3myscript.pySettingDEBUGto*tellsdebugprintto always print. Occasionally that's the behaviour you want, but more likely you'll want to restrict it to a limited subset of possibledebug()calls. You can therefore set it to colon-separated paths, like the following examples:app-debug()calls will only print in this scenario:debug = Debug("app")app:thingwill only print fordebug = Debug(app:thing)app:*- will print ifdebug = Debug("app")ordebug = Debug(app:thing)ordebug = Debug(app:thing:anotherthing)etc.app:*:anotherthingwill print ifdebug = Debug("app:thing:anotherthing")ordebug = Debug("app:somethingelse:anotherthing")etc.You can also setDEBUGso multiple different paths are enabled. For instance:app,app:thing-debug()calls will print wheredebug = Debug("app")ordebug = Debug("app:thing")app:thing,app:anotherthing- will print wheredebug = Debug("app:thing")ordebug = Debug("app:anotherthing")This should hopefully be fairly intuitive. Just set the path of theDEBUGenvironment variable to match what you want to print. You'll usually be fine with setting it toDEBUG=nameofmyapp,nameofmyapp:*and leaving it, but if you're working on a big codebase or trying to figure out a particularly persistent bug, you may want to adjust it to narrow down what gets printed.Custom format functionsBy default,debugprintwill attempt to pretty-printlists,dicts,sets,tuples, andxml.etree.ElementTree.ElementTrees. But what if you're using non-standard data structures, likelxmltrees orpyrsistentimmutable data structures?You can define custom format functions for any data type you like. The general structure of a custom format function is that it returns either a string to be printed orNone.debugprintwill call each defined custom format function in turn. The logic indebugprintis similar to this:# not the actual code but not far offinput_value# value to printforfunctionincustom_functions:output_string=function(input_value)ifoutput_string:breakifoutput_string:# print this stringelse:# carry on and try using the default formattersExample custom format functionsThe best way to see what a custom format function looks like is to check out a couple of examples:frompprintimportpformatfromlxmlimportetreefrompyrsistentimportPRecord,PMap,PVector,PSet,thawimportdebugprint# a custom format function for LXML element treesdeflxml_formatter(possible_lxml_object):ifisinstance(possible_lxml_object,etree._Element):returnetree.tostring(possible_lxml_object,pretty_print=True,encoding="unicode",)else:returnNone# a custom format function for pyrsistent immutable data structuresdefpyrsistent_formatter(possible_pyrsistent_data_structure):ifisinstance(possible_pyrsistent_data_structure,(PRecord,PMap,PVector,PSet)):returnpformat(thaw(possible_pyrsistent_data_structure))else:returnNonedebugprint.use_format(lxml_formatter)debugprint.use_format(pyrsistent_formatter)The basic logic of a custom format function is just:defcustom_format_function(some_input):ifisinstance(some_input,the_type_this_function_is_looking_for):returnformat_this_special_type_for_printing(some_input)else:# you can return any falsy value herereturnNone