@@ -45,62 +45,47 @@ Specification
45
45
=============
46
46
This PEP introduces a protocol that can be used by static and runtime type checkers to validate
47
47
the consistency between ``Annotated `` metadata and a given type.
48
- Objects that implement this protocol have a method named ``__supports_type__ ``
49
- that takes a single positional argument and returns `` bool ``::
48
+ Objects that implement this protocol have a property, attribute called ``__supports_type__ ``
49
+ that specifies whether the metadata is valid for a given type.
50
50
51
51
class Int64:
52
- def __supports_type__(self, obj: int) -> bool:
53
- return isinstance(obj, int)
52
+ __supports_type__: int
54
53
55
- The protocol being introduced would be defined as follows if it were to be defined in code form::
54
+ The attribute may also be marked as a ``ClassVar `` to avoid interaction with dataclasses:
55
+
56
+ from dataclasses import dataclass
57
+ from typing import ClassVar
56
58
57
- from typing import Protocol
58
-
59
- class SupportsType[T](Protocol):
60
- def __supports_type__(self, obj: T, /) -> bool:
61
- ...
59
+ @dataclass
60
+ class Gt:
61
+ value: int
62
+ __supports_type__: ClassVar[int]
62
63
63
64
When a static type checker encounters a type expression of the form ``Annotated[T, M1, M2, ...] ``,
64
65
it should enforce that for each metadata element in ``M1, M2, ... ``, one of the following is true:
65
66
66
67
* The metadata element evaluates to an object that does not have a ``__supports_type__ `` attribute; or
67
- * The metadata element evaluates to an object ``M `` that implements the ``SupportsType `` protocol;
68
- and, with ``T `` instantiated to a value ``v ``, a call to ``M.__supports_type__(v) `` type checks without errors;
69
- and that call does not evaluate to ``Literal[False] ``.
68
+ * The metadata element evaluates to an object ``M `` that has a the ``__supports_type__ `` attribute;
69
+ and, with ``T `` instantiated to a value ``v ``, ``v `` can be assigned to ``M.__supports_type__ `` without error.
70
70
71
- The body of the ``__supports_type__ `` method is not used to check the validity of the metadata
72
- and static type checkers can ignore it. However, tools that use the annotation at
73
- runtime may call the method to check that a particular value is valid.
74
-
75
- For example, to support a generic ``Gt `` metadata, one might write::
71
+ To support a generic ``Gt `` metadata, one might write::
76
72
77
73
from typing import Protocol
78
74
79
75
class SupportsGt[T](Protocol):
80
76
def __gt__(self, __other: T) -> bool:
81
77
...
82
-
78
+
83
79
class Gt[T]:
80
+ __supports_type__: ClassVar[SupportsGt[T]]
81
+
84
82
def __init__(self, value: T) -> None:
85
83
self.value = value
86
84
87
- def __supports_type__(self, obj: SupportsGt[T], /) -> bool:
88
- return obj > self.value
89
-
90
85
x1: Annotated[int, Gt(0)] = 1 # OK
91
86
x2: Annotated[str, Gt(0)] = 0 # type checker error: str is not assignable to SupportsGt[int]
92
87
x3: Annotated[int, Gt(1)] = 0 # OK for static type checkers; runtime type checkers may flag this
93
88
94
- Implementations may be generic and may use overloads that return ``Literal[True] `` or ``Literal[False] ``
95
- to indicate if the metadata is valid for the given type.
96
-
97
- Implementations may raise a NotImplementedError if they cannot determine if the metadata is valid for the given type.
98
- Tools calling ``__supports_type__ `` at runtime should catch this exception and treat it as if ``__supports_type__ ``
99
- was not present; they should not take this as an indication that the metadata is invalid for the type.
100
-
101
- Tools that use the metadata at runtime may choose to ignore the implementation of ``__supports_type__ ``; this PEP does not
102
- specify how the method should be used at runtime, only that it may be available for use.
103
-
104
89
Backwards Compatibility
105
90
=======================
106
91
@@ -150,10 +135,19 @@ does not generally use marker base classes. In addition, it provides less flexib
150
135
the current proposal: it would not allow overloads, and it would require metadata objects
151
136
to add a new base class, which may make their runtime implementation more complex.
152
137
138
+ Using a method instead of an attribute for ``__supports_type__ ``
139
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140
+
141
+ We considered using a method instead of an attribute for the protocol, so that this method can be used
142
+ at runtime to check the validity of the metadata and to support overloads or returning boolean literals.
143
+ However using a method adds boilerplate to the implementation and the value of the runtime use cases or
144
+ more complex scenarios involving overloads and returning boolean literals was not clear.
145
+
153
146
Acknowledgments
154
147
===============
155
148
156
- We thank Eric Traut for suggesting the idea of using a protocol.
149
+ We thank Eric Traut for suggesting the idea of using a protocol and implementing provisional support in Pyright.
150
+ Thank you to Jelle Zijlstra for sponsoring this PEP.
157
151
158
152
Copyright
159
153
=========
0 commit comments