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

Writing Stubs: Add "Overloads and Flags" section #1894

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions docs/guides/writing_stubs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,64 @@ common mistakes like unintentionally passing in ``None``.

If in doubt, consider asking the library maintainers about their intent.

Common Patterns
===============

.. _stub-patterns:

This section documents common patterns that are useful in stub files.

Overloads and Flags
-------------------

.. _overloads-and-flags:

Sometimes a function or method has a flag argument that changes the return type
or other accepted argument types. For example, take the following function::

def open(name: str, mode: Literal["r", "w"] = "r") -> Reader | Writer:
...

We can express this case easily with two overloads::

@overload
def open(name: str, mode: Literal["r"] = "r") -> Reader: ...
@overload
def open(name: str, mode: Literal["w"]) -> Writer: ...

The first overload is picked when the mode is ``"r"`` or not given, and the
second overload is picked when the mode is ``"w"``. But what if the first
argument is optional?

::

def open(name: str | None = None, mode: Literal["r", "w"] = "r") -> Reader | Writer:
...

Ideally we would be able to use the following overloads::

@overload
def open(name: str | None = None, mode: Literal["r"] = "r") -> Reader: ...
@overload
def open(name: str | None = None, mode: Literal["w"]) -> Writer: ...

And while the first overload is fine, the second is a syntax error in Python,
because non-default arguments cannot follow default arguments. To work around
this, we need an extra overload::

@overload
def open(name: str | None = None, mode: Literal["r"] = "r") -> Reader: ...
@overload
def open(name: str | None, mode: Literal["w"]) -> Writer: ...
@overload
def open(*, mode: Literal["w"]) -> Writer: ...

As before, the first overload is picked when the mode is ``"r"`` or not given.
Otherwise, the second overload is used when ``open`` is called with an explicit
``name``, e.g. ``open("file.txt", "w")`` or ``open(None, "w")``. The third
overload is used when ``open`` is called without a name , e.g.
``open(mode="w")``.

Style Guide
===========

Expand Down
Loading