Nick Coghlan wrote: > On Fri, Mar 9, 2012 at 6:19 PM, Mark Shannon <mark at hotpy.org> wrote: >> The Python API would be changed, but in a backwards compatible way. >> exec, eval and __import__ would all gain an optional (keyword-only?) >> "builtins" parameter. > > No, some APIs effectively define *protocols*. For such APIs, *adding* > parameters is almost of comparable import to taking them away, because > they require that other APIs modelled on the prototype also change. In > this case, not only exec() has to change, but eval, __import__, > probably runpy, function creation, eventually any third party APIs for > code execution, etc, etc. > > Adding a new parameter to exec is a change with serious implications, > and utterly unnecessary, since the API part is already covered by > setting __builtins__ in the passed in globals namespace (which is > appropriately awkward to advise people that they're doing something > strange with potentially unintended consequences or surprising > limitations). It is the implementation that interests me. Implementing the (locals, globals, builtins) triple as a single object has advantages both in terms of internal consistency and efficiency. I just thought to expose this to the user. I am now persuaded that I don't want to expose anything :) > > That said, binding a reference to the builtin *early* (for example, at > function definition time or when a new invocation of the eval loop > first fires up) may be a reasonable idea, but you don't have to change > the user facing API to explore that option - it works just as well > with "__builtins__" as an optional value in the existing global > namespace. OK. So, how about this: (builtins refers to the dict used for variable lookup, not the module) New eval pseudocode eval(code, globals, locals): triple = (locals, globals, globals["__builtins__"]) return eval_internal(triple) Similarly for exec, __import__ and runpy. That way the (IMO clumsy) builtins = globals["__builtins__"] only happens at a few known locations. It should then be clear where all code gets its namespaces from. Namespaces should be inherited as follows: frame: function scope: globals and builtins from function, locals from parameters. module scope: globals and builtins from module, locals == globals. in eval, exec, or runpy: all explicit. function: globals and builtins from module (no locals) module: globals and builtins from import (no locals) import: explicitly from __import__() or implicitly from current frame in an import statement. For frame and function, free and cell (nonlocal) variables would be unchanged. On entry the namespaces will be {}, {}, sys.modules['builtins'].__dict__ This is pretty much what happens anyway, except that where code gets its builtins from is now well defined. Cheers, Mark.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4