-
Notifications
You must be signed in to change notification settings - Fork 28
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
Python 2: Forbid type objects as keys. #163
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to remove myself from the reviewers list as I do not understand what's going on here.
I apologize for being unclear. I admit to mixing in the fix for #153 ("Python 3 forbids classes as keys, but Python 2 doesn't") with some semi-unrelated cleanup. The direct fix for #153 was to specifically check It turns out there are a lot of differences among the supported implementations of Python as to how "doesn't implement comparison" can be detected. The refactoring changes both simplified and improved the speed of that. (I hope.) Please let me know if I can help clarify further. I'd very much like to get this merged and released. |
Fixes #153. Also take the opportunity to clean up the TODOs in _datatypes.py with regards to checking for default comparison: Use a metaclass so we get caching, and only check for the special methods on the type object.
def __call__(self, item): | ||
self._check_default_comparison(item) | ||
if isinstance(item, _HasDefaultComparison): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_HasDefaultComparison
is defined as a new class above but where is item
registered for it, so this check can become true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_HasDefaultComparison is defined as a new class above
Right. Specifically, it's defined as a new abstract base class, i.e., something that extends abc.ABCMeta
. (We could do this with a regular type
subclass, but extending ABCMeta
gets us caching of the results of isinstance
automatically.)
where is
item
registered for it, so this check can become true?
That's the beauty part (as the saying goes). By making _HasDefaultComparison
implement __subclasshook__
, we algorithmically define what isinstance(item, _HasDefaultComparison)
means. No need to register anything! The algorithm is the same as what was here before, just moved into __subclasshook__
. (The expression isinstance(item, Cls)
basically boils down to Cls.__subclasscheck__(type(item))
; ABCMeta
defines __subclasscheck__
to do some caching, and if the answer is not in the cache, to call Cls.__subclasshook__()
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did that help clear things up sufficiently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL, I think I now understood how it works. Maybe adding a link to the Python documentation would help future readers of the code who rarely use abstract base classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
Thank you! |
Fixes #153.
Also take the opportunity to clean up the TODOs in _datatypes.py with regards to checking for default comparison: Use a metaclass so we get caching, and only check for the special methods on the type object.