Skip to content

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

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

Closed
wants to merge 1 commit into from

Conversation

carljm
Copy link
Member

@carljm carljm commented Jun 1, 2017

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
Copy link
Member Author

carljm commented Jun 2, 2017

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
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
Copy link
Member Author

carljm commented Jun 2, 2017

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
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