[Python-checkins] bpo-37045: PEP 591: Add final qualifiers to typing module (GH-13571)
Ivan Levkivskyi
webhook-mailer at python.org
Sun May 26 04:37:21 EDT 2019
https://github.com/python/cpython/commit/f367242d10ef36db38133a39ab7627f63099cba4
commit: f367242d10ef36db38133a39ab7627f63099cba4
branch: master
author: Ivan Levkivskyi <levkivskyi at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-05-26T09:37:07+01:00
summary:
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`.
files:
A Misc/NEWS.d/next/Library/2019-05-25-18-36-50.bpo-37045.suHdVJ.rst
M Doc/library/typing.rst
M Lib/test/test_typing.py
M Lib/typing.py
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 86a3db8467ec..8362f1d8e6b7 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -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
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index c9bfd0c7ed72..3d93eb396ce7 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -12,7 +12,7 @@
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 @@ def test_no_isinstance(self):
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):
diff --git a/Lib/typing.py b/Lib/typing.py
index 7aab1628a319..06a7eb0dff84 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -35,6 +35,7 @@
'Any',
'Callable',
'ClassVar',
+ 'Final',
'Generic',
'Optional',
'Tuple',
@@ -92,6 +93,7 @@
# 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 @@ def __subclasscheck__(self, cls):
@_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 @@ class Starship:
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 utf8(value):
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.
diff --git a/Misc/NEWS.d/next/Library/2019-05-25-18-36-50.bpo-37045.suHdVJ.rst b/Misc/NEWS.d/next/Library/2019-05-25-18-36-50.bpo-37045.suHdVJ.rst
new file mode 100644
index 000000000000..001529ed6db4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-05-25-18-36-50.bpo-37045.suHdVJ.rst
@@ -0,0 +1 @@
+PEP 591: Add ``Final`` qualifier and ``@final`` decorator to the ``typing`` module.
\ No newline at end of file
More information about the Python-checkins
mailing list