Is a string formatter that pulls variables from its calling scope bad practice?

Update: Python 3.6 has this feature (a more powerful variant) builtin:

x, y, z = range(3)
print(f"{x} {y + z}")
# -> 0 3

See PEP 0498 — Literal String Interpolation


It[manual solution] leads to somewhat surprising behaviour with nested functions:

from callerscope import format

def outer():
    def inner():
        nonlocal a
        try:
            print(format("{a} {b}"))
        except KeyError as e:
            assert e.args[0] == 'b'
        else:
            assert 0

    def inner_read_b():
        nonlocal a
        print(b) # read `b` from outer()
        try:
            print(format("{a} {b}"))
        except KeyError as e:
            assert 0
    a, b = "ab"
    inner()
    inner_read_b()

Note: the same call succeeds or fails depending on whether a variable is mentioned somewhere above or below it.

Where callerscope is:

import inspect
from collections import ChainMap
from string import Formatter

def format(format_string, *args, _format=Formatter().vformat, **kwargs):
    caller_locals = inspect.currentframe().f_back.f_locals
    return _format(format_string, args, ChainMap(kwargs, caller_locals))

Leave a Comment