
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

"Barry A. Warsaw" wrote:
+1. I have a similar function in mxTools. I would use a different name though... something like "sys.getframe()". -- Marc-Andre Lemburg ______________________________________________________________________ Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/

On Tue, 24 Oct 2000, Barry A. Warsaw wrote:
Do you think a variant of Itpl that only looked up variable names would solve your problem?
How about this routine to get the current frame: inspect.currentframe(). Could i humbly re-request approval for inspect.py in the standard library? http://lfw.org/python/inspect.py It's been up there for a long time, there's lots of good stuff in it, and previous feedback on this list has all been fairly positive -- there just hasn't been an explicit approval for inclusion. This is probably because we were busy trying to get 2.0 released (even though inspect.py was proposed here before ascii.py, and ascii.py did get accepted). I think the functionality it provides is important to supporting the language definition. It abstracts away the special secret attributes and protects us from future changes in the internals. If there are changes that need to be made before it can be accepted, i'd be happy to make them. -- ?!ng

On Thu, 26 Oct 2000, Fredrik Lundh wrote:
It's been up there for a long time
hey, you still haven't fixed the *2 bug...
Sorry -- looks like an older version than the one i have on my local disk, which did get fixed. The one on the website is now up to date. http://lfw.org/python/inspect.py -- ?!ng

Could i humbly re-request approval for inspect.py in the standard library?
To avoid that we forget about this again, and to make sure it gets proper treatment, could you submit it through the patch manager? No need to turn it into a context diff, just upload the entire file. --Guido van Rossum (home page: http://www.python.org/~guido/)

Ping> Could i humbly re-request approval for inspect.py in the standard Ping> library? Ping> http://lfw.org/python/inspect.py +1. I've written functions over the years to do some small bits of what inspect.py does. I'd much rather have those things in a standard place with more eyeballs checking them for correctness. Skip

"SM" == Skip Montanaro <skip@mojam.com> writes:
Ping> Could i humbly re-request approval for inspect.py in the Ping> standard library? Ping> http://lfw.org/python/inspect.py SM> +1. I've written functions over the years to do some small SM> bits of what inspect.py does. I'd much rather have those SM> things in a standard place with more eyeballs checking them SM> for correctness. I've downloaded it, and will take a look.

"KY" == Ka-Ping Yee <ping@lfw.org> writes:
>> 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. KY> Do you think a variant of Itpl that only looked up variable KY> names would solve your problem? I think it would calm my objections, yes. I'll observe that I think there may be a middle ground between my approach and Itpl. I've found a couple of situations where I want a minimal form of evaluation, e.g. I want attributes to be expanded (and methods called), but not a general evaluation facility. So for example, I'd like to be able to say: print _("The name of the mailing list is %(mlist.listname())s") What worries me about a general evaluation framework is that we then have to be really really careful about security and origin of the strings we're using here. Maybe we'd need to adopt something like Perl's taint strings. KY> How about this routine to get the current frame: KY> inspect.currentframe(). It uses the same intentional-exception trick to get at the current frame. It's about 8x faster to do it in C. I'll note that your version raises and catches a string exception, while mine uses an exception instance. I don't see much difference in speed between the two. KY> If there are changes that need to be made before it can be KY> accepted, i'd be happy to make them. Sent under a separate private response... -Barry

On 24 October 2000, Barry A. Warsaw said:
+1. Not sure if I would call that function 'getcaller()' or 'getframe()'; could go either way. In my implementation of this (I guess everyone has one!), I also have a couple of convenience functions: def get_caller_env (level=1): """get_caller_env(level : int = 1) -> (globals : dict, locals : dict) Return the environment of a caller: its dictionaries of global and local variables. 'level' has same meaning as for 'caller()'. """ def get_frame_info (frame): """get_frame_info(frame) -> (filename : string, lineno : int, function_name : string, code_line : string) Extracts and returns a tuple of handy information from an execution frame. """ def get_caller_info (level=1): """get_caller_info(level : int = 1) -> (filename : string, lineno : int, function_name : string, code_line : string) Extracts and returns a tuple of handy information from some caller's execution frame. 'level' is the same as for 'caller()', i.e. if level is 1 (the default), gets this information about the caller of the function that is calling 'get_caller_info()'. """ These are mainly used by my unit-testing framework (which you'll no doubt remember, Barry) to report where test failures originate. Very handy! Looks like Ping's inspect.py has something like my 'get_frame_info()', only it's called 'getframe()'. Obviously this is the wrong name if Barry's new function gets added as 'sys.getframe()'. How 'bout this: sys.getframe(n) - return stack frame for depth 'n' inspect.getframeinfo(frame) - return (filename, line number, function name, lines-of-code) Although I haven't grokked Ping's module enough to know if that rename really fits his scheme. Greg -- Greg Ward - software developer gward@mems-exchange.org MEMS Exchange / CNRI voice: +1-703-262-5376 Reston, Virginia, USA fax: +1-703-262-5367

"Barry A. Warsaw" wrote:
+1. I have a similar function in mxTools. I would use a different name though... something like "sys.getframe()". -- Marc-Andre Lemburg ______________________________________________________________________ Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/

On Tue, 24 Oct 2000, Barry A. Warsaw wrote:
Do you think a variant of Itpl that only looked up variable names would solve your problem?
How about this routine to get the current frame: inspect.currentframe(). Could i humbly re-request approval for inspect.py in the standard library? http://lfw.org/python/inspect.py It's been up there for a long time, there's lots of good stuff in it, and previous feedback on this list has all been fairly positive -- there just hasn't been an explicit approval for inclusion. This is probably because we were busy trying to get 2.0 released (even though inspect.py was proposed here before ascii.py, and ascii.py did get accepted). I think the functionality it provides is important to supporting the language definition. It abstracts away the special secret attributes and protects us from future changes in the internals. If there are changes that need to be made before it can be accepted, i'd be happy to make them. -- ?!ng

On Thu, 26 Oct 2000, Fredrik Lundh wrote:
It's been up there for a long time
hey, you still haven't fixed the *2 bug...
Sorry -- looks like an older version than the one i have on my local disk, which did get fixed. The one on the website is now up to date. http://lfw.org/python/inspect.py -- ?!ng

Could i humbly re-request approval for inspect.py in the standard library?
To avoid that we forget about this again, and to make sure it gets proper treatment, could you submit it through the patch manager? No need to turn it into a context diff, just upload the entire file. --Guido van Rossum (home page: http://www.python.org/~guido/)

Ping> Could i humbly re-request approval for inspect.py in the standard Ping> library? Ping> http://lfw.org/python/inspect.py +1. I've written functions over the years to do some small bits of what inspect.py does. I'd much rather have those things in a standard place with more eyeballs checking them for correctness. Skip

"SM" == Skip Montanaro <skip@mojam.com> writes:
Ping> Could i humbly re-request approval for inspect.py in the Ping> standard library? Ping> http://lfw.org/python/inspect.py SM> +1. I've written functions over the years to do some small SM> bits of what inspect.py does. I'd much rather have those SM> things in a standard place with more eyeballs checking them SM> for correctness. I've downloaded it, and will take a look.

"KY" == Ka-Ping Yee <ping@lfw.org> writes:
>> 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. KY> Do you think a variant of Itpl that only looked up variable KY> names would solve your problem? I think it would calm my objections, yes. I'll observe that I think there may be a middle ground between my approach and Itpl. I've found a couple of situations where I want a minimal form of evaluation, e.g. I want attributes to be expanded (and methods called), but not a general evaluation facility. So for example, I'd like to be able to say: print _("The name of the mailing list is %(mlist.listname())s") What worries me about a general evaluation framework is that we then have to be really really careful about security and origin of the strings we're using here. Maybe we'd need to adopt something like Perl's taint strings. KY> How about this routine to get the current frame: KY> inspect.currentframe(). It uses the same intentional-exception trick to get at the current frame. It's about 8x faster to do it in C. I'll note that your version raises and catches a string exception, while mine uses an exception instance. I don't see much difference in speed between the two. KY> If there are changes that need to be made before it can be KY> accepted, i'd be happy to make them. Sent under a separate private response... -Barry

On 24 October 2000, Barry A. Warsaw said:
+1. Not sure if I would call that function 'getcaller()' or 'getframe()'; could go either way. In my implementation of this (I guess everyone has one!), I also have a couple of convenience functions: def get_caller_env (level=1): """get_caller_env(level : int = 1) -> (globals : dict, locals : dict) Return the environment of a caller: its dictionaries of global and local variables. 'level' has same meaning as for 'caller()'. """ def get_frame_info (frame): """get_frame_info(frame) -> (filename : string, lineno : int, function_name : string, code_line : string) Extracts and returns a tuple of handy information from an execution frame. """ def get_caller_info (level=1): """get_caller_info(level : int = 1) -> (filename : string, lineno : int, function_name : string, code_line : string) Extracts and returns a tuple of handy information from some caller's execution frame. 'level' is the same as for 'caller()', i.e. if level is 1 (the default), gets this information about the caller of the function that is calling 'get_caller_info()'. """ These are mainly used by my unit-testing framework (which you'll no doubt remember, Barry) to report where test failures originate. Very handy! Looks like Ping's inspect.py has something like my 'get_frame_info()', only it's called 'getframe()'. Obviously this is the wrong name if Barry's new function gets added as 'sys.getframe()'. How 'bout this: sys.getframe(n) - return stack frame for depth 'n' inspect.getframeinfo(frame) - return (filename, line number, function name, lines-of-code) Although I haven't grokked Ping's module enough to know if that rename really fits his scheme. Greg -- Greg Ward - software developer gward@mems-exchange.org MEMS Exchange / CNRI voice: +1-703-262-5376 Reston, Virginia, USA fax: +1-703-262-5367
participants (7)
-
barry@wooz.org
-
Fredrik Lundh
-
Greg Ward
-
Guido van Rossum
-
Ka-Ping Yee
-
M.-A. Lemburg
-
Skip Montanaro