Repository navigation
Add the typing spec #1517
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
Merged
Merged
Add the typing spec #1517
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d34b43f
Add the typing spec
JelleZijlstra fb22549
Don't need a separate conf.py
JelleZijlstra 471664a
Remove 2.7 compatibility note
JelleZijlstra ebcec76
Update sys.version_info example to a more recent version
JelleZijlstra cb87c36
frozen_default
JelleZijlstra 64bb1aa
@deprecated (PEP 702)
JelleZijlstra e4e86f0
Remove stray files
JelleZijlstra File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| _build/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Minimal makefile for Sphinx documentation | ||
| # | ||
|
|
||
| # You can set these variables from the command line, and also | ||
| # from the environment for the first two. | ||
| SPHINXOPTS ?= | ||
| SPHINXBUILD ?= sphinx-build | ||
| SOURCEDIR = . | ||
| BUILDDIR = _build | ||
|
|
||
| # Put it first so that "make" without argument is like "make help". | ||
| help: | ||
| @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||
|
|
||
| .PHONY: help Makefile | ||
|
|
||
| # Catch-all target: route all unknown targets to Sphinx using the new | ||
| # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). | ||
| %: Makefile | ||
| @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| Type aliases | ||
| ============ | ||
|
|
||
| (See :pep:`613` for the introduction of ``TypeAlias``, and | ||
| :pep:`695` for the ``type`` statement.) | ||
|
|
||
| Type aliases may be defined by simple variable assignments:: | ||
|
|
||
| Url = str | ||
|
|
||
| def retry(url: Url, retry_count: int) -> None: ... | ||
|
|
||
| Or by using ``typing.TypeAlias``:: | ||
|
|
||
| from typing import TypeAlias | ||
|
|
||
| Url: TypeAlias = str | ||
|
|
||
| def retry(url: Url, retry_count: int) -> None: ... | ||
|
|
||
| Or by using the ``type`` statement (Python 3.12 and higher):: | ||
|
|
||
| type Url = str | ||
|
|
||
| def retry(url: Url, retry_count: int) -> None: ... | ||
|
|
||
| Note that we recommend capitalizing alias names, since they represent | ||
| user-defined types, which (like user-defined classes) are typically | ||
| spelled that way. | ||
|
|
||
| Type aliases may be as complex as type hints in annotations -- | ||
| anything that is acceptable as a type hint is acceptable in a type | ||
| alias:: | ||
|
|
||
| from typing import TypeVar | ||
| from collections.abc import Iterable | ||
|
|
||
| T = TypeVar('T', bound=float) | ||
| Vector = Iterable[tuple[T, T]] | ||
|
|
||
| def inproduct(v: Vector[T]) -> T: | ||
| return sum(x*y for x, y in v) | ||
| def dilate(v: Vector[T], scale: T) -> Vector[T]: | ||
| return ((x * scale, y * scale) for x, y in v) | ||
| vec: Vector[float] = [] | ||
|
|
||
|
|
||
| This is equivalent to:: | ||
|
|
||
| from typing import TypeVar | ||
| from collections.abc import Iterable | ||
|
|
||
| T = TypeVar('T', bound=float) | ||
|
|
||
| def inproduct(v: Iterable[tuple[T, T]]) -> T: | ||
| return sum(x*y for x, y in v) | ||
| def dilate(v: Iterable[tuple[T, T]], scale: T) -> Iterable[tuple[T, T]]: | ||
| return ((x * scale, y * scale) for x, y in v) | ||
| vec: Iterable[tuple[float, float]] = [] | ||
|
|
||
| ``TypeAlias`` | ||
| ------------- | ||
|
|
||
| The explicit alias declaration syntax with ``TypeAlias`` clearly differentiates between the three | ||
| possible kinds of assignments: typed global expressions, untyped global | ||
| expressions, and type aliases. This avoids the existence of assignments that | ||
| break type checking when an annotation is added, and avoids classifying the | ||
| nature of the assignment based on the type of the value. | ||
|
|
||
| Implicit syntax (pre-existing): | ||
|
|
||
| :: | ||
|
|
||
| x = 1 # untyped global expression | ||
| x: int = 1 # typed global expression | ||
|
|
||
| x = int # type alias | ||
| x: type[int] = int # typed global expression | ||
|
|
||
|
|
||
| Explicit syntax: | ||
|
|
||
| :: | ||
|
|
||
| x = 1 # untyped global expression | ||
| x: int = 1 # typed global expression | ||
|
|
||
| x = int # untyped global expression (see note below) | ||
| x: type[int] = int # typed global expression | ||
|
|
||
| x: TypeAlias = int # type alias | ||
| x: TypeAlias = "MyClass" # type alias | ||
|
|
||
|
|
||
| Note: The examples above illustrate implicit and explicit alias declarations in | ||
| isolation. For the sake of backwards compatibility, type checkers should support | ||
| both simultaneously, meaning an untyped global expression ``x = int`` will | ||
| still be considered a valid type alias. | ||
|
|
||
| ``type`` statement | ||
| ------------------ | ||
|
|
||
| Type aliases may also be defined using the ``type`` statement (Python 3.12 and | ||
| higher). | ||
|
|
||
| The ``type`` statement allows the creation of explicitly generic | ||
| type aliases:: | ||
|
|
||
| type ListOrSet[T] = list[T] | set[T] | ||
|
|
||
| Type parameters declared as part of a generic type alias are valid only | ||
| when evaluating the right-hand side of the type alias. | ||
|
|
||
| As with ``typing.TypeAlias``, type checkers should restrict the right-hand | ||
| expression to expression forms that are allowed within type annotations. | ||
| The use of more complex expression forms (call expressions, ternary operators, | ||
| arithmetic operators, comparison operators, etc.) should be flagged as an | ||
| error. | ||
|
|
||
| Type alias expressions are not allowed to use traditional type variables (i.e. | ||
| those allocated with an explicit ``TypeVar`` constructor call). Type checkers | ||
| should generate an error in this case. | ||
|
|
||
| :: | ||
|
|
||
| T = TypeVar("T") | ||
| type MyList = list[T] # Type checker error: traditional type variable usage | ||
|
|
||
| ``NewType`` | ||
| ----------- | ||
|
|
||
| There are also situations where a programmer might want to avoid logical | ||
| errors by creating simple classes. For example:: | ||
|
|
||
| class UserId(int): | ||
| pass | ||
|
|
||
| def get_by_user_id(user_id: UserId): | ||
| ... | ||
|
|
||
| However, this approach introduces a runtime overhead. To avoid this, | ||
| ``typing.py`` provides a helper function ``NewType`` that creates | ||
| simple unique types with almost zero runtime overhead. For a static type | ||
| checker ``Derived = NewType('Derived', Base)`` is roughly equivalent | ||
| to a definition:: | ||
|
|
||
| class Derived(Base): | ||
| def __init__(self, _x: Base) -> None: | ||
| ... | ||
|
|
||
| While at runtime, ``NewType('Derived', Base)`` returns a dummy function | ||
| that simply returns its argument. Type checkers require explicit casts | ||
| from ``int`` where ``UserId`` is expected, while implicitly casting | ||
| from ``UserId`` where ``int`` is expected. Examples:: | ||
|
|
||
| UserId = NewType('UserId', int) | ||
|
|
||
| def name_by_id(user_id: UserId) -> str: | ||
| ... | ||
|
|
||
| UserId('user') # Fails type check | ||
|
|
||
| name_by_id(42) # Fails type check | ||
| name_by_id(UserId(42)) # OK | ||
|
|
||
| num = UserId(5) + 1 # type: int | ||
|
|
||
| ``NewType`` accepts exactly two arguments: a name for the new unique type, | ||
| and a base class. The latter should be a proper class (i.e., | ||
| not a type construct like ``Union``, etc.), or another unique type created | ||
| by calling ``NewType``. The function returned by ``NewType`` | ||
| accepts only one argument; this is equivalent to supporting only one | ||
| constructor accepting an instance of the base class (see above). Example:: | ||
|
|
||
| class PacketId: | ||
| def __init__(self, major: int, minor: int) -> None: | ||
| self._major = major | ||
| self._minor = minor | ||
|
|
||
| TcpPacketId = NewType('TcpPacketId', PacketId) | ||
|
|
||
| packet = PacketId(100, 100) | ||
| tcp_packet = TcpPacketId(packet) # OK | ||
|
|
||
| tcp_packet = TcpPacketId(127, 0) # Fails in type checker and at runtime | ||
|
|
||
| Both ``isinstance`` and ``issubclass``, as well as subclassing will fail | ||
| for ``NewType('Derived', Base)`` since function objects don't support | ||
| these operations. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this here? I went into this directory and typed
make venvbut it complained about a missingconf.py.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, probably a holdover from when it was in its own repo. I'll get rid of it. (You should
makein the parent directory, which includes this spec and the other typing docs.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I figured that out eventually. It's a bit weird how clicking on the Table of Contents header at the top of the sidebar takes you to the global ToC, not the Spec ToC, even though there is no "Next topic" linked from spec/historical.html, the last topic in Spec (so I would expect that the ToC link would go to the Spec ToC). But that's a very minor detail at this point, and probably some Sphinx oddity.