-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
PEP 835: Address Discourse feedback from encukou and tjreedy #4998
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
Open
till-varoquaux
wants to merge
1
commit into
python:main
Choose a base branch
from
till-varoquaux:feature/update-pep835-links
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+47
−40
Open
Changes from all commits
Commits
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -229,45 +229,15 @@ Supported Left-Hand Operands | |
| ----------------------------- | ||
|
|
||
| The ``@`` operator is implemented by adding ``nb_matrix_multiply`` to the | ||
| metatype (``type``) and to several typing-related types. The operator is | ||
| metatype (``type``) and to several types used in type hinting (including | ||
| ``NoneType``). The operator is | ||
| supported for any left-hand operand that currently supports the ``|`` | ||
| operator for making a union. | ||
| operator for making a union (including ``type(None)``, which acts as the | ||
| canonical equivalent to ``None`` in type hints). | ||
|
|
||
| For all other left-hand operands, the operator returns ``NotImplemented``, | ||
| allowing normal ``__matmul__`` dispatch to proceed. | ||
|
|
||
| Parsing and Grammar | ||
| =================== | ||
|
|
||
| This proposal requires no changes to the Python grammar. Because ``@`` is | ||
| already a valid operator, it is natively parsed as a binary operation. The | ||
| shorthand is resolved during semantic analysis, entirely bypassing the need | ||
| to patch grammar files or update the parser. | ||
|
|
||
| How to Teach This | ||
| ================= | ||
|
|
||
| In Python, the ``@`` symbol already has an established association with | ||
| metadata through decorators. The annotation shorthand extends this | ||
| intuition to the type system: ``int @ Field(gt=0)`` reads as "``int``, | ||
| decorated with ``Field(gt=0)``." | ||
|
|
||
| For beginners, the key rule is simple: **in a type annotation, ``@`` means | ||
| "with this metadata."** The full ``Annotated[int, Field(gt=0)]`` syntax | ||
| remains available and is entirely equivalent for those who find it clearer. | ||
|
|
||
| For experienced developers, the precedence rules follow standard Python | ||
| operator precedence (``@`` binds tighter than ``|``), and chaining | ||
| ``T @ m1 @ m2`` flattens exactly as nested ``Annotated`` does. | ||
|
|
||
| Documentation and teaching materials should introduce the shorthand alongside | ||
| ``Annotated``, not as a replacement. The longhand form is still preferred in | ||
| contexts where multiple metadata items are passed as a group and chaining | ||
| would be unwieldy. | ||
|
|
||
| Backwards Compatibility | ||
| ======================= | ||
|
|
||
| Forward References and Deferred Evaluation | ||
| ------------------------------------------- | ||
|
|
||
|
|
@@ -277,8 +247,9 @@ module provides several formats for retrieving annotations: | |
| - ``Format.VALUE``: Fully evaluates the annotation. Raises ``NameError`` | ||
| if any name is unresolvable. | ||
| - ``Format.FORWARDREF``: Wraps unresolvable names in ``ForwardRef`` objects. | ||
| However, compound expressions using operators (``@``, ``|``) produce an | ||
| opaque ``ForwardRef`` string containing the entire expression. | ||
| However, when operators like ``@`` or ``|`` fail to evaluate because of an | ||
| unresolvable name, this format falls back to returning the entire expression | ||
| as an opaque string wrapped in a single ``ForwardRef`` object. | ||
| - ``Format.STRING``: Returns the raw source text with no evaluation. | ||
|
|
||
| For the ``@`` operator, ``Format.FORWARDREF`` is insufficient. Consider:: | ||
|
|
@@ -293,8 +264,8 @@ forward reference is resolved. This is a blocking issue for libraries like | |
| Pydantic and FastAPI, which inspect metadata at class-definition time. | ||
|
|
||
| This proposal introduces a new format, ``Format.FORWARDREF_STRUCTURAL``. | ||
| This format assumes typing semantics and evaluates compound type expressions | ||
| **structurally**. It always returns an ``AnnotatedType`` for ``@``, a union | ||
| This format assumes typing semantics and evaluates type expressions involving | ||
| operators (like ``@`` and ``|``) **structurally**. It always returns an ``AnnotatedType`` for ``@``, a union | ||
| for ``|``, and a ``GenericAlias`` for subscripting. When a name cannot be | ||
| resolved, only that name is wrapped in ``ForwardRef``; the surrounding | ||
| operators are still evaluated. The example above produces:: | ||
|
|
@@ -304,7 +275,10 @@ operators are still evaluated. The example above produces:: | |
| The metadata is immediately accessible. This format also resolves the | ||
| pre-existing issue with ``|`` unions, where ``"Foo" | int`` under | ||
| ``Format.FORWARDREF`` produces ``ForwardRef('Foo | int')`` instead of the | ||
| structural ``ForwardRef('Foo') | int``. | ||
| structural ``ForwardRef('Foo') | int``. Notably, if ``FORWARDREF_STRUCTURAL`` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems very speculative; there are lots of problems that would need to be solved before this can be entertained. |
||
| becomes the default evaluation strategy for type hints, it could eventually | ||
| reduce or eliminate the need for runtime type objects to implement | ||
| ``__matmul__``. | ||
|
|
||
| Interaction with PEP 563 | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
|
@@ -321,6 +295,38 @@ coarser than from :pep:`749` thunks. When a name is unresolvable, the | |
| rather than just a name. :pep:`749` provides a strictly better experience and | ||
| is the recommended path forward. | ||
|
|
||
| Parsing and Grammar | ||
| =================== | ||
|
|
||
| This proposal requires no changes to the Python grammar. Because ``@`` is | ||
| already a valid operator, it is natively parsed as a binary operation. The | ||
| shorthand is resolved during semantic analysis, entirely bypassing the need | ||
| to patch grammar files or update the parser. | ||
|
|
||
| How to Teach This | ||
| ================= | ||
|
|
||
| In Python, the ``@`` symbol already has an established association with | ||
| metadata through decorators. The annotation shorthand extends this | ||
| intuition to the type system: ``int @ Field(gt=0)`` reads as "``int``, | ||
| decorated with ``Field(gt=0)``." | ||
|
|
||
| For beginners, the key rule is simple: **in a type annotation, ``@`` means | ||
| "with this metadata."** The full ``Annotated[int, Field(gt=0)]`` syntax | ||
| remains available and is entirely equivalent for those who find it clearer. | ||
|
|
||
| For experienced developers, the precedence rules follow standard Python | ||
| operator precedence (``@`` binds tighter than ``|``), and chaining | ||
| ``T @ m1 @ m2`` flattens exactly as nested ``Annotated`` does. | ||
|
|
||
| Documentation and teaching materials should introduce the shorthand alongside | ||
| ``Annotated``, not as a replacement. The longhand form is still preferred in | ||
| contexts where multiple metadata items are passed as a group and chaining | ||
| would be unwieldy. | ||
|
|
||
| Backwards Compatibility | ||
| ======================= | ||
|
|
||
| Operator Overloading | ||
| -------------------- | ||
|
|
||
|
|
@@ -354,7 +360,8 @@ metaclass. | |
| The private ``typing._AnnotatedAlias`` class is retained as a deprecated | ||
| compatibility shim. Code using ``isinstance(x, typing._AnnotatedAlias)`` | ||
| will continue to work but emit a ``DeprecationWarning``. The shim is | ||
| scheduled for removal in Python 3.18. | ||
| scheduled for removal in Python 3.21 (following the standard 5-year deprecation | ||
| policy outlined in :pep:`387`). | ||
|
|
||
| Code that should be updated: | ||
|
|
||
|
|
||
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.
This is a questionable claim; some parts of typing.py turn None into NoneType but not all typing APIs do.
Also the mention of NoneType is a bit confusing. NoneType is a type like any other, so it inherits
__or__(currently) and__matmul__(if this PEP is accepted) fromtype. The more interesting question is what happens to None itself. Currently, it does not in fact support__or__;None | intworks, but that's through the latter's__ror__. Therefore, unions of None with a stringified type (None | "int") fail at runtime. You are proposing to make matmul work for None.