{"action":"created","comment":{"url":"https://api.github.com/repos/python/mypy/pulls/comments/87673625","pull_request_review_id":8117206,"id":87673625,"diff_hunk":"@@ -893,10 +894,61 @@ def analyze_ordinary_member_access(self, e: MemberExpr,\n             return self.analyze_ref_expr(e)\n         else:\n             # This is a reference to a non-module attribute.\n-            return analyze_member_access(e.name, self.accept(e.expr), e,\n+            base_type = self.accept(e.expr)\n+            member_type = analyze_member_access(e.name, base_type, e,\n                                          is_lvalue, False, False,\n                                          self.named_type, self.not_ready_callback, self.msg,\n                                          chk=self.chk)\n+            if is_lvalue:\n+                return member_type\n+            else:\n+                return self.analyze_descriptor_access(base_type, member_type, e)\n+\n+    def analyze_descriptor_access(self, instance_type: Type, descriptor_type: Type,\n+                                  context: Context) -> Type:\n+        \"\"\"\n+        Type check descriptor access.\n+\n+        Return the inferred rvalue_type and whether to infer anything about the lvalue type\n+        \"\"\"\n+        if not isinstance(descriptor_type, Instance):\n+            return descriptor_type\n+\n+        if not descriptor_type.type.has_readable_member('__get__'):\n+            return descriptor_type\n+\n+        dunder_get = descriptor_type.type.get_method('__get__')\n+\n+        if dunder_get is None:\n+            self.msg.fail(\"{}.__get__ is not callable\".format(descriptor_type), context)\n+            return AnyType()\n+\n+        function = function_type(dunder_get, self.named_type('builtins.function'))\n+        bound_method = bind_self(function, descriptor_type)\n+        typ = map_instance_to_supertype(descriptor_type, dunder_get.info)\n+        dunder_get_type = expand_type_by_instance(bound_method, typ)\n+\n+        if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj():\n+            instance_type = instance_type.items()[0].ret_type\n+            _, inferred_dunder_get_type = self.check_call(\n+                dunder_get_type, [TempNode(NoneTyp()), TempNode(TypeType(instance_type))],\n+                [nodes.ARG_POS, nodes.ARG_POS], context\n+            )\n+        else:\n+            _, inferred_dunder_get_type = self.check_call(","path":"mypy/checkexpr.py","position":72,"original_position":72,"commit_id":"9d5f770016c3e63e110990bc02ae06673b368bf9","original_commit_id":"9d5f770016c3e63e110990bc02ae06673b368bf9","user":{"login":"gvanrossum","id":2894642,"avatar_url":"https://avatars.githubusercontent.com/u/2894642?v=3","gravatar_id":"","url":"https://api.github.com/users/gvanrossum","html_url":"https://github.com/gvanrossum","followers_url":"https://api.github.com/users/gvanrossum/followers","following_url":"https://api.github.com/users/gvanrossum/following{/other_user}","gists_url":"https://api.github.com/users/gvanrossum/gists{/gist_id}","starred_url":"https://api.github.com/users/gvanrossum/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gvanrossum/subscriptions","organizations_url":"https://api.github.com/users/gvanrossum/orgs","repos_url":"https://api.github.com/users/gvanrossum/repos","events_url":"https://api.github.com/users/gvanrossum/events{/privacy}","received_events_url":"https://api.github.com/users/gvanrossum/received_events","type":"User","site_admin":false},"body":"Maybe there's a way to refactor these branches so that there's only one call to check_call()?","created_at":"2016-11-11T23:14:09Z","updated_at":"2016-11-11T23:21:00Z","html_url":"https://github.com/python/mypy/pull/2266#discussion_r87673625","pull_request_url":"https://api.github.com/repos/python/mypy/pulls/2266","_links":{"self":{"href":"https://api.github.com/repos/python/mypy/pulls/comments/87673625"},"html":{"href":"https://github.com/python/mypy/pull/2266#discussion_r87673625"},"pull_request":{"href":"https://api.github.com/repos/python/mypy/pulls/2266"}}},"pull_request":{"url":"https://api.github.com/repos/python/mypy/pulls/2266","id":89688003,"html_url":"https://github.com/python/mypy/pull/2266","diff_url":"https://github.com/python/mypy/pull/2266.diff","patch_url":"https://github.com/python/mypy/pull/2266.patch","issue_url":"https://api.github.com/repos/python/mypy/issues/2266","number":2266,"state":"open","locked":false,"title":"Implement the descriptor protocol","user":{"login":"cpennington","id":30236,"avatar_url":"https://avatars.githubusercontent.com/u/30236?v=3","gravatar_id":"","url":"https://api.github.com/users/cpennington","html_url":"https://github.com/cpennington","followers_url":"https://api.github.com/users/cpennington/followers","following_url":"https://api.github.com/users/cpennington/following{/other_user}","gists_url":"https://api.github.com/users/cpennington/gists{/gist_id}","starred_url":"https://api.github.com/users/cpennington/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cpennington/subscriptions","organizations_url":"https://api.github.com/users/cpennington/orgs","repos_url":"https://api.github.com/users/cpennington/repos","events_url":"https://api.github.com/users/cpennington/events{/privacy}","received_events_url":"https://api.github.com/users/cpennington/received_events","type":"User","site_admin":false},"body":"This is an attempt to implement the descriptor protocol (as per https://github.com/python/mypy/issues/244).\n\nHowever, it currently is suffering from being being unable to resolve type variables during assignment, and I'm not sure what I would need to resolve that. For instance, using this implementation of properties:\n\n```\nfrom typing import Any, Text, TypeVar, Optional, Type, Generic, Callable, Union\nfrom operator import attrgetter\n\nT = TypeVar('T')\nG = TypeVar('G')\nS = TypeVar('S')\n\nclass MyProperty(Generic[T, G]):\n    def __init__(\n        self,\n        fget: Callable[[T], G],\n        fset: Optional[Callable[[T, G], None]] = None,\n        fdel: Optional[Callable[[T], None]] = None,\n        doc: Optional[Text] = None,\n    ) -> None:\n        self._getter = fget\n        self._setter = fset\n        self._deleter = fdel\n        if doc:\n            self.__doc__ = doc\n        else:\n            self.__doc__ = fget.__doc__\n\n    def setter(self, fset: Callable[[T, G], None]):\n        return MyProperty(self._getter, fset, self._deleter, self.__doc__)\n\n    def deleter(self, fdel: Callable[[T], None]):\n        return MyProperty(self._getter, self._setter, fdel, self.__doc__)\n\n    def __get__(self, instance: Optional[T], owner: Type[T]) -> Union['MyProperty[T, G]', G]:\n        if instance is None:\n            return self\n\n        return self._getter(instance)\n\n    def __set__(self, instance: T, value: G) -> None:\n        if self._setter:\n            self._setter(instance, value)\n        else:\n            raise Exception()\n\n    def __delete__(self, instance: T) -> None:\n        if self._deleter:\n            self._deleter(instance)\n        else:\n            raise Exception()\n\nclass NonGenericNonDataDescriptor(Generic[T]):\n    def __init__(self, value: int) -> None:\n        self._value = value  # type: int\n\n    def __get__(self, instance: Optional[T], owner: Type[T]) -> int:\n        return self._value\n\nclass NonGenericDataDescriptor(Generic[T]):\n    def __init__(self, value: int) -> None:\n        self._value = value  # type: int\n\n    def __get__(self, instance: Optional[T], owner: Type[T]) -> Union['NonGenericDataDescriptor[T]', int]:\n        if instance is None:\n            return self\n\n        return self._value\n\n    def __set__(self, instance: T, value: int) -> None:\n        self._value = value\n\n\nclass Test:\n    my_unique_name = MyProperty(attrgetter('foo'))  # type: MyProperty[Test, int]\n    ten = NonGenericNonDataDescriptor(10)  # type: NonGenericNonDataDescriptor[Test]\n    some_int = NonGenericDataDescriptor(5)  # type: NonGenericDataDescriptor[Test]\n\n    @MyProperty\n    def my_other_name(self):\n        return self.bar\n\n    def __init__(self) -> None:\n        self.foo = 0\n        self.bar = \"ten\"\n\ndef f() -> None:\n    t = Test()\n\n    t.my_unique_name = 1\n    t.my_unique_name + 1\n\n    t.ten + 1\n\n    t.some_int = 7\n    t.some_int + 1\n```\n\nWhen checked with mypy, we get the following errors:\n\n```\n$ mypy --fast-parser test_descriptors.py --show-traceback\n\ntest_descriptors.py: note: In function \"f\":\ntest_descriptors.py:85: error: Incompatible types in assignment (expression has type \"int\", variable has type \"G\")\ntest_descriptors.py:86: error: Unsupported operand types for + (\"Union[MyProperty[Test, int], int]\" and \"int\")\n```\n\nThe second error is because we don't have overloading outside of stub files (see https://github.com/python/mypy/issues/1136), I think, but the former is the one I'm not sure how to resolve.\n","created_at":"2016-10-17T19:37:47Z","updated_at":"2016-11-11T23:21:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b8c5b00bbfeb25d3014aa174cdf645b72ae3869c","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/python/mypy/pulls/2266/commits","review_comments_url":"https://api.github.com/repos/python/mypy/pulls/2266/comments","review_comment_url":"https://api.github.com/repos/python/mypy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/python/mypy/issues/2266/comments","statuses_url":"https://api.github.com/repos/python/mypy/statuses/9d5f770016c3e63e110990bc02ae06673b368bf9","head":{"label":"edx:implement-descriptor-protocol","ref":"implement-descriptor-protocol","sha":"9d5f770016c3e63e110990bc02ae06673b368bf9","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":71173895,"name":"mypy","full_name":"edx/mypy","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/mypy","description":"Optional static typing for Python 2 and 3 (PEP484)","fork":true,"url":"https://api.github.com/repos/edx/mypy","forks_url":"https://api.github.com/repos/edx/mypy/forks","keys_url":"https://api.github.com/repos/edx/mypy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/mypy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/mypy/teams","hooks_url":"https://api.github.com/repos/edx/mypy/hooks","issue_events_url":"https://api.github.com/repos/edx/mypy/issues/events{/number}","events_url":"https://api.github.com/repos/edx/mypy/events","assignees_url":"https://api.github.com/repos/edx/mypy/assignees{/user}","branches_url":"https://api.github.com/repos/edx/mypy/branches{/branch}","tags_url":"https://api.github.com/repos/edx/mypy/tags","blobs_url":"https://api.github.com/repos/edx/mypy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/mypy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/mypy/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/mypy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/mypy/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/mypy/languages","stargazers_url":"https://api.github.com/repos/edx/mypy/stargazers","contributors_url":"https://api.github.com/repos/edx/mypy/contributors","subscribers_url":"https://api.github.com/repos/edx/mypy/subscribers","subscription_url":"https://api.github.com/repos/edx/mypy/subscription","commits_url":"https://api.github.com/repos/edx/mypy/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/mypy/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/mypy/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/mypy/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/mypy/contents/{+path}","compare_url":"https://api.github.com/repos/edx/mypy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/mypy/merges","archive_url":"https://api.github.com/repos/edx/mypy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/mypy/downloads","issues_url":"https://api.github.com/repos/edx/mypy/issues{/number}","pulls_url":"https://api.github.com/repos/edx/mypy/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/mypy/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/mypy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/mypy/labels{/name}","releases_url":"https://api.github.com/repos/edx/mypy/releases{/id}","deployments_url":"https://api.github.com/repos/edx/mypy/deployments","created_at":"2016-10-17T19:32:56Z","updated_at":"2016-10-17T19:32:59Z","pushed_at":"2016-11-04T13:41:12Z","git_url":"git://github.com/edx/mypy.git","ssh_url":"git@github.com:edx/mypy.git","clone_url":"https://github.com/edx/mypy.git","svn_url":"https://github.com/edx/mypy","homepage":"http://www.mypy-lang.org/","size":16295,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"python:master","ref":"master","sha":"7b6fbb76cf1c80e859581c6e5538b399fc56039b","user":{"login":"python","id":1525981,"avatar_url":"https://avatars.githubusercontent.com/u/1525981?v=3","gravatar_id":"","url":"https://api.github.com/users/python","html_url":"https://github.com/python","followers_url":"https://api.github.com/users/python/followers","following_url":"https://api.github.com/users/python/following{/other_user}","gists_url":"https://api.github.com/users/python/gists{/gist_id}","starred_url":"https://api.github.com/users/python/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/python/subscriptions","organizations_url":"https://api.github.com/users/python/orgs","repos_url":"https://api.github.com/users/python/repos","events_url":"https://api.github.com/users/python/events{/privacy}","received_events_url":"https://api.github.com/users/python/received_events","type":"Organization","site_admin":false},"repo":{"id":7053637,"name":"mypy","full_name":"python/mypy","owner":{"login":"python","id":1525981,"avatar_url":"https://avatars.githubusercontent.com/u/1525981?v=3","gravatar_id":"","url":"https://api.github.com/users/python","html_url":"https://github.com/python","followers_url":"https://api.github.com/users/python/followers","following_url":"https://api.github.com/users/python/following{/other_user}","gists_url":"https://api.github.com/users/python/gists{/gist_id}","starred_url":"https://api.github.com/users/python/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/python/subscriptions","organizations_url":"https://api.github.com/users/python/orgs","repos_url":"https://api.github.com/users/python/repos","events_url":"https://api.github.com/users/python/events{/privacy}","received_events_url":"https://api.github.com/users/python/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/python/mypy","description":"Optional static typing for Python 2 and 3 (PEP484)","fork":false,"url":"https://api.github.com/repos/python/mypy","forks_url":"https://api.github.com/repos/python/mypy/forks","keys_url":"https://api.github.com/repos/python/mypy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/python/mypy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/python/mypy/teams","hooks_url":"https://api.github.com/repos/python/mypy/hooks","issue_events_url":"https://api.github.com/repos/python/mypy/issues/events{/number}","events_url":"https://api.github.com/repos/python/mypy/events","assignees_url":"https://api.github.com/repos/python/mypy/assignees{/user}","branches_url":"https://api.github.com/repos/python/mypy/branches{/branch}","tags_url":"https://api.github.com/repos/python/mypy/tags","blobs_url":"https://api.github.com/repos/python/mypy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/python/mypy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/python/mypy/git/refs{/sha}","trees_url":"https://api.github.com/repos/python/mypy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/python/mypy/statuses/{sha}","languages_url":"https://api.github.com/repos/python/mypy/languages","stargazers_url":"https://api.github.com/repos/python/mypy/stargazers","contributors_url":"https://api.github.com/repos/python/mypy/contributors","subscribers_url":"https://api.github.com/repos/python/mypy/subscribers","subscription_url":"https://api.github.com/repos/python/mypy/subscription","commits_url":"https://api.github.com/repos/python/mypy/commits{/sha}","git_commits_url":"https://api.github.com/repos/python/mypy/git/commits{/sha}","comments_url":"https://api.github.com/repos/python/mypy/comments{/number}","issue_comment_url":"https://api.github.com/repos/python/mypy/issues/comments{/number}","contents_url":"https://api.github.com/repos/python/mypy/contents/{+path}","compare_url":"https://api.github.com/repos/python/mypy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/python/mypy/merges","archive_url":"https://api.github.com/repos/python/mypy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/python/mypy/downloads","issues_url":"https://api.github.com/repos/python/mypy/issues{/number}","pulls_url":"https://api.github.com/repos/python/mypy/pulls{/number}","milestones_url":"https://api.github.com/repos/python/mypy/milestones{/number}","notifications_url":"https://api.github.com/repos/python/mypy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/python/mypy/labels{/name}","releases_url":"https://api.github.com/repos/python/mypy/releases{/id}","deployments_url":"https://api.github.com/repos/python/mypy/deployments","created_at":"2012-12-07T13:30:23Z","updated_at":"2016-11-11T09:40:45Z","pushed_at":"2016-11-11T14:31:41Z","git_url":"git://github.com/python/mypy.git","ssh_url":"git@github.com:python/mypy.git","clone_url":"https://github.com/python/mypy.git","svn_url":"https://github.com/python/mypy","homepage":"http://www.mypy-lang.org/","size":19933,"stargazers_count":1964,"watchers_count":1964,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":228,"mirror_url":null,"open_issues_count":509,"forks":228,"open_issues":509,"watchers":1964,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/python/mypy/pulls/2266"},"html":{"href":"https://github.com/python/mypy/pull/2266"},"issue":{"href":"https://api.github.com/repos/python/mypy/issues/2266"},"comments":{"href":"https://api.github.com/repos/python/mypy/issues/2266/comments"},"review_comments":{"href":"https://api.github.com/repos/python/mypy/pulls/2266/comments"},"review_comment":{"href":"https://api.github.com/repos/python/mypy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/python/mypy/pulls/2266/commits"},"statuses":{"href":"https://api.github.com/repos/python/mypy/statuses/9d5f770016c3e63e110990bc02ae06673b368bf9"}}}}
{"actor":{"display_login":"gvanrossum"}}