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: reversed() does not work with weakref.proxy of sequences
Type: behavior Stage: needs patch
Components: Library (Lib) Versions: Python 3.10, Python 3.9, Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: benjamin.peterson Nosy List: Claudiu.Popa, abingham, benjamin.peterson, iritkatriel, pitrou, rhettinger, vstinner
Priority: normal Keywords: patch

Created on 2013-10-23 07:00 by abingham, last changed 2022-04-11 14:57 by admin.

Files
File name Uploaded Description Edit
reversed_proxy_failure.py abingham, 2013-10-23 07:00 Demonstrates issue
reversed_proxy.patch Claudiu.Popa, 2013-10-23 21:26 review
issue19359.patch Claudiu.Popa, 2014-03-12 13:50 Remove trailing whitespace. review
reversed_list_proxy_test.py rhettinger, 2014-03-14 09:00 Show that __reversed__ isn't detected on a proxy
Messages (8)
msg201002 - (view) Author: Austin Bingham (abingham) Date: 2013-10-23 07:00
When passed a weakref.proxy to a legitimate sequence, reversed() throws a TypeError complaining that its argument isn't a sequence. Perhaps this is the expected behavior, but it's surprising to at least me and the few others I've spoken with about it.
msg201069 - (view) Author: PCManticore (Claudiu.Popa) * (Python triager) Date: 2013-10-23 21:26
Attached an attemptive patch.
msg213526 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2014-03-14 09:00
Thanks for looking at this.

Putting in a special case for weak references isn't the way to go.  The problem is that _PyObject_LookupSpecial isn't working well with weakref proxies.

A solution for reversed() could be to replace ``PyObject_GetAttrString(seq, "__reversed__")`` with the slower call to ``_PyObject_LookupSpecial(seq, "__reversed__", &reversed_cache);``.

The unfortunate downside is that this would slow down the common cases.

Another solution is to patch _PyObject_LookupSpecial to make it smarter with respect to weakref objects or possibly use a fallback to PyObject_GetAttrString when a method isn't found using the speedy lookup.

The advantage of fixing _PyObject_LookupSpecial is that it fixes all uses of _PyObject_LookupSpecial not just reversed().  Also, it would keep the current speed benefit for the common case.

I'm reassigning to Benjamin because it was his optimized that broke the ability to find the __reversed__ method on a proxy object.

Another issue that needs to be looked at is whether PySequence_Check() needs to be made aware of weakref proxies.
msg213528 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-03-14 10:04
I suppose Benjamin's commit is afd62eb1692e.

Claudiu, that doesn't look like the best approach to me. Instead of hardcoding a weakref.proxy check in reversed(), why not implement __reversed__ on weakref.proxy?
msg213529 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-03-14 10:07
It would also perhaps be practical to have some kind of "proxy mixin" that everyone can re-use to avoid having to reimplement __special__ methods by hand.
msg213530 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-03-14 10:11
Another possible idea is to introduce a "proxy protocol" (__proxy__ /
tp_proxy) that would be used as a fallback by PyObject_LookupSpecial to
fetch the lookup target, i.e.:

def PyObject_LookupSpecial(obj, name):
    tp = type(obj)
    try:
        return getattr(tp, name)
    except AttributeError:
        return getattr(tp.tp_proxy(), name)

(not sure that makes sense, I haven't thought very hard about it)
msg213562 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2014-03-14 15:14
afd62eb1692e wasn't a matter of speed, but a matter of correctness. Special methods should be looked up on the type on the instance as was done before.
msg388678 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-03-14 17:52
On 3.10 the reversed_proxy_failure.py script fails with

Traceback (most recent call last):
  File "/Users/iritkatriel/src/cpython/tmp.py", line 18, in <module>
    reversed(weakref.proxy(foo))  # TypeError
AttributeError: 'Foo' object has no attribute '__reversed__'
History
Date User Action Args
2022-04-11 14:57:52adminsetgithub: 63558
2021-03-14 17:52:30iritkatrielsetnosy: + iritkatriel

messages: + msg388678
versions: + Python 3.8, Python 3.9, Python 3.10, - Python 2.7, Python 3.4, Python 3.5
2014-03-14 15:14:03benjamin.petersonsetmessages: + msg213562
2014-03-14 10:11:48pitrousetmessages: + msg213530
2014-03-14 10:07:50pitrousetmessages: + msg213529
2014-03-14 10:04:53pitrousetnosy: + pitrou
messages: + msg213528
2014-03-14 09:00:02rhettingersetfiles: + reversed_list_proxy_test.py
priority: low -> normal
type: enhancement -> behavior

assignee: rhettinger -> benjamin.peterson
versions: + Python 2.7, Python 3.4
nosy: + vstinner, benjamin.peterson

messages: + msg213526
stage: needs patch
2014-03-13 23:29:37rhettingersetpriority: normal -> low
type: behavior -> enhancement
versions: + Python 3.5, - Python 2.7, Python 3.3
2014-03-12 13:50:51Claudiu.Popasetfiles: + issue19359.patch
2013-10-24 16:27:28rhettingersetassignee: rhettinger
2013-10-23 21:26:05Claudiu.Popasetfiles: + reversed_proxy.patch

nosy: + Claudiu.Popa
messages: + msg201069

keywords: + patch
2013-10-23 08:29:10pitrousetnosy: + rhettinger
2013-10-23 07:00:08abinghamcreate