Skip to content
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

(🎁) Flag to make default return type None instead of Any #9413

Open
KotlinIsland opened this issue Sep 4, 2020 · 26 comments
Open

(🎁) Flag to make default return type None instead of Any #9413

KotlinIsland opened this issue Sep 4, 2020 · 26 comments

Comments

@KotlinIsland
Copy link
Contributor

KotlinIsland commented Sep 4, 2020

🚀 Feature

Most other languages(Kotlin, Rust, Swift etc) have a void(or equivalent) return type by default. Can an option be added to have an implicit None return type.

def foo():  # implicit None
  print("hi")

🆕 Update

Basedmypy 1.3 supports this functionality with the --default-return flag.

@gvanrossum
Copy link
Member

Already exists: ‘ --check-untyped-defs’.

@KotlinIsland
Copy link
Contributor Author

@gvanrossum I don't think you understand my feature request, in Kotlin

fun foo() {
    return
}

is a valid function, that returns an implicit Unit(essentially Void) and is fully typed.

Swift and Rust(and others I assume) also have this feature and I think it's a good one.

The --check-untyped-defs flag isn't what I'm asking for.

Thanks for the quick response.

@KotlinIsland
Copy link
Contributor Author

Go also supports this.

@KotlinIsland
Copy link
Contributor Author

Currently returns Any

def foo():
  return

@gvanrossum
Copy link
Member

Whoops.

@gvanrossum gvanrossum reopened this Sep 4, 2020
@JukkaL
Copy link
Collaborator

JukkaL commented Sep 4, 2020

I think that this could be a useful feature, but perhaps only when --check-untyped-defs or --disallow-untyped-defs is enabled, as otherwise def foo(): would be ambiguous -- it's unclear if we should type check the body.

There is a problem when using --check-untyped-defs, as we should only give def foo(): an implicit None return type if there is no return statement with an explicit value in the body. This would be a bit dangerous however, as any return statement with a value would turn the return type into Any. So maybe this should only be allowed with --disallow-untyped-defs?

Unit tests, in particular, could benefit from this, since there tend to be a lot of functions like def test_foo(...) -> None.

Additionally, maybe an explicit None return type should be an error in this mode, as otherwise it could look confusing (or at least inconsistent). We could give this error a dedicated error code so that it would be possible to disable the message, however.

@KotlinIsland
Copy link
Contributor Author

Maybe name the flag --default-none-return and if enabled then def foo(): would then be considered a typed def and should be checked regardless of whether --check-untyped-defs is selected.

I would think an explicit none return type in this mode should be a warning.

@eternal-sorrow
Copy link

I think that this could be a useful feature, but perhaps only when --check-untyped-defs or --disallow-untyped-defs

Or it would rather be an alternative to these 2 options.

This would be a bit dangerous however, as any return statement with a value would turn the return type into Any.

This case should be considered an error.

@gvanrossum
Copy link
Member

I feel this feature request is misguided. It creates an incompatible variant language. Just write ‘-> None’ already!

@eternal-sorrow
Copy link

eternal-sorrow commented Jan 27, 2022

Just write ‘-> None’ already!

I don't know. Maybe I'm just too spoiled by the fact that return None, return and implicit return do exactly the same thing, but it feels just wrong to add -> None to a function that doesn't actually have a return None statement. For me it feels natural that functions that don't have a return type annotation should be considered as retuning None, because that's what happens if a function doesn't have a return statement.

@JelleZijlstra
Copy link
Member

This feels like a special case of #4409.

@KotlinIsland
Copy link
Contributor Author

I implemented this in my fork, haven't made a release yet though.

@djmattyg007
Copy link

It feels nonsensical that mypy is incapable of inferring this itself. I've seen far too many developers come from Typescript (where its inference capabilities are incredible) to Python and straight up complain about why they have to manually annotate the obvious.

@KotlinIsland
Copy link
Contributor Author

KotlinIsland commented Apr 27, 2022

Basedmypy 1.3 supports this functionality with the --default-return flag.

@beauxq
Copy link

beauxq commented May 2, 2022

It feels nonsensical that mypy is incapable of inferring this itself. I've seen far too many developers come from Typescript (where its inference capabilities are incredible) to Python and straight up complain about why they have to manually annotate the obvious.

I would not want it to be inferred. I would like this to give me an error:

def foo():
    return 3

If there's no return type annotation, that means return None, so I'm not allowed to return int.

@beauxq
Copy link

beauxq commented May 2, 2022

It creates an incompatible variant language.

I don't understand this. Can you explain?

@djmattyg007
Copy link

I would not want it to be inferred. I would like this to give me an error:

but why?

@eternal-sorrow
Copy link

but why?

There is a perfect explanation of that in the post you replied to.

@diddi-
Copy link

diddi- commented Oct 4, 2022

I would not want it to be inferred. I would like this to give me an error:

but why?

Because it may not be obvious to a human reader what the expected return type is. It's one thing to have mypy infer the return type by looking at what's being returned, it's a completely different thing for a human to do the same. In the example of returning a single integer that's easy enough of course, but chained function calls or objects are less obvious.

By being explicit with what you want to return you are also reducing risk for bugs and it makes refactoring and team work much easier. And yes I do consider not writing a return type to be just as explicit as writing -> None. It makes sense that when you look at a function that doesn't have a return type it's because it doesn't return anything.

I am very much in favor of having a flag that enables this behavior as I find myself writing -> None a lot in unit tests etc. which will never return anything. It just feels redundant to have it there.

@KotlinIsland KotlinIsland changed the title Flag to make default return type None instead of Any Flag to make default return type None instead of Any Oct 5, 2022
@AlexWaygood AlexWaygood added the topic-inference When to infer types or require explicit annotations label Nov 24, 2022
@berzi
Copy link

berzi commented Nov 24, 2022

I'll bring a counterproposal from my (closed as duplicate) issue #14180, which I'll copy here for convenience.

I seem to understand that all arguments against the proposal have to do with it potentially being unexpected as a default behaviour (and it would definitely be something of a breaking change for anyone who already adopts mypy and doesn't enable disallow_untyped_defs). My proposal includes the behaviour only behind a flag, which would make the feature opt-in and thus not a problem for current adopters anymore. Please see the full proposal below:

Feature

Behind a config flag like untyped_return_type_is_none = true, Mypy assumes that a function with no return type, e.g.:

def my_func(i_am_annotated: str):  # Note: no "-> int" annotation
    return 5

Returns None, instead of accepting any return type at face value.

Additionally, while untyped_return_type_is_none is true, disallow_untyped_defs allows functions to have no return annotation if their return type is None.

Pitch

Adoption for the disallow_untyped_defs is difficult, because it requires None to be explicitly annotated. This makes code more wordy in many places where the annotation adds zero value (think of all test functions/methods for extremely popular frameworks like pytest or unittest). This is especially overwhelming for maintainers of large codebases with many tests and existing functions.

Having the flag would give users an option to leave -> None functions unannotated.

Current behaviour would be unchanged if the new flag were not activated, but activating the flag would force users to annotate those functions that do return a value, providing a large but progressive step towards disallow_untyped_defs, reducing the essentially meaningless menial work of adding -> None everywhere.

@beauxq
Copy link

beauxq commented Nov 24, 2022

@AlexWaygood This is not about inference.
The return type would not be inferred.
With this option turned on, the return type for any function without a return type annotation would be None.
Nothing is inferred.

def foo():
    return 3

With this option turned on, this would be considered an annotated function, and it would report an error because I'm not allowed to return 3 when the return type is None.

@AlexWaygood AlexWaygood removed the topic-inference When to infer types or require explicit annotations label Nov 24, 2022
@MartinBernstorff
Copy link

MartinBernstorff commented Mar 9, 2023

Strongly in favor of this! The major blocker for out team in adopting disallow_untyped_defs is requiring -> None in plenty of functions where it is obvious.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Mar 9, 2023

Note you could use a complementary tool like https://github.com/JelleZijlstra/autotyping to automatically add -> None (edit from later: or ruff check . --select ANN2 --fix)

@eternal-sorrow
Copy link

@hauntsaninja Yes, but -> None is a visual garbage that makes function definitions longer (they are already long because of the arguments annotations). And it doesn't add anything in terms of code readability.

@MartinBernstorff
Copy link

MartinBernstorff commented Mar 9, 2023

As a workaround, ruff supports flake8-annotations which tests for this, and flake8-annotations has the requested setting!

[tool.ruff.flake8-annotations]
suppress-none-returning = true

@KotlinIsland KotlinIsland changed the title Flag to make default return type None instead of Any (🎁) Flag to make default return type None instead of Any Oct 12, 2023
@ronpsmith
Copy link

Came here looking for this feature - to default the return type to None if one wasn't specified, and give me an error if the function is returning something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests