bpo-37045: PEP 591: Add final qualifiers to typing module (GH-13571)
The implementation is straightforward, it just mimics `ClassVar` (since the latter is also a name/access qualifier, not really a type). Also it is essentially copied from `typing_extensions`.
This commit is contained in:
parent
47dd2f9fd8
commit
f367242d10
|
@ -940,6 +940,31 @@ The module defines the following classes, functions and decorators:
|
|||
|
||||
See :pep:`484` for details and comparison with other typing semantics.
|
||||
|
||||
.. decorator:: final
|
||||
|
||||
A decorator to indicate to type checkers that the decorated method
|
||||
cannot be overridden, and the decorated class cannot be subclassed.
|
||||
For example::
|
||||
|
||||
class Base:
|
||||
@final
|
||||
def done(self) -> None:
|
||||
...
|
||||
class Sub(Base):
|
||||
def done(self) -> None: # Error reported by type checker
|
||||
...
|
||||
|
||||
@final
|
||||
class Leaf:
|
||||
...
|
||||
class Other(Leaf): # Error reported by type checker
|
||||
...
|
||||
|
||||
There is no runtime checking of these properties. See :pep:`591` for
|
||||
more details.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
.. decorator:: no_type_check
|
||||
|
||||
Decorator to indicate that annotations are not type hints.
|
||||
|
@ -1104,6 +1129,25 @@ The module defines the following classes, functions and decorators:
|
|||
|
||||
.. versionadded:: 3.5.3
|
||||
|
||||
.. data:: Final
|
||||
|
||||
A special typing construct to indicate to type checkers that a name
|
||||
cannot be re-assigned or overridden in a subclass. For example::
|
||||
|
||||
MAX_SIZE: Final = 9000
|
||||
MAX_SIZE += 1 # Error reported by type checker
|
||||
|
||||
class Connection:
|
||||
TIMEOUT: Final[int] = 10
|
||||
|
||||
class FastConnector(Connection):
|
||||
TIMEOUT = 1 # Error reported by type checker
|
||||
|
||||
There is no runtime checking of these properties. See :pep:`591` for
|
||||
more details.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
.. data:: AnyStr
|
||||
|
||||
``AnyStr`` is a type variable defined as
|
||||
|
|
|
@ -12,7 +12,7 @@ from typing import T, KT, VT # Not in __all__.
|
|||
from typing import Union, Optional
|
||||
from typing import Tuple, List, MutableMapping
|
||||
from typing import Callable
|
||||
from typing import Generic, ClassVar
|
||||
from typing import Generic, ClassVar, Final, final
|
||||
from typing import cast
|
||||
from typing import get_type_hints
|
||||
from typing import no_type_check, no_type_check_decorator
|
||||
|
@ -1438,6 +1438,53 @@ class ClassVarTests(BaseTestCase):
|
|||
issubclass(int, ClassVar)
|
||||
|
||||
|
||||
class FinalTests(BaseTestCase):
|
||||
|
||||
def test_basics(self):
|
||||
Final[int] # OK
|
||||
with self.assertRaises(TypeError):
|
||||
Final[1]
|
||||
with self.assertRaises(TypeError):
|
||||
Final[int, str]
|
||||
with self.assertRaises(TypeError):
|
||||
Final[int][str]
|
||||
with self.assertRaises(TypeError):
|
||||
Optional[Final[int]]
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual(repr(Final), 'typing.Final')
|
||||
cv = Final[int]
|
||||
self.assertEqual(repr(cv), 'typing.Final[int]')
|
||||
cv = Final[Employee]
|
||||
self.assertEqual(repr(cv), 'typing.Final[%s.Employee]' % __name__)
|
||||
|
||||
def test_cannot_subclass(self):
|
||||
with self.assertRaises(TypeError):
|
||||
class C(type(Final)):
|
||||
pass
|
||||
with self.assertRaises(TypeError):
|
||||
class C(type(Final[int])):
|
||||
pass
|
||||
|
||||
def test_cannot_init(self):
|
||||
with self.assertRaises(TypeError):
|
||||
Final()
|
||||
with self.assertRaises(TypeError):
|
||||
type(Final)()
|
||||
with self.assertRaises(TypeError):
|
||||
type(Final[Optional[int]])()
|
||||
|
||||
def test_no_isinstance(self):
|
||||
with self.assertRaises(TypeError):
|
||||
isinstance(1, Final[int])
|
||||
with self.assertRaises(TypeError):
|
||||
issubclass(int, Final)
|
||||
|
||||
def test_final_unmodified(self):
|
||||
def func(x): ...
|
||||
self.assertIs(func, final(func))
|
||||
|
||||
|
||||
class CastTests(BaseTestCase):
|
||||
|
||||
def test_basics(self):
|
||||
|
|
|
@ -35,6 +35,7 @@ __all__ = [
|
|||
'Any',
|
||||
'Callable',
|
||||
'ClassVar',
|
||||
'Final',
|
||||
'Generic',
|
||||
'Optional',
|
||||
'Tuple',
|
||||
|
@ -92,6 +93,7 @@ __all__ = [
|
|||
# One-off things.
|
||||
'AnyStr',
|
||||
'cast',
|
||||
'final',
|
||||
'get_type_hints',
|
||||
'NewType',
|
||||
'no_type_check',
|
||||
|
@ -121,7 +123,7 @@ def _type_check(arg, msg, is_argument=True):
|
|||
"""
|
||||
invalid_generic_forms = (Generic, _Protocol)
|
||||
if is_argument:
|
||||
invalid_generic_forms = invalid_generic_forms + (ClassVar, )
|
||||
invalid_generic_forms = invalid_generic_forms + (ClassVar, Final)
|
||||
|
||||
if arg is None:
|
||||
return type(None)
|
||||
|
@ -336,8 +338,8 @@ class _SpecialForm(_Final, _Immutable, _root=True):
|
|||
|
||||
@_tp_cache
|
||||
def __getitem__(self, parameters):
|
||||
if self._name == 'ClassVar':
|
||||
item = _type_check(parameters, 'ClassVar accepts only single type.')
|
||||
if self._name in ('ClassVar', 'Final'):
|
||||
item = _type_check(parameters, f'{self._name} accepts only single type.')
|
||||
return _GenericAlias(self, (item,))
|
||||
if self._name == 'Union':
|
||||
if parameters == ():
|
||||
|
@ -398,6 +400,24 @@ ClassVar = _SpecialForm('ClassVar', doc=
|
|||
be used with isinstance() or issubclass().
|
||||
""")
|
||||
|
||||
Final = _SpecialForm('Final', doc=
|
||||
"""Special typing construct to indicate final names to type checkers.
|
||||
|
||||
A final name cannot be re-assigned or overridden in a subclass.
|
||||
For example:
|
||||
|
||||
MAX_SIZE: Final = 9000
|
||||
MAX_SIZE += 1 # Error reported by type checker
|
||||
|
||||
class Connection:
|
||||
TIMEOUT: Final[int] = 10
|
||||
|
||||
class FastConnector(Connection):
|
||||
TIMEOUT = 1 # Error reported by type checker
|
||||
|
||||
There is no runtime checking of these properties.
|
||||
""")
|
||||
|
||||
Union = _SpecialForm('Union', doc=
|
||||
"""Union type; Union[X, Y] means either X or Y.
|
||||
|
||||
|
@ -1085,6 +1105,32 @@ def overload(func):
|
|||
return _overload_dummy
|
||||
|
||||
|
||||
def final(f):
|
||||
"""A decorator to indicate final methods and final classes.
|
||||
|
||||
Use this decorator to indicate to type checkers that the decorated
|
||||
method cannot be overridden, and decorated class cannot be subclassed.
|
||||
For example:
|
||||
|
||||
class Base:
|
||||
@final
|
||||
def done(self) -> None:
|
||||
...
|
||||
class Sub(Base):
|
||||
def done(self) -> None: # Error reported by type checker
|
||||
...
|
||||
|
||||
@final
|
||||
class Leaf:
|
||||
...
|
||||
class Other(Leaf): # Error reported by type checker
|
||||
...
|
||||
|
||||
There is no runtime checking of these properties.
|
||||
"""
|
||||
return f
|
||||
|
||||
|
||||
class _ProtocolMeta(type):
|
||||
"""Internal metaclass for _Protocol.
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
PEP 591: Add ``Final`` qualifier and ``@final`` decorator to the ``typing`` module.
|
Loading…
Reference in New Issue