{"action":"created","issue":{"url":"https://api.github.com/repos/ambv/typehinting/issues/155","labels_url":"https://api.github.com/repos/ambv/typehinting/issues/155/labels{/name}","comments_url":"https://api.github.com/repos/ambv/typehinting/issues/155/comments","events_url":"https://api.github.com/repos/ambv/typehinting/issues/155/events","html_url":"https://github.com/ambv/typehinting/issues/155","id":104080414,"number":155,"title":"Bug in __subclasscheck__ causes isinstance() to fail","user":{"login":"JamesGuthrie","id":1117749,"avatar_url":"https://avatars.githubusercontent.com/u/1117749?v=3","gravatar_id":"","url":"https://api.github.com/users/JamesGuthrie","html_url":"https://github.com/JamesGuthrie","followers_url":"https://api.github.com/users/JamesGuthrie/followers","following_url":"https://api.github.com/users/JamesGuthrie/following{/other_user}","gists_url":"https://api.github.com/users/JamesGuthrie/gists{/gist_id}","starred_url":"https://api.github.com/users/JamesGuthrie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JamesGuthrie/subscriptions","organizations_url":"https://api.github.com/users/JamesGuthrie/orgs","repos_url":"https://api.github.com/users/JamesGuthrie/repos","events_url":"https://api.github.com/users/JamesGuthrie/events{/privacy}","received_events_url":"https://api.github.com/users/JamesGuthrie/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-08-31T15:55:16Z","updated_at":"2015-09-01T09:25:54Z","closed_at":null,"body":"From the discussions in #135 and #136 it looks as though `__instancecheck__` and `__subclasscheck__` are here to stay for the collections.abc subclasses.\r\nThere is a bug in the implementation of `__subclasscheck__` for `GenericMeta`. The following sample code exposes the bug:\r\n\r\n```\r\nfrom typing import Iterable\r\n\r\nassert isinstance([], Iterable)\r\nassert isinstance([], Iterable)\r\n```\r\n\r\nThe second call of `isinstance([], Iterable)` returns `False` when it should return `True`.\r\n\r\nThe reason for this appears to be the an incorrect implementation of `__subclasscheck__()` for `GenericMeta`. `super().__subclasscheck__(cls)` on line 1024 of typing.py results in the `list` type being placed in the negative cache of `typing.Iterable`, afterwards `issubclass(cls, self.__extra__)` evaluates to `True`, but the negative cache is not invalidated. On the next check of `isinstance([], Iterable)` the negative cache is consulted and returns `False`.\r\n\r\nOne possible fix is to invalidate `typing.Iterable`'s negative cache if `issubclass(cls, self.__extra__)` evaluates to `True`, as in the following diff:\r\n\r\n```\r\ndiff --git a/prototyping/typing.py b/prototyping/typing.py\r\nindex ddaec3e..f576ce1 100644\r\n--- a/prototyping/typing.py\r\n+++ b/prototyping/typing.py\r\n@@ -1025,7 +1025,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta):\r\n             return True\r\n         if self.__extra__ is None or isinstance(cls, GenericMeta):\r\n             return False\r\n-        return issubclass(cls, self.__extra__)\r\n+        if issubclass(cls, self.__extra__):\r\n+            abc.ABCMeta._abc_invalidation_counter += 1\r\n+            return True\r\n+        return False\r\n\r\n\r\n class Generic(metaclass=GenericMeta):\r\n```\r\n\r\nIf you think my proposed change is good, I'll submit a PR with the change."},"comment":{"url":"https://api.github.com/repos/ambv/typehinting/issues/comments/136647777","html_url":"https://github.com/ambv/typehinting/issues/155#issuecomment-136647777","issue_url":"https://api.github.com/repos/ambv/typehinting/issues/155","id":136647777,"user":{"login":"JamesGuthrie","id":1117749,"avatar_url":"https://avatars.githubusercontent.com/u/1117749?v=3","gravatar_id":"","url":"https://api.github.com/users/JamesGuthrie","html_url":"https://github.com/JamesGuthrie","followers_url":"https://api.github.com/users/JamesGuthrie/followers","following_url":"https://api.github.com/users/JamesGuthrie/following{/other_user}","gists_url":"https://api.github.com/users/JamesGuthrie/gists{/gist_id}","starred_url":"https://api.github.com/users/JamesGuthrie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JamesGuthrie/subscriptions","organizations_url":"https://api.github.com/users/JamesGuthrie/orgs","repos_url":"https://api.github.com/users/JamesGuthrie/repos","events_url":"https://api.github.com/users/JamesGuthrie/events{/privacy}","received_events_url":"https://api.github.com/users/JamesGuthrie/received_events","type":"User","site_admin":false},"created_at":"2015-09-01T09:25:54Z","updated_at":"2015-09-01T09:25:54Z","body":"Yes you're right, I've taken another look at the code in order to better determine where the fix should be. I assume this issue cropped up when `__instancecheck__` was removed from GenericMeta.\r\n\r\nThe call graph for `isinstance([], Iterable)` is:\r\n1. `ABCMeta.__instancecheck__(cls=typing.Iterable, instance=[])`\r\nInitially the caches aren't populated so `cls.__subclasscheck__(<class 'list'>)` is called:\r\n2. `GenericMeta.__subclasscheck__(self=typing.Iterable, cls=<class 'list'>)`\r\nNext the supertype of typing.Iterable is consulted `super().__subclasscheck__(cls)`\r\n3. `ABCMeta.__subclasscheck__(cls=typing.Iterable, subclass=<class 'list'>)`\r\nHere it is determined that `<class 'list'>` is not a subclass of `typing.Iterable` and the negative cache of `typing.Iterable` is set, returning back to `GenericMeta.__subclasscheck__(self=typing.Iterable, cls=<class 'list'>)`.\r\nFinally, the check for `issubclass(cls, self.__extra__)` is done:\r\n4. `ABCMeta.__subclasscheck__(cls=collections.abc.Iterable, subclass=<class 'list'>)`\r\nWhich returns True.\r\n\r\nThe next pass through, the first call is `ABCMeta.__instancecheck__(cls=typing.Iterable, instance=[])`, which has a negative cache set, so it returns False.\r\n\r\nI see two issues and potential paths to a fix:\r\n1. Handing control of `__instancecheck__` to the superclass is breaking the expected behaviour, `__instancecheck__` should be implemented in `GenericMeta` (if this is the solution, we need to figure out how to reconcile this with #135.\r\n2. The logic in `GenericMeta.__subclasscheck__` needs to be fixed.\r\n  * Reorder the checks, first consulting `issubclass(cls, self.__extra__)`, and only then consulting `super().__subclasscheck__(cls)`. I'm not sure if the ordering matters much here, the test suite passes with that ordering.\r\n  * We could fiddle around in `typing.Iterable`'s caches, removing the negative cache entry and putting the correct entry in the cache. This seems awfully messy though.\r\n"}}