Wemake-python-styleguide: Enforce consistent `return` statements

Created on 3 Dec 2018  路  14Comments  路  Source: wemake-services/wemake-python-styleguide

Rule request

Question

There are three ways to return None in python:

  1. return
  2. return None
  3. function without a return

We need to ask developers what do they think about this inconsistency.
My opinion is that we should only keep 2 and possibly 3.

Why keeping 2? It is the most explicit notation out of the two options.

Why possibly 3? In my ideal world all functions should return something. Ideally in one place.
Yearly returns are also fine, but in this case all branches of this function must return something explicitly.

Thesis

After several hours of thinking about this problem and asking opinions of other developers, this is what I came to:

  1. If there are no returns in a functions - that's fine
  2. If not all code paths return - that's fine
  3. If all return in a functions uses short form, that's fine
  4. If any return in a function uses explicit form return something, it means that all returns must be explicit: return something

Related: https://eslint.org/docs/rules/consistent-return

Reasoning

This is done for consistency reasons.

help wanted starter rule request

Most helpful comment

Better don't count on me here. But I'm still watching the changes to catch the wave.

All 14 comments

Examples

Correct

def test(arg: int) -> int:
    if arg > 0:
        return arg
    return -arg
def test(arg: int) -> None:
    if arg > 0:
        return
    if arg < 10:
        return
    some_call(arg)
def test(arg: int) -> None:
    if arg > 0:
        return
    assert arg < 10  # no-return

Wrong

def test(arg: int) -> None:
    if arg > 0:
        return
    return None
def test(arg: int) -> None:
    if arg > 0:
        return None
    return
def test(arg: int) -> None:
    if arg > 0:
        return arg
    assert arg < 10  # no-return
def test(arg: int) -> None:
    if arg > 0:
        return arg
    assert arg < 10  # no-return

Is that correct?

@kxepal You are right. This example is not correct. I have updated the description.

:beer:
However, examples two and three quite simiar. May be improve second one with return None in both of cases?

No, that's redundant. When using early return pattern (all returns are used without any arguments) it is fine not to have an extra return in the end.

@kxepal maybe you want to help me with this one? I would appreciate your help!

Ok, after some initial attempts to handle this I have found the right explanation for this feature. It consists of several independent rules.

Consistent return values

We only check that if any return contains a value - all returns must contain a value. We do not check types or that all path returns. Only the value.

Last line extra return

It is possible to finish your function with a return statement. Like so:

def some():
      ...
      return

Or

def some():
      ...
      return some_value_or_None

The thing is: just return in the last line is not required at all. It is more readable not to write it.
But having return some_value_or_None is always possible.

@sobolevn
Actually, I'm not sure how to start with all these asts. Could you recommend any good example around?

Thanks! I'm going to grab some tea for this.

@kxepal how's your progress? Should I take this over or not?

Better don't count on me here. But I'm still watching the changes to catch the wave.

Wow, never saw that one.

@kxepal I guess consistent values rule is also the case with yield.

def function():
      if some:
           yield
     yield None
     yield 1

I see no difference with return in how values should be defined.

Was this page helpful?
0 / 5 - 0 ratings