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

Mark Shannon mark at hotpy.org
Fri Mar 9 09:19:24 CET 2012


Guido van Rossum wrote:
> On Thu, Mar 8, 2012 at 4:33 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:
>> 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.
> 
> Agreed on the gratuitous API changes. I'd like to hear Mark's response.
> 
C API or Python API?

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.

I see no reason to change any of the C API functions.
New functions taking an extra parameter could be added,
but it wouldn't be a requirement.

Cheers,
Mark






More information about the Python-Dev mailing list