Broadly speaking, this line (488 in today's CVS) in Python/weakref.c isn't right: WRAP_UNARY(proxy_int, PyNumber_Int) because PyNumber_Int will convert from a string argument. You can "exploit" this like so: class U(unicode): pass u = U("1") try: range(u) except TypeError: print "raised, good" else: print "didn't raise, bad" import _weakref try: range(_weakref.proxy(u)) except TypeError: print "raised, good" else: print "didn't raise, bad" (prints raised, good didn't raise, bad for me). I think the fix is PyNumber_Int -> PyInt_AsLong, but haven't checked that.
It's not fixed. range() now uses the tp_index slot, in weakrefs this becomes: WRAP_UNARY(proxy_index, PyNumber_Index) and indeed PyNumber_Index does not accept strings. But try with time.sleep() instead; here the line WRAP_UNARY(proxy_float, PyNumber_Float) is involved: import _weakref, time class U(str): pass u = U("1") try: time.sleep(u) except TypeError: print("raised, good") else: print("didn't raise, bad") try: time.sleep(_weakref.proxy(u)) except TypeError: print("raised, good") else: print("didn't raise, bad")
The patch ".patch" is my attempt to fix this bug. 'PyArg_ParseTuple', etc, eventually call 'convertsimple'. What this patch does is to insert some code at the start of 'convertsimple' that checks whether the argument is a weakref proxy and, if it is, fetch the object to which the proxy refers. From then on it's working with the true argument, so it'll work just like would have done if it been given the proxied object itself originally.