[Python-Dev] Adding a builtins parameter to eval(), exec() and __import__().

Nick Coghlan ncoghlan at gmail.com
Fri Mar 9 01:33:00 CET 2012


On Fri, Mar 9, 2012 at 3:31 AM, Guido van Rossum <guido at python.org> wrote:
> But the __builtins__ that are actually used by any particular piece of
> code is *not* taken by importing builtins. It is taken from what the
> globals store under the key __builtins__.
>
> This is a feature that was added specifically for sandboxing purposes,
> but I believe it has found other uses too.

Agreed, but swapping out builtins for a different namespace is still
the exception rather than the rule. My Impression of Mark's proposal
was that this approach would become the *preferred* way of doing
things, and that's the part I don't like at a conceptual level.

>>> The key point is that every piece of code already inherits locals, globals
>>> and builtins from somewhere else.
>>> We can already control locals (by which parameters are passed in) and
>>> globals via exec, eval, __import__, and runpy (any others?)
>>> but we can't control builtins.
>>
>> Correct - because controlling builtins is the domain of sandboxes.
>
> Incorrect (unless I misunderstand the context) -- when you control the
> globals you control the __builtins__ set there.

And this is where I don't like the idea at a practical level. We
already have a way to swap in a different set of builtins for a
certain execution context (i.e. set "__builtins__" in the global
namespace) for a small chunk of code, as well as allowing
collections.ChainMap to insert additional namespaces into the name
lookup path.

This proposal suggests adding an additional mapping argument to every
API that currently accepts a locals and/or globals mapping, thus
achieving... well, nothing substantial, as far as I can tell (aside
from a lot of pointless churn in a bunch of APIs, not all of which are
under our direct control).

> In any case, the locals / globals / builtins chain is a
> simplification; there are also any number of intermediate scopes
> (between locals and globals) from which "nonlocal" variables may be
> used. Like optimized function globals, these don't use a dict lookup
> at all, they are determined by compile-time analysis.

Acknowledged, but code executed via the exec API with both locals and
globals passed in is actually one of the few places where that lookup
chain survives in its original form (module level class definitions
being the other).

Now, rereading Mark's original message, a simpler proposal of having
*function objects* do an early lookup of
"self.__globals__['__builtins__']" at creation time and caching that
somewhere such that the frame objects can get hold of it (rather than
having to do the lookup every time the function gets called or a
builtin gets referenced) might be a nice micro-optimisation. It's the
gratuitous API changes that I'm objecting to, not the underlying idea
of binding the reference to the builtins namespace earlier in the
function definition process. I'd even be OK with leaving the default
builtins reference *out* of the globals namespace in favour of storing
a hidden reference on the frame objects.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list