Skip to main content
2 of 6
deleted 6 characters in body
holroy
  • 11.8k
  • 1
  • 27
  • 59

Looking up alias using inspect in Python

I'm on a quest for making a good debug_print() method for printing out debug information in some of my private projects. When testing I aliased my debug_print() imported from a module to debug() print, and some of the intrinsic of my code stopped working.

This led to a question on Stack Overflow, which with a little nudge in the right direction from SuperBiasedMan, led to the following code which I would like for you to review. First the code from utilities.py:

import inspect
import re

DEBUG_FLAG = True

def _log_args(*args):
    """Uses reflection to returning passing argument code with values."""
    
    prev_frame = inspect.currentframe().f_back
    func_name = prev_frame.f_code.co_name
    code_context = inspect.getframeinfo(prev_frame.f_back).code_context[0].strip()

    # Do some magic, which does work _unless_ func_name is aliased :-)
    print('code context     : {}'.format(code_context))
    print('func_name        : {}'.format(func_name))
    
    ## Main focus area for review starts here

    globals_copy = globals()

    # Extract legal tokens from code_context
    tokens = re.compile('[_a-zA-Z][a-zA-Z_0-9]*').findall(code_context)

    for token in tokens:
#        print( '  Checking token : {}'.format(token))

        # Check if token is found as an object in globals()        
        code_object = globals_copy.get(token, None)
        if not code_object:
            continue
        
        # Check if code_object is one of my userdefined functions
        if inspect.isfunction(code_object):
            code_func_name = getattr(code_object, '__name__', None)
        else:
            continue
        
        # Check if expanded token is actually an alias (or equal) to func_name
        if code_func_name == func_name:
            func_name = token
            break
    else:
        # For-loop went through all tokens, and didn't find anything
        func_name = None
        
    if func_name:
        print('Calling function : {}'.format(func_name))
    else:
        print('Didn\'t find a calling function?!')
     
    ## Main focus area for review ends here
    return ', '.join(str(arg) for arg in args)


def format_args(*args):
    """Returns string with name of arguments with values."""
    return _log_args(args)

def debug_print(*args):
    """Prints name of arguments with values."""
    if DEBUG_FLAG:
        print _log_args(args)

And then some test code from another file:

from utilities import debug_print, format_args, debug_print as debug, format_args as fargs

def main():
    
    a, b = "text", (12, 13)

    print "== Unaliased =="
    test_text = format_args(a, b)
    print test_text   # Returns 
    debug_print(a, b)
    
    print "\n== Aliased =="
    test_text = fargs(a, b)
    print test_text   # Returns 
    debug(a, b)
    
    
if __name__ == '__main__':
    main()
    

The current output reflects that this is a work in progress, and the main focus for this review is commented upon in the code.

== Unaliased ==
code context     : test_text = format_args(a, b)
func_name        : format_args
Calling function : format_args
('text', (12, 13))
code context     : debug_print(a, b)
func_name        : debug_print
Calling function : debug_print
('text', (12, 13))

== Aliased ==
code context     : test_text = fargs(a, b)
func_name        : format_args
Calling function : fargs
('text', (12, 13))
code context     : debug(a, b)
func_name        : debug_print
Calling function : debug
('text', (12, 13)
holroy
  • 11.8k
  • 1
  • 27
  • 59