Title: exec(): method's default arguments from dict-inherited globals
Type: behavior Stage: resolved
Components: Documentation, Interpreter Core Versions: Python 3.7, Python 3.6
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Ilya Polyakovskiy, anthonypjshaw, docs@python, r.david.murray, rhettinger
Priority: normal Keywords: patch

Created on 2017-11-07 12:49 by Ilya Polyakovskiy, last changed 2019-06-01 15:53 by rhettinger. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 4313 closed Ilya Polyakovskiy, 2017-11-07 13:15
PR 4315 closed Ilya Polyakovskiy, 2017-11-07 13:30
PR 13140 merged anthonypjshaw, 2019-05-06 22:08
Messages (4)
msg305746 - (view) Author: Ilya Polyakovskiy (Ilya Polyakovskiy) * Date: 2017-11-07 12:49
I'm using exec() to run code with globals object inherited from dict. The problem is overloaded __getitem__ doesn't called to load default argument for class methods.
Here the example. Let's assume we create some variable storage for code execution

class Env(dict):
    def __init__(self, external_storage):
        self._external_storage = external_storage

    def __setitem__(self, key, value):
        print('__setitem__: {}'.format(key))
        self._external_storage[key] = value

    def __getitem__(self, key):
        print('__getitem__: {}'.format(key))
        return self._external_storage[key]

storage = {}
env = Env(storage)
env['var'] = 2

class A:
    def foo(self, x=var):
        print('foo(): {}'.format(x))

a = A()
""", env)

This code will fail with output:
__setitem__: var
Traceback (most recent call last):
  File "", line 29, in <module>
    """, env)
  File "<string>", line 2, in <module>
  File "<string>", line 3, in A
NameError: name 'var' is not defined

As far as I understand the problem is Python/ceval.c:2120. There is only PyDict_GetItem used to load variable from f_globals, instead of PyObject_GetItem in case of f_globals is not exact dict.
msg305747 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-11-07 12:57
Yes, that's the way it works (and is intended to work, for performance reasons).  The documentation on this could be improved...while it does say globals must be a dict and that locals can be any mapping object, it does it in a sentence that is a bit confusing in this context, and it doesn't make it clear that it has to be a "real" dict, not a subclass.  (I wouldn't be surprised if that sentence was written back when you couldn't subclass dict.)
msg341651 - (view) Author: anthony shaw (anthonypjshaw) * (Python triager) Date: 2019-05-06 22:09
Added a PR for the documentation clarification.
msg344179 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-06-01 15:52
New changeset 059b9ea5ac98f432e41b05d1fa5aab4ffa22df4d by Raymond Hettinger (Anthony Shaw) in branch 'master':
bpo-31968: Documentation -- add clarification on the globals dict for exec() (GH-13140)
Date User Action Args
2019-06-01 15:53:07rhettingersetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2019-06-01 15:52:01rhettingersetnosy: + rhettinger
messages: + msg344179
2019-05-06 22:09:11anthonypjshawsetnosy: + anthonypjshaw
messages: + msg341651
2019-05-06 22:08:47anthonypjshawsetpull_requests: + pull_request13053
2017-11-07 13:30:00Ilya Polyakovskiysetpull_requests: + pull_request4274
2017-11-07 13:15:48Ilya Polyakovskiysetkeywords: + patch
stage: patch review
pull_requests: + pull_request4272
2017-11-07 12:57:38r.david.murraysetversions: + Python 3.7, - Python 3.5
nosy: + docs@python, r.david.murray

messages: + msg305747

assignee: docs@python
components: + Documentation
2017-11-07 12:49:54Ilya Polyakovskiysettype: behavior
2017-11-07 12:49:16Ilya Polyakovskiycreate