[Python-Dev] getting at the current frame
Barry A. Warsaw
barry@wooz.org
Tue, 24 Oct 2000 16:11:54 -0400 (EDT)
I've been playing around with ideas for internationalizing Mailman,
which naturally <wink> leads to string interpolation. To see why,
think about making the following code translatable:
def trade(yours, mine):
print 'if you give me %s, i will give you %s' % (yours, mine)
Because the order of the interpolated values may change in the
translated string, you really have to do something like:
def trade(yours, mine):
print 'if you give me %(yours)s, i will give you %(mine)s' % {
'yours': yours,
'mine' : mine,
}
which actually will look something like this in real code:
def trade(yours, mine):
print _('if you give me %(yours)s, i will give you %(mine)s') % {
'yours': yours,
'mine' : mine,
}
The string wrapped in _() is what gets translated here.
Okay, we all know that's a pain, right? Lots of people have proposed
solutions. I've looked briefly at !?ng's Itpl.py, but I think it
probably does too much by adding evaluation.
I can define _() to make the problem somewhat more convenient:
def _(s):
try:
raise Exception
except Exception:
frame = sys.exc_info()[2].tb_frame.f_back
d = frame.f_globals.copy()
d.update(frame.f_locals())
return the_translation_of(s) % d
Now I can write the code like this:
def trade(yours, mine):
print _('if you give me %(yours)s, i will give you %(mine)s')
All well and good and doable in Python today, except getting the
current frame with the exception raising trick is slooow. A simple
proposed addition to the sys module can improve the performance by
about 8x:
def _(s):
frame = sys.getcaller(1)
d = frame.f_globals.copy()
d.update(frame.f_locals())
return the_translation_of(s) % d
The implementation of sys.getcaller() is given in the below patch.
Comments? I think this particular addition is too small for a PEP,
although ?!ng still owns PEP 215 (which needs filling in).
-Barry
-------------------- snip snip --------------------
Index: sysmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/sysmodule.c,v
retrieving revision 2.78
diff -u -r2.78 sysmodule.c
--- sysmodule.c 2000/09/01 23:29:28 2.78
+++ sysmodule.c 2000/10/24 17:50:30
@@ -15,6 +15,8 @@
*/
#include "Python.h"
+#include "compile.h"
+#include "frameobject.h"
#include "osdefs.h"
@@ -284,6 +286,38 @@
}
#endif
+static char getcaller_doc[] =
+"getcaller([depth]) -> frameobject\n\
+\n\
+By default, return the frame object at the top of the call stack. If an\n\
+integer depth is given, return the frame object that many calls below the\n\
+top of the stack. If that is deeper than the call stack, a ValueError is\n\
+raised.";
+
+
+static PyObject *
+sys_getcaller(PyObject *self, PyObject *args)
+{
+ PyFrameObject *f = PyThreadState_Get()->frame;
+ int depth = -1;
+
+ if (!PyArg_ParseTuple(args, "|i:getcaller", &depth))
+ return NULL;
+
+ while (depth > 0 && f != NULL) {
+ f = f->f_back;
+ --depth;
+ }
+ if (f == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "call stack is not deep enough");
+ return NULL;
+ }
+ Py_INCREF(f);
+ return (PyObject*)f;
+}
+
+
#ifdef Py_TRACE_REFS
/* Defined in objects.c because it uses static globals if that file */
extern PyObject *_Py_GetObjects(PyObject *, PyObject *);
@@ -313,6 +347,7 @@
{"getrefcount", sys_getrefcount, 1, getrefcount_doc},
{"getrecursionlimit", sys_getrecursionlimit, 1,
getrecursionlimit_doc},
+ {"getcaller", sys_getcaller, 1, getcaller_doc},
#ifdef USE_MALLOPT
{"mdebug", sys_mdebug, 1},
#endif