classification
Title: BUILD_MAP_UNPACK doesn't function as expected for dict subclasses
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: bup, serhiy.storchaka, steven.daprano
Priority: normal Keywords:

Created on 2018-11-05 06:23 by bup, last changed 2018-11-05 07:17 by steven.daprano.

Messages (4)
msg329280 - (view) Author: Dan Snider (bup) * Date: 2018-11-05 06:23
>>> class Dict(dict):
       
	def keys(self): assert 0
	def update(*args, **kwds): assert 0
	def __getitem__(self, key): assert 0
        def __iter__(self): assert 0 

	
>>> {**Dict(a=1)}
{'a': 1}

The opcode uses PyDict_Update, which calls the internal dict_merge function which contains the following line:

    if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter))

Translated to Python, that should be equal to 

    if type(b).__flags__ & (1<<29) and type.__getattribute__(type(b), '__iter__') is type.__getattribute__(dict, '__iter__')`

Both that and the line in C evaluate to false for me (while a dict subclass that doesn't override __iter__ evaluates to true), so I 
 apparently can't help narrow down the problem any further assuming people agree that this is even is a problem...

The BUILD_MAP_UNPACK_WITH_CALL, CALL_FUNCTION_EX, and CALL_FUNCTION_KW opcodes are affected as well.
msg329281 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-11-05 06:30
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in keys
AssertionError

This works as expected to me.
msg329282 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2018-11-05 07:14
You say it doesn't work as expected, but you don't say what you expect or why. (Don't make me guess what you mean -- explicit is better than implicit.)

When I try your subclass in 3.6, I get an unexpected TypeError:

py> class Dict(dict):
...     def keys(self): assert 0
...     def update(*args, **kwds): assert 0
...     def __getitem__(self, key): assert 0
...     def __iter__(self): assert 0
...
py> {**Dict(a=1)}
{'a': 1}
py> Dict(a=1).keys()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in keys
TypeError
msg329283 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2018-11-05 07:17
How weird... after restarting the interpreter, I can't reproduce that TypeError. I get the AssertionError Serhiy showed.
History
Date User Action Args
2018-11-05 07:17:36steven.dapranosetmessages: + msg329283
2018-11-05 07:14:10steven.dapranosetnosy: + steven.daprano
messages: + msg329282
2018-11-05 06:30:31serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg329281
2018-11-05 06:23:01bupcreate