[Python-ideas] Really support custom types for global namespace
Eric Snow
ericsnowcurrently at gmail.com
Thu Jun 19 21:26:00 CEST 2014
On Wed, Jun 18, 2014 at 5:25 AM, Robert Lehmann <mail at robertlehmann.de> wrote:
> The interpreter currently supports setting a custom type for globals() and
> overriding __getitem__. The same is not true for __setitem__:
>
> class Namespace(dict):
> def __getitem__(self, key):
> print("getitem", key)
> def __setitem__(self, key, value):
> print("setitem", key, value)
>
> def fun():
> global x, y
> x # should call globals.__getitem__
> y = 1 # should call globals.__setitem__
>
> dis.dis(fun)
> # 3 0 LOAD_GLOBAL 0 (x)
> # 3 POP_TOP
> #
> # 4 4 LOAD_CONST 1 (1)
> # 7 STORE_GLOBAL 1 (y)
> # 10 LOAD_CONST 0 (None)
> # 13 RETURN_VALUE
>
> exec(fun.__code__, Namespace())
> # => getitem x
> # no setitem :-(
>
> I think it is weird why reading global variables goes through the usual
> magic methods just fine, while writing does not. The behaviour seems to
> have been introduced in Python 3.3.x (commit e3ab8aa) to support custom
> __builtins__. The documentation is fuzzy on this issue:
>
>> If only globals is provided, it must be a dictionary, which will be used
>> for both the global and the local variables. If globals and locals are
>> given, they are used for the global and local variables, respectively. If
>> provided, locals can be any mapping object.
"it must be a dictionary" implies to me the exclusion of subclasses.
Keep in mind that subclassing core builtin types (like dict) is
generally not a great idea and overriding methods there is definitely
a bad idea. A big part of this is due to an implementation detail of
CPython: the use of the concrete C API, especially for dict. The
concrete API is useful for performance, but it isn't subclass-friendly
(re: overridden methods) in the least.
> People at python-list were at odds if this was a bug,
> unspecified/unsupported behaviour, or a deliberate design decision.
I'd lean toward unspecified behavior, though (again) the docs imply to
me that using anything other than dict isn't guaranteed to work right.
So I'd consider this a proposal to add a slow path to STORE_GLOBAL
that supports dict subclasses with overridden __setitem__() and to
explicitly indicate support for get/set in the docs for exec().
To be honest, I'm not sold on the idea. There are subtleties involved
here that make messing around with exec a high risk endeavor,
requiring sufficient justification. What's the use case here?
Also, is this exec-specific? Consider the case of class definitions
and that the namespace in which they are executed can be customized
via __prepare_class__() on the metaclass. I could be wrong, but I'm
pretty sure you don't run into the problem there. So there may be
more to the story here.
> If it
> is just unsupported, I don't think the asymmetry makes it any better. If it
> is deliberate, I don't understand why dispatching on the dictness of globals
> (PyDict_CheckExact(f_globals)) is good enough for LOAD_GLOBAL, but not for
> STORE_GLOBAL in terms of performance.
>
> I have a patch (+ tests) to the current default branch straightening out
> this asymmetry and will happily open a ticket if you think this is indeed a
> bug.
Definitely open a ticket (and reply here with a link).
-eric
More information about the Python-ideas
mailing list