views:

108

answers:

3

MySQLDb uses weak proxy to prevent circular dependencies between cursors and connections.

But you would expect from the documentation on weakref that you could still tests for equivalence. Yet:

In [36]: interactive.cursor.connection.thread_id()
Out[36]: 4267758

In [37]: interactive.web_logic.conns.primary.thread_id()
Out[37]: 4267758

In [38]: interactive.cursor.connection == interactive.web_logic.conns.primary
Out[38]: False

In [39]: interactive.cursor.connection
Out[39]: <weakproxy at 0x3881c60 to Connection at 0x94c010>

In [40]: interactive.web_logic.conns.primary
Out[40]: <_mysql.connection open to 'xendb01' at 94c010>

How do I tell if the connections are the same ?

A: 

If the object is a standard weakref, you need to call it to get the object itself.

import weakref
class Test(object): pass
a = Test()
b = weakref.ref(a)
a is b() # True
a == b() # True

Using weakrefs here seems wrong, though: if I construct a connection, create a cursor from it, and discard the connection object, the cursor should remain valid. There shouldn't be a circular dependency unless the connection is keeping a list of all cursors, in which case that is what should be the weakref.

Glenn Maynard
it's a weakproxy, not a weakref.Perhaps a weakness in the weakproxy implementation ?
rhettg
Oh, you mean weakref.proxy (not "weakproxy", at least not in the Python standard lib). That doesn't seem to expose any method to retrieve the original object, which seems like a major limitation of the type. It must be an oversight; this limitation seems completely unnecessary. There doesn't even seem to be any way to find out if an object is a proxy (short of trying to parse obj.__str__()). This type doesn't seem very fully fleshed-out.
Glenn Maynard
That said, try "obj1.__dict__ is obj2.__dict__". ("You may only post one comment every 30 seconds." This site is occasionally irritating.)
Glenn Maynard
Yeah i got 'weakproxy' from the str representation and from reading the c code. But it appears that weakref.proxy is the only way to actually create it.
rhettg
+2  A: 

I've long found weakref.proxy's design and implementation to be somewhat shaky. Witness...:

>>> import weakref
>>> ob=set(range(23))
>>> rob=weakref.proxy(ob)
>>> rob==ob
False
>>> rob.__eq__(ob)
True

...DEFINITELY peculiar! In practice what I use from weakref are weak-key or sometimes weak-value dictionaries; but weakref.ref is sounder than the proxy wrapper on top of it:

>>> wr=weakref.ref(ob)
>>> wr()==ob
True

The need to "call" the ref to get the object (or None if the object has since disappeared) unfortunately makes it non-transparent (so a DB API module couldn't do it while staying compliant to the API). I don't understand why MySqlDb wants weak cursor->connection referencing at all, but if they do I see why they felt they had to use proxies rather than refs. However, one pays a very high price for that transparency!

Btw, the "explicit __eq__" trick (or an equivalent one with __cmp__, depending on the type of the underlying object) may help you, even though it's definitely inelegant!

Alex Martelli
I always thought that proxy pulls some very ugly abstraction breaking magic behind the scenes to avoid having to call a function like ref. Now I know for sure.
Unknown
Your "particular" comparison results are because set.__eq__ doesn't know about proxies. It sees that the other object isn't a set, so it returns False. rob.__eq__ ends up being proxied from ob.__eq__, so it works in that direction.It's a wart without a good solution. (The options are to either have set and every other type check for instances of weakref.proxy, or have the python interpreter transparently replace it before calling __eq__ etc.)
Matthew Marshall
@Matthew: not quite, `rob==ob` and `ob==rob` are both false (though `rob.__eq__(ob)` and `ob.__eq__(rob)` do behave as you indicate). It's quite possible there's no good way to make a sufficiently transparent proxy, I'm just unhappy we tried in the first place!-)
Alex Martelli
+1  A: 

Wrap the non proxy with weakref.proxy and use the identity operator:

>>> interactive.cursor.connection is weakref.proxy(interactive.web_logic.conns.primary)
True

Calling weakref.proxy() twice will return the same proxy object.

Matthew Marshall
That's sort of ugly. You'd also have to do it on both sides, too, if you don't want to assume either side is a proxy. I suppose this will work in some obscure cases where obj.__dict__ is being assigned to something else (eg. multiple objects share a __dict__), but in most cases I'd still stick to just comparing __dict__.
Glenn Maynard
That's a pretty cool hack actually. Guess I COULD go around converting all operands to weakref.proxy before comparing. Makes weakref.proxy kinda of a virus that infects your whole application......
rhettg
Also, it assumes that this behavior is guaranteed. It has the feel of an optimization that you shouldn't depend on, since I don't see the behavior mentioned in the docs. It's probably reasonably safe, but if you end up using this in your code a lot and the assumption is broken later on, it may become a difficult-to-fix problem.
Glenn Maynard