Skip to content

strip-none-return

strip-none-return removes a written -> None from a function that returns nothing, so def configure() -> None: reads def configure():. An omitted return annotation already reads as a function that returns nothing, which leaves the explicit form as visual weight rather than information.

The rewrite is purely mechanical, running only where the return annotation is a bare None, with or without its own parentheses, and leaving a None nested inside a larger annotation (int | None, Callable[..., None]) and every parameter annotation as written. A declaration-only stub keeps its -> None too, because a body that is only ..., with or without a docstring ahead of it (an @overload arm, a Protocol method, an abstract method), is a placeholder whose -> None declares a type-checker contract rather than a redundant annotation. The companion signature-annotations rule enforces the other side of the convention, reporting where a parameter or a value-returning function lacks the annotation it owes.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueTurns the rule on or off.

The Canonical Case

configure and reset each declare -> None over a body that returns nothing. strip-none-return removes both annotations, so the headers read def configure(): and def reset(state):.

A function with no return annotation already reads as one that returns nothing, so the explicit -> None adds no information.

def configure():
    load_settings()


def reset(state):
    state.clear()
python

More Examples

configure declares its return annotation as -> (None), with None wrapped in parentheses. strip-none-return removes the arrow, the parentheses, and the None together, so the header reads def configure():, because a parenthesized None is the same bare annotation as -> None.

Service.__init__ and the module-level async def shutdown each declare -> None over a body that returns nothing. strip-none-return removes the annotation from both, so the headers read def __init__(self): and async def shutdown():, because a method and an async def are stripped the same way as a plain function.

No Change

lookup declares -> int | None and make_handler declares -> Callable[..., None]. strip-none-return leaves both annotations as written, because the rule removes only a return annotation that is a bare None, and a None nested inside a union or a Callable names a different type.

No Change

The first @overload arm of f declares -> None over a body of a single .... strip-none-return leaves that annotation in place, because the arm declares the return type the type checker reads for f(x: int), and removing it would leave that arm with no return type beside the second arm's -> str.

For per-statement opt-outs, the Suppression chapter covers the # prose: skip[strip-none-return] directive, which covers every line a wrapped statement spans.