Home > Articles

This chapter is from the book

This chapter is from the book

5.21 Environment Inspection

Functions can inspect their execution environment using the built-in functions globals() and locals(). globals() returns the dictionary that’s serving as the global namespace. This is the same as the func.__globals__ attribute. This is usually the same dictionary that’s holding the contents of the enclosing module. locals() returns a dictionary containing the values of all local and closure variables. This dictionary is not the actual data structure used to hold these variables. Local variables can come from outer functions (via a closure) or be defined internally. locals() collects all of these variables and puts them into a dictionary for you. Changing an item in the locals() dictionary has no effect on the underlying variable. For example:

def func():
    y = 20
    locs = locals()
    locs['y'] = 30       # Try to change y
    print(locs['y'])     # Prints 30
    print(y)             # Prints 20

If you wanted a change to take effect, you’d have to copy it back into the local variable using normal assignment.

def func():
    y = 20
    locs = locals()
    locs['y'] = 30
    y = locs['y']

A function can obtain its own stack frame using inspect.currentframe(). A function can obtain the stack frame of its caller by following the stack trace through f.f_back attributes on the frame. Here is an example:

import inspect

def spam(x, y):
    z = x + y
    grok(z)

def grok(a):
    b = a * 10

    # outputs: {'a':5, 'b':50 }
    print(inspect.currentframe().f_locals)

    # outputs: {'x':2, 'y':3, 'z':5 }
    print(inspect.currentframe().f_back.f_locals)

spam(2, 3)

Sometimes you will see stack frames obtained using the sys._getframe() function instead. For example:

import sys
def grok(a):
    b = a * 10
    print(sys._getframe(0).f_locals)    # myself
    print(sys._getframe(1).f_locals)    # my caller

The attributes in Table 5.2 can be useful for inspecting frames.

Table 5.2 Frame Attributes

Attribute

Description

f.f_back

Previous stack frame (toward the caller)

f.f_code

Code object being executed

f.f_locals

Dictionary of local variables (locals())

f.f_globals

Dictionary used for global variables (globals())

f.f_builtins

Dictionary used for built-in names

f.f_lineno

Line number

f.f_lasti

Current instruction. This is an index into the bytecode string of f_code.

f.f_trace

Function called at start of each source code line

Looking at stack frames is useful for debugging and code inspection. For example, here’s an interesting debug function that lets you view the values of the selected variables of the caller:

import inspect
from collections import ChainMap

def debug(*varnames):
    f = inspect.currentframe().f_back
    vars = ChainMap(f.f_locals, f.f_globals)
    print(f'{f.f_code.co_filename}:{f.f_lineno}')
    for name in varnames:
        print(f'    {name} = {vars[name]!r}')

# Example use
def func(x, y):
    z = x + y
    debug('x','y')  # Shows x and y along with file/line
    return z

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.