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:
Ivan Levkivskyi 2019-05-26 09:37:07 +01:00 committed by GitHub
parent 47dd2f9fd8
commit f367242d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 142 additions and 4 deletions

View File

@ -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

View File

@ -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):

View File

@ -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.

View File

@ -0,0 +1 @@
PEP 591: Add ``Final`` qualifier and ``@final`` decorator to the ``typing`` module.