1

I'm trying to replace my Template(s).substitute("$a,$b", locals()) with something short like

sub("$a,$b")

However, I don't have access to locals of surrounding scope inside sub(), any idea how to get them? One possible workaround I found is to throw an exception, catch it, and step along the frames to find the previous frame, but perhaps there's an easier way?

import traceback, sys, code
try:
  2/0
except Exception as e:
  type, value, tb = sys.exc_info()
  traceback.print_exc()
  last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
  frame = last_frame().tb_frame
  ns = dict(frame.f_globals)
Yaroslav Bulatov
  • 57,332
  • 22
  • 139
  • 197

2 Answers2

2

Try using sys._current_frames() instead of raising exception.

Possible alternatives: sys._getframe(), inspect.currentframe(), inspect.stack()

I cant think of better solution, than analysing frames.

Filip Malczak
  • 3,124
  • 24
  • 44
  • current_frames() gives me dictionary with 1 frame -- the current one. But I need locals from the previous frame – Yaroslav Bulatov Mar 13 '14 at 01:17
  • Yes, _current_frames maps thread to frames, rest of them (functions from post) return frame of specified depth in stack. If you have single frame, you can follow it's f_back attribute to obtain calling frame for the one that you hold. I linked docs for those functions, so you can read it and figure out how to navigate in stack. – Filip Malczak Mar 13 '14 at 07:24
1

You can access it directly viasys._getframe(), although it's only guaranteed to work with CPython.

from string import Template
import sys

def sub(template):
    namespace = sys._getframe(1).f_locals  # caller's locals
    return Template(template).substitute(namespace)

a, b = 1, 42
print sub("$a,$b")  # -> 1,42
martineau
  • 119,623
  • 25
  • 170
  • 301