-
-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug in BidirectionalMapping.__subclasshook__(): Any class with an inverse attribute is considered a collections.abc.Mapping #111
Comments
I think I found the issue. It is in https://github.com/jab/bidict/blob/master/bidict/_abc.py in the subclasshook method.
|
...that caused classes with an `inverse` attribute to be incorrectly considered subclasses of `collections.abc.Mapping`. Fixes #111.
Sorry for the bug and thanks so much for the helpful repro! Pushed a fix to the abcfix branch. Would you be willing to test your code with this version to confirm the fix works for you? |
While this does fix the issue for me, I think the fix is potentially dangerous because we never actually explicitly check if C is a subclass of collections.abc.Mapping so it might rear it's ugly head again?(not sure)
and that also seems to work for me but ofc may be even that isn't real fix since I don't have the whole bidict project running with unit tests etc. |
Thanks for confirming my fix! The alternative changes you're proposing[1] are insufficient. Consider the following: >>> from collections.abc import Mapping
>>> class MyMapping(Mapping):
... pass
...
>>> Mapping.__subclasshook__(MyMapping)
NotImplemented
>>> issubclass(MyMapping, Mapping)
True So we can't bail early if the In particular, this would break the following case: >>> class VirtualBidirectionalMapping(collections.abc.Mapping):
... def inverse(self):
... return ...
...
>>> issubclass(VirtualBidirectionalMapping, BidirectionalMapping)
True In other words, if we want to preserve the existing behavior, where any This is the same approach taken by Note that I have lots of test cases exercising this fixed Thanks! [1] I think a complete version of what you proposed would actually be something like this: diff --git a/bidict/_abc.py b/bidict/_abc.py
index 8f1b690..ce2dd98 100644
--- a/bidict/_abc.py
+++ b/bidict/_abc.py
@@ -105,8 +105,9 @@ class BidirectionalMapping(Mapping): # pylint: disable=abstract-method,no-init
"""
if cls is not BidirectionalMapping: # lgtm [py/comparison-using-is]
return NotImplemented
- if not Mapping.__subclasshook__(C):
- return NotImplemented
+ super_subclasshook = super().__subclasshook__(C)
+ if super_subclasshook in (NotImplemented, False):
+ return super_subclasshook
mro = C.__mro__
if not any(B.__dict__.get('inverse') for B in mro):
return NotImplemented
return True |
...that caused classes with an `inverse` attribute to be incorrectly considered subclasses of `collections.abc.Mapping`. Fixes #111.
...that caused classes with an `inverse` attribute to be incorrectly considered subclasses of `collections.abc.Mapping`. Fixes #111.
Makes sense. Thanks for fixing this fast. |
My pleasure, and thank you again for the helpful bug report. Another option I’m considering here is to just remove the subclasshook. From polling users in the bidict Gitter chat, as well as a code search on GitHub, so far it appears to be unused, and so is just pure maintenance burden (especially since it requires such fiddly, subtle code to implement properly, and causes crazy bugs like this one if you don’t get it quite right). I’m still thinking about it, but in the meantime, any objection from your side to dropping support for virtual BidirectionalMapping subclasses? |
+1 for removing subclasshook. |
...due to lack of use and maintenance cost. Fixes #111.
...due to lack of use and maintenance cost. Fixes #111.
...due to lack of use and maintenance cost. Fixes #111.
I just released v0.20.0 to PyPI with a fix for this issue (removing |
Importing bidict causes the tfp.sts.Autoregressive class to not work at all. Simplest code to reproduce the problem is as follows
Not sure if the issue is with tfp or bidict.
bidict :0.19.0
tf : 2.2.0
tfp:0.10.0
see tensorflow/probability#1016
The text was updated successfully, but these errors were encountered: