Skip to content

Commit ad4cbc4

Browse files
authored
Writing Stubs: Add "Overloads and Flags" section (#1894)
1 parent 69ddc23 commit ad4cbc4

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

docs/guides/writing_stubs.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,64 @@ common mistakes like unintentionally passing in ``None``.
405405

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

408+
Common Patterns
409+
===============
410+
411+
.. _stub-patterns:
412+
413+
This section documents common patterns that are useful in stub files.
414+
415+
Overloads and Flags
416+
-------------------
417+
418+
.. _overloads-and-flags:
419+
420+
Sometimes a function or method has a flag argument that changes the return type
421+
or other accepted argument types. For example, take the following function::
422+
423+
def open(name: str, mode: Literal["r", "w"] = "r") -> Reader | Writer:
424+
...
425+
426+
We can express this case easily with two overloads::
427+
428+
@overload
429+
def open(name: str, mode: Literal["r"] = "r") -> Reader: ...
430+
@overload
431+
def open(name: str, mode: Literal["w"]) -> Writer: ...
432+
433+
The first overload is picked when the mode is ``"r"`` or not given, and the
434+
second overload is picked when the mode is ``"w"``. But what if the first
435+
argument is optional?
436+
437+
::
438+
439+
def open(name: str | None = None, mode: Literal["r", "w"] = "r") -> Reader | Writer:
440+
...
441+
442+
Ideally we would be able to use the following overloads::
443+
444+
@overload
445+
def open(name: str | None = None, mode: Literal["r"] = "r") -> Reader: ...
446+
@overload
447+
def open(name: str | None = None, mode: Literal["w"]) -> Writer: ...
448+
449+
And while the first overload is fine, the second is a syntax error in Python,
450+
because non-default arguments cannot follow default arguments. To work around
451+
this, we need an extra overload::
452+
453+
@overload
454+
def open(name: str | None = None, mode: Literal["r"] = "r") -> Reader: ...
455+
@overload
456+
def open(name: str | None, mode: Literal["w"]) -> Writer: ...
457+
@overload
458+
def open(*, mode: Literal["w"]) -> Writer: ...
459+
460+
As before, the first overload is picked when the mode is ``"r"`` or not given.
461+
Otherwise, the second overload is used when ``open`` is called with an explicit
462+
``name``, e.g. ``open("file.txt", "w")`` or ``open(None, "w")``. The third
463+
overload is used when ``open`` is called without a name , e.g.
464+
``open(mode="w")``.
465+
408466
Style Guide
409467
===========
410468

0 commit comments

Comments
 (0)