This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: dict views don't implement subtraction correctly
Type: Stage: resolved
Components: Interpreter Core Versions: Python 3.6, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: josh.r, python-dev, wim.glenn, xiang.zhang
Priority: normal Keywords:

Created on 2016-03-03 23:20 by josh.r, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (4)
msg261180 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2016-03-03 23:20
Don't know when the problem was introduced, but dictviews_sub is doing:

    tmp = _PyObject_CallMethodId(result, &PyId_difference_update, "O", other);

to implement subtraction (after creating result as a set of the keys in question). That's violating the CallMethod contract though (which states the format string should produce a tuple), and while it looks like CallMethod does fixups when the contract is violated, this creates some very odd behaviors.

With a list, everything is fine:

    >>> d = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
    >>> d.keys() - [0, 2]
    {1, 3}
    >>> d.keys() - (0, 2)
    TypeError: 'int' object is not iterable

Basically, the fix up doesn't get applied when you subtract a tuple, so it's as if it's trying to call:

    result.difference_update(*(0, 2))  # Unpacking used to illustrate, effect is  result.difference_update(0, 2)

With the list, it's wrapping to make a one element tuple, so it behaves like:

    result.difference_update(*([0, 2],))  # Unpacking used to illustrate, effect is  result.difference_update([0, 2])

For more details, see http://stackoverflow.com/q/35784258/364696

Fix should be to change call line to:

    tmp = _PyObject_CallMethodObjArgsId(result, &PyId_difference_update, other, NULL);

(assuming _PyObject_CallMethodObjArgsId is a thing), or if it's not a thing, to fix the format string to force tuple wrapping:

    tmp = _PyObject_CallMethodId(result, &PyId_difference_update, "(O)", other);
msg261182 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2016-03-04 06:11
New changeset 0cae6b6e3d7d by Benjamin Peterson in branch '3.4':
properly use the ObjArgs variant of CallMethod in dictview binary operations (closes #26478)
https://hg.python.org/cpython/rev/0cae6b6e3d7d

New changeset a5bc5c9490f5 by Benjamin Peterson in branch '3.5':
merge 3.4 (closes #26478)
https://hg.python.org/cpython/rev/a5bc5c9490f5

New changeset 9532a8815a9c by Benjamin Peterson in branch 'default':
merge 3.5 (closes #26478)
https://hg.python.org/cpython/rev/9532a8815a9c
msg261193 - (view) Author: wim glenn (wim.glenn) * Date: 2016-03-04 18:05
Well that was patched quickly, impressive turnaround on this
msg261322 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2016-03-08 04:55
New changeset 309f3559a729 by Benjamin Peterson in branch '2.7':
properly use PyObject_CallMethod in dictview binary operations (closes #26478)
https://hg.python.org/cpython/rev/309f3559a729
History
Date User Action Args
2022-04-11 14:58:28adminsetgithub: 70665
2016-03-08 04:55:26python-devsetmessages: + msg261322
2016-03-04 18:05:39wim.glennsetnosy: + wim.glenn
messages: + msg261193
2016-03-04 06:11:05python-devsetstatus: open -> closed

nosy: + python-dev
messages: + msg261182

resolution: fixed
stage: resolved
2016-03-04 02:35:37xiang.zhangsetnosy: + xiang.zhang
2016-03-03 23:25:07josh.rsetcomponents: + Interpreter Core
2016-03-03 23:20:42josh.rcreate