classification
Title: SystemError in class creation in case of a metaclass with a bad __prepare__() method
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.7, Python 3.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Oren Milman, eric.snow, ncoghlan, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2017-09-26 07:38 by Oren Milman, last changed 2017-09-27 16:28 by ncoghlan. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 3764 merged Oren Milman, 2017-09-26 11:15
PR 3790 merged python-dev, 2017-09-27 14:05
Messages (8)
msg303019 - (view) Author: Oren Milman (Oren Milman) * Date: 2017-09-26 07:38
The following code causes a SystemError:
class BadMetaclass(type):
    def __prepare__(*args):
        pass

class Foo(metaclass=BadMetaclass):
    pass


This is because builtin___build_class__() assumes that __prepare__() returned a
mapping, and passes it to PyEval_EvalCodeEx(), which passes it to
_PyEval_EvalCodeWithName(), which passes it to _PyFrame_New_NoTrack(), which
raises the SystemError.


This issue seems related to #17421.
msg303118 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-09-27 09:48
I'm not a metaclass expert. Eric, Nick, could you please take a look at the patch?
msg303121 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-09-27 10:06
There's no __prepare__ method in 2.7, so that version can't be affected by this.

For 3.6 and 3.7, I can't reproduce the reported SystemError. Instead, I get:

```
>>> class BadMetaclass(type):
...     def __prepare__(*args):
...         pass
... 
>>> class Foo(metaclass=BadMetaclass):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in Foo
TypeError: 'NoneType' object is not subscriptable

```

The error remains the same if I add `@classmethod` to the __prepare__ definition.


That's still thoroughly cryptic and worth changing, but indicates the test should be checking the error message details, not just the error type.
msg303122 - (view) Author: Oren Milman (Oren Milman) * Date: 2017-09-27 10:13
Nick, maybe you tried to reproduce in release?
In debug (where I got the SystemError), you have in the beginning of _PyFrame_New_NoTrack():
#ifdef Py_DEBUG
    if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
        (locals != NULL && !PyMapping_Check(locals))) {
        PyErr_BadInternalCall();
        return NULL;
    }
msg303143 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-09-27 14:01
Aye, that's exactly what I did.
msg303144 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-09-27 14:04
New changeset 5837d0418f47933b2e3c139bdee8a79c248a943c by Nick Coghlan (Oren Milman) in branch 'master':
bpo-31588: Validate return value of __prepare__() methods (GH-3764)
https://github.com/python/cpython/commit/5837d0418f47933b2e3c139bdee8a79c248a943c
msg303146 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-09-27 14:07
CI is still running for the backport, so I'll merge that tomorrow.

Thanks for the patch!
msg303159 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-09-27 16:21
New changeset 084f80b82c564c8a3cef26fc6e56da188a379be2 by Nick Coghlan (Miss Islington (bot)) in branch '3.6':
[3.6] bpo-31588: Validate return value of __prepare__() methods (GH-3790)
https://github.com/python/cpython/commit/084f80b82c564c8a3cef26fc6e56da188a379be2
History
Date User Action Args
2017-09-27 16:28:47ncoghlansetstatus: open -> closed
stage: backport needed -> resolved
2017-09-27 16:21:35ncoghlansetmessages: + msg303159
2017-09-27 14:07:40ncoghlansetresolution: fixed
messages: + msg303146
stage: patch review -> backport needed
2017-09-27 14:05:45python-devsetpull_requests: + pull_request3776
2017-09-27 14:04:39ncoghlansetmessages: + msg303144
2017-09-27 14:01:41ncoghlansetmessages: + msg303143
2017-09-27 10:13:21Oren Milmansetmessages: + msg303122
2017-09-27 10:06:13ncoghlansetmessages: + msg303121
versions: - Python 2.7
2017-09-27 09:48:16serhiy.storchakasetmessages: + msg303118
versions: + Python 2.7, Python 3.6
2017-09-26 13:31:33serhiy.storchakasetnosy: + ncoghlan, eric.snow
2017-09-26 11:15:44Oren Milmansetkeywords: + patch
stage: patch review
pull_requests: + pull_request3749
2017-09-26 07:50:51serhiy.storchakasetnosy: + serhiy.storchaka
2017-09-26 07:50:01serhiy.storchakalinkissue17421 dependencies
2017-09-26 07:38:12Oren Milmancreate