Skip to content

Add exception to descriptor protocol for MethodType().__func__. - #3484

Closed
carljm wants to merge 1 commit into
python:masterfrom
carljm:fix-method.__func__
Closed

Add exception to descriptor protocol for MethodType().__func__.#3484
carljm wants to merge 1 commit into
python:masterfrom
carljm:fix-method.__func__

Conversation

@carljm

@carljm carljm commented Jun 1, 2017

Copy link
Copy Markdown
Member

types.FunctionType is annotated as a descriptor returning types.MethodType. In general, this is correct: if you access a FunctionType as an attribute of some instance, you get back a MethodType.

But MethodType has an attribute __func__, which is a FunctionType. And this attribute does not obey the descriptor protocol; it really does give you a FunctionType, not a MethodType.

(I think in general MethodType, as a type implemented in C, doesn't obey the descriptor protocol: e.g. this is true of its __self__ attribute too, even if the self-object of a method is a descriptor, if you access mymethod.__self__ you get the object, not the return value of its __get__ method. But __self__ does not currently require special-casing here, because mypy just considers it to be of type object, which isn't a descriptor; mypy doesn't track the type of the actual __self__ of a given method.)

This pull request fixes mypy's type inference so it knows that mymethod.__func__ is a FunctionType, not a MethodType.

@carljm
carljm force-pushed the fix-method.__func__ branch from e4e3b7a to 4a38240 Compare June 2, 2017 00:50
@carljm

carljm commented Jun 2, 2017

Copy link
Copy Markdown
Member Author

This fix feels grossly specific and special-case-y, but I've so far not found a more general approach. I guess the ideal might be some way to annotate an attribute in a stub file as "does not respect the descriptor protocol". Very open to suggestions of better / more general ways to fix this.

@gvanrossum

Copy link
Copy Markdown
Member

I guess the descriptor protocol is invoked by the class' __getattribute__ method (for instance attributes), and only "user-defined" classes invoke it, but mypy doesn't understand this (it doesn't have any idea there's a difference between user-defined classes vs. classes implemented in C).

I'm still reluctant to admit such a blatant custom exception. Where did this cause a problem for you? Was there just one instance or are there a whole bunch?

@carljm

carljm commented Jun 2, 2017

Copy link
Copy Markdown
Member Author

I share your aversion to this special case.

We only ran into this once, in introspection code. It certainly is no problem to just cast in that case (it's what we're doing now). It just seemed that since a) mypy is clearly inferring the wrong type and b) it's fixable, it might still be preferable to have an ugly fix than continue to be wrong.

There are probably other cases of types implemented in C that bypass __getattribute__ that have attributes that are descriptor types, but this is the only one I've run into.

Does python/typeshed#1383 feel any better? It's not exactly beautiful, but at least it keeps the workaround in typeshed, with the specific type that has the issue, rather than as a special case in mypy.

@gvanrossum

Copy link
Copy Markdown
Member

Yeah, I am inclined to accept the hackery in typeshed, so closing this one.

@gvanrossum gvanrossum closed this Jun 2, 2017
gvanrossum pushed a commit to python/typeshed that referenced this pull request Jun 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants