You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Proposal: treat "obvious" return type as annotated #4409
f obviously (without any deep analysis) returns str, and g returns A. Why not use this information? This pattern is very common, and taking advantage of it can help in precise checking and remove clutter (for example -> Union[Tuple[int, int, str], Tuple[int, str, bool]]).
I propose treating calls to functions whose return expressions consists solely of literals and constructor calls as if they were declared with the returned type (join or union of the return types).
Well, I always felt that the main reason for this was to allow mypy to be used on already-existing codebases, so the unannotated functions don't need to be type-safe (yet). This would change that behavior.
@kirbyfan64 I think I agree with what you are saying, but I also think there isn't a reason something like this couldn't be behind a flag, I've heard from several people that this is a significant pain point, as people don't want to have to do a lot of work to annotate obvious functions, so big +1 from me.
Reacted by Avasam, Eternal Sorrow, Richard Ebeling and Carl Walsh
@kirbyfan64 the proposal does not require unannotated functions to be type-safe. It simply allows annotated functions that use them to be slightly more type safe (peek into the immediate return expressions, if there is one).
Either a literal or a direct constructor call on a globally defined class. (Part of the idea is that the return statements are also the documentation).
Expression atoms would be straightforward enough (not certain about identifiers however). At some point this could probably be expanded to entire expressions I believe (down the road).
Yes, that could be a nice path. Atoms, then (recursively) tuples/lists, then simple names. My hope is this could be done as part of semantic analysis, just like the analysis of the annotations themselves.
I think it will be as safe as any other analysis performed by mypy. The first example is not a call to a global constructor; the second example defeats any other analysis.
I guess the discussion can be separated to two orthogonal questions:
What should be considered as "obvious" return type
How this mechanism is enabled: a flag for unannotated functions, a flag for partially-annotated functions, using an Infer annotation, or some other means
Note there is an old proposal python/typing#276 to make this explicit. During the discussion it became clear that inferring types for arguments can be very hard, but for return types it is probably OK.
Recently, additional interest in this appeared in the context of data classes (PEP 557), where annotations are syntactically required, but people may be lazy to write exact types, so that this could be allowed:
@dataclassclassC:
x: ... =5# equivalent to 'x = 5' (without an annotation) for a type checker
while for function this will work as
deffunc() -> ...:
return5reveal_type(func()) # Revealed type is 'builtins.int'
An additional argument in favour of ellipsis is that libraries in numeric stack use ellipsis for inferred number of dimensions.
One problem with annotations is that they mean that the function will be checked. It also does not completely address the issue of clutter and laziness.
I agree that if this is the direction, then the ellipsis idea is nice. But two minor points should be noted: its meaning is unrelated to Tuple[int, ...], and ... everywhere might make the code look bad.
One problem with annotations is that they mean that the function will be checked.
It is maybe a matter of taste, but I think it is rather a plus, since I like to keep the current separation between annotated and non-annotated functions.
@kstauffer That doesn't sound like a useful solution at all. It disables a whole bunch of warnings for all affected code, rather than one specific warning for just the functions where it is necessary. All it will lead to is other legitimate warnings being missed.
(Emphasis added.) Did you actually try it? It disables precisely the warning you don't care about and only in the files where you don't care about it (your test files). My config was just an example. You can tailor yours to your needs. https://mypy.readthedocs.io/en/stable/config_file.html
TypeScript's inference is really great in this regard, with strict mode enabled you don't have to annotated the return types of functions. I would love some level of support for this in mypy.
Reacted by DevilXD, Mon, Matthew Gamble, Peter Grman, ikokostya, Haowei Wang, Carlos Martin, David Bushong, Paul Garner, Neil Girdhar and 4 more
It may be obvious to most humans that f should be marked as returning str, but it would be more precise to say it returns Literal["hello"]. Maybe a slightly more complex example could be said to return an enum of possible string literals rather than any arbitrary str. It is also not incorrect to say that f returns Any or object. Automatically inferring the return type could be problematic when the developer intentions are unclear.
Reacted by Коренберг Марк, Raymond Gauthier and David Handy
Having to write -> None on every function that can only return None because it has no explicit return statements is quite annoying.
In general, if MyPy can infer the return type accurately, then it should. There is no benefit to not doing so.
It doesn't have to report the error, if not in --strict mode, but there seem no reason not to use the information that MyPy already knows.
For example:
defx():
return1defy() ->str:
return"hello "+x()
The above code is clearly wrong, yet without --strict, x() has the type Any and "hello " + x() isn't checked.
With --strict, MyPy complains that x() is not annotated, which is a distraction from the actual error.
Reacted by Matthew Gamble, herr kaste and wyattscarpenter
In general, if MyPy can infer the return type accurately, then it should. There is no benefit to not doing so.
There probably is a benefit: performance. I'd assume inferring the return types where missing requires a bit of extra work, because currently mypy can just skip checking all returns and their types, which should be faster.
But nonetheless it would be a very nice feature. If performance is a factor it could simply be made an opt-in feature.
Ok I think I am going to sit down and commit time to work on an implementation of this. It's a prerequisite to #6646 IMO, and I think fixing that is very important.
I think I will try an implementation with the full inference engine, but I also want to try a more minimal approach, as I think it is less risky.
Reacted by herr kaste, mo, Thomas M Kehrenberg, ryanc-bs and Avasam
I was gonna ask for some sort of no-untyped-def check (disallow_incomplete_defs or disallow_untyped_defs) that only raises for methods that mypy won't analyze (ie: 0 non-self parameters w/ missing return type, or missing parameter annotations).
But this request would alleviate some of the pain with disallow_incomplete/untyped_defs and reduce a lot of Function is missing a return type annotation
I think one potential problem that might come up with adding a configuration option for turning this on is that this changes how a given type signature is interpreted by mypy, unlike most of the other configuration options, so if a library you were using turned that option on while you had it off or you turned it on while the library turned it off then mypy would do something that wasn't intended. In the former case, mypy would silently infer an Any return type if the library authors were relying on the return type being inferred, while in the latter case mypy would, if I'm understanding the proposal correctly, start emitting errors relating to that inferred return type being used incorrectly, even if the library authors intentionally avoided annotating that since they were relying on it getting inferred as Any.
In my experience, I've definitely run into errors similar to the latter case with pyright, since (if I recall correctly) I usually have reasonably strict settings turned on while I sometimes use libraries without type annotations, and libraries that have complicated return types tend to result in pyright inferring a return type that leads to a false positive error. To some extent that's an understandable design decision since someone turning on stricter settings might prefer false positives over false negatives, but I think it's a risk to keep in mind here.
As far as I can tell, the only precedent for a config option that changes how types are interpreted by mypy is implicit_optional, and I think the historical context behind that makes it reasonably different than this case, since if you and a library disagree on whether to set that flag, there is a "right" option, while in the case of this option, libraries might be expected to support both it being set and unset, which would end up with them getting the worst of both worlds for their external API at least (you'd need to add return types for functions with obvious return types to make sure mypy/other type checkers inferred them correctly, but you'd also probably want to annotate non-obvious return types with Any explicitly so that mypy didn't infer the wrong thing).
This proposal does not require the full inference of the return type. Only the obvious one in simple cases, like if a function does not have a return statement, infer it as -> None, or if it has only one return statement, that's the return type of the function.
@ikokostya while this function is similar to the one I have described at the beginning of this issue, I am against inferring its type. This feature should be about (a) performing zero computation and (b) having the return expression itself serve as an explicit annotation.
Activity
refi64 commentedon Dec 25, 2017
Well, I always felt that the main reason for this was to allow mypy to be used on already-existing codebases, so the unannotated functions don't need to be type-safe (yet). This would change that behavior.
emmatyping commentedon Dec 25, 2017
@kirbyfan64 I think I agree with what you are saying, but I also think there isn't a reason something like this couldn't be behind a flag, I've heard from several people that this is a significant pain point, as people don't want to have to do a lot of work to annotate obvious functions, so big +1 from me.
elazarg commentedon Dec 25, 2017
@kirbyfan64 the proposal does not require unannotated functions to be type-safe. It simply allows annotated functions that use them to be slightly more type safe (peek into the immediate return expressions, if there is one).
elazarg commentedon Dec 25, 2017
To elaborate:
gvanrossum commentedon Dec 25, 2017
elazarg commentedon Dec 25, 2017
Either a literal or a direct constructor call on a globally defined class. (Part of the idea is that the return statements are also the documentation).
emmatyping commentedon Dec 25, 2017
Expression atoms would be straightforward enough (not certain about identifiers however). At some point this could probably be expanded to entire expressions I believe (down the road).
elazarg commentedon Dec 25, 2017
Yes, that could be a nice path. Atoms, then (recursively) tuples/lists, then simple names. My hope is this could be done as part of semantic analysis, just like the analysis of the annotations themselves.
refi64 commentedon Dec 25, 2017
Could a director constructor call really be safe though? e.g.:
Or even:
Of course, this isn't ideal, but it happens in unannotated code.
elazarg commentedon Dec 25, 2017
I think it will be as safe as any other analysis performed by mypy. The first example is not a call to a global constructor; the second example defeats any other analysis.
elazarg commentedon Dec 26, 2017
I guess the discussion can be separated to two orthogonal questions:
Inferannotation, or some other meansilevkivskyi commentedon Dec 26, 2017
Note there is an old proposal python/typing#276 to make this explicit. During the discussion it became clear that inferring types for arguments can be very hard, but for return types it is probably OK.
Recently, additional interest in this appeared in the context of data classes (PEP 557), where annotations are syntactically required, but people may be lazy to write exact types, so that this could be allowed:
while for function this will work as
An additional argument in favour of ellipsis is that libraries in numeric stack use ellipsis for inferred number of dimensions.
elazarg commentedon Dec 27, 2017
One problem with annotations is that they mean that the function will be checked. It also does not completely address the issue of clutter and laziness.
I agree that if this is the direction, then the ellipsis idea is nice. But two minor points should be noted: its meaning is unrelated to
Tuple[int, ...], and...everywhere might make the code look bad.ilevkivskyi commentedon Dec 28, 2017
@elazarg
It is maybe a matter of taste, but I think it is rather a plus, since I like to keep the current separation between annotated and non-annotated functions.
elazarg commentedon Dec 28, 2017
The idea is to not to make the human hands robotic, but to give them fingernails 😄
Behind a flag, the taste will be the user's.
22 remaining items
djmattyg007 commentedon Jan 20, 2022
@kstauffer That doesn't sound like a useful solution at all. It disables a whole bunch of warnings for all affected code, rather than one specific warning for just the functions where it is necessary. All it will lead to is other legitimate warnings being missed.
kstauffer commentedon Jan 20, 2022
@djmattyg007
(Emphasis added.) Did you actually try it? It disables precisely the warning you don't care about and only in the files where you don't care about it (your test files). My config was just an example. You can tailor yours to your needs. https://mypy.readthedocs.io/en/stable/config_file.html
djmattyg007 commentedon Jan 23, 2022
I don't want to disable those settings on a per-file basis. I want to disable them on a per-function basis.
michaeloliverx commentedon Jul 8, 2022
TypeScript's inference is really great in this regard, with strict mode enabled you don't have to annotated the return types of functions. I would love some level of support for this in
mypy.ryanc-bs commentedon Nov 8, 2023
It may be obvious to most humans that f should be marked as returning
str, but it would be more precise to say it returnsLiteral["hello"]. Maybe a slightly more complex example could be said to return an enum of possible string literals rather than any arbitrarystr. It is also not incorrect to say thatfreturnsAnyorobject. Automatically inferring the return type could be problematic when the developer intentions are unclear.ikokostya commentedon Nov 8, 2023
@ryanc-bs
The following definitions can be considered as equal:
mypyalready infers type for local variable, and I don't see why rule for inferring return type should be different. For comparison, in both casespyrightinfers return type offasLiteral["hello"](see https://microsoft.github.io/pyright/#/type-inference?id=return-type-inference and https://microsoft.github.io/pyright/#/type-inference?id=literals).markshannon commentedon Dec 1, 2023
Having to write
-> Noneon every function that can only returnNonebecause it has no explicitreturnstatements is quite annoying.In general, if MyPy can infer the return type accurately, then it should. There is no benefit to not doing so.
It doesn't have to report the error, if not in
--strictmode, but there seem no reason not to use the information that MyPy already knows.For example:
The above code is clearly wrong, yet without
--strict,x()has the typeAnyand"hello " + x()isn't checked.With
--strict, MyPy complains thatx()is not annotated, which is a distraction from the actual error.bluenote10 commentedon Dec 1, 2023
There probably is a benefit: performance. I'd assume inferring the return types where missing requires a bit of extra work, because currently mypy can just skip checking all returns and their types, which should be faster.
But nonetheless it would be a very nice feature. If performance is a factor it could simply be made an opt-in feature.
emmatyping commentedon Apr 29, 2024
Ok I think I am going to sit down and commit time to work on an implementation of this. It's a prerequisite to #6646 IMO, and I think fixing that is very important.
I think I will try an implementation with the full inference engine, but I also want to try a more minimal approach, as I think it is less risky.
Avasam commentedon May 23, 2024
I was gonna ask for some sort of
no-untyped-defcheck (disallow_incomplete_defsordisallow_untyped_defs) that only raises for methods that mypy won't analyze (ie: 0 non-self parameters w/ missing return type, or missing parameter annotations).But this request would alleviate some of the pain with
disallow_incomplete/untyped_defsand reduce a lot ofFunction is missing a return type annotationpranavrajpal commentedon Jul 8, 2024
I think one potential problem that might come up with adding a configuration option for turning this on is that this changes how a given type signature is interpreted by mypy, unlike most of the other configuration options, so if a library you were using turned that option on while you had it off or you turned it on while the library turned it off then mypy would do something that wasn't intended. In the former case, mypy would silently infer an
Anyreturn type if the library authors were relying on the return type being inferred, while in the latter case mypy would, if I'm understanding the proposal correctly, start emitting errors relating to that inferred return type being used incorrectly, even if the library authors intentionally avoided annotating that since they were relying on it getting inferred asAny.In my experience, I've definitely run into errors similar to the latter case with pyright, since (if I recall correctly) I usually have reasonably strict settings turned on while I sometimes use libraries without type annotations, and libraries that have complicated return types tend to result in pyright inferring a return type that leads to a false positive error. To some extent that's an understandable design decision since someone turning on stricter settings might prefer false positives over false negatives, but I think it's a risk to keep in mind here.
As far as I can tell, the only precedent for a config option that changes how types are interpreted by mypy is
implicit_optional, and I think the historical context behind that makes it reasonably different than this case, since if you and a library disagree on whether to set that flag, there is a "right" option, while in the case of this option, libraries might be expected to support both it being set and unset, which would end up with them getting the worst of both worlds for their external API at least (you'd need to add return types for functions with obvious return types to make sure mypy/other type checkers inferred them correctly, but you'd also probably want to annotate non-obvious return types withAnyexplicitly so that mypy didn't infer the wrong thing).eternal-sorrow commentedon Jul 8, 2024
This proposal does not require the full inference of the return type. Only the obvious one in simple cases, like if a function does not have a return statement, infer it as
-> None, or if it has only one return statement, that's the return type of the function.ikokostya commentedon Jul 8, 2024
@eternal-sorrow
It looks like artificial limitation. For example, in the following example
return type is
str | bool | None. It seems fairly obvious.PyrightandTypeScriptcan handle such cases.elazarg commentedon Jul 9, 2024
@ikokostya while this function is similar to the one I have described at the beginning of this issue, I am against inferring its type. This feature should be about (a) performing zero computation and (b) having the return expression itself serve as an explicit annotation.