Merge pull request #7780 from bluetech/final

Mark some public and to-be-public classes as `@final`
This commit is contained in:
Ran Benita 2020-09-23 13:42:11 +03:00 committed by GitHub
commit 5cfd7c0ddd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 81 additions and 1 deletions

View File

@ -0,0 +1,3 @@
Public classes which are not designed to be inherited from are now marked `@final <https://docs.python.org/3/library/typing.html#typing.final>`_.
Code which inherits from these classes will trigger a type-checking (e.g. mypy) error, but will still work in runtime.
Currently the ``final`` designation does not appear in the API Reference but hopefully will in the future.

View File

@ -38,6 +38,7 @@ from _pytest._io import TerminalWriter
from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest.compat import ATTRS_EQ_FIELD from _pytest.compat import ATTRS_EQ_FIELD
from _pytest.compat import final
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
@ -414,6 +415,7 @@ co_equal = compile(
_E = TypeVar("_E", bound=BaseException, covariant=True) _E = TypeVar("_E", bound=BaseException, covariant=True)
@final
@attr.s(repr=False) @attr.s(repr=False)
class ExceptionInfo(Generic[_E]): class ExceptionInfo(Generic[_E]):
"""Wraps sys.exc_info() objects and offers help for navigating the traceback.""" """Wraps sys.exc_info() objects and offers help for navigating the traceback."""

View File

@ -7,6 +7,7 @@ from typing import Sequence
from typing import TextIO from typing import TextIO
from .wcwidth import wcswidth from .wcwidth import wcswidth
from _pytest.compat import final
# This code was initially copied from py 1.8.1, file _io/terminalwriter.py. # This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
@ -36,6 +37,7 @@ def should_do_markup(file: TextIO) -> bool:
) )
@final
class TerminalWriter: class TerminalWriter:
_esctable = dict( _esctable = dict(
black=30, black=30,

View File

@ -21,6 +21,7 @@ from .pathlib import rm_rf
from .reports import CollectReport from .reports import CollectReport
from _pytest import nodes from _pytest import nodes
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import order_preserving_dict from _pytest.compat import order_preserving_dict
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import ExitCode from _pytest.config import ExitCode
@ -50,6 +51,7 @@ Signature: 8a477f597d28d172789f06886806bc55
""" """
@final
@attr.s @attr.s
class Cache: class Cache:
_cachedir = attr.ib(type=Path, repr=False) _cachedir = attr.ib(type=Path, repr=False)

View File

@ -17,6 +17,7 @@ from typing import Tuple
from typing import Union from typing import Union
import pytest import pytest
from _pytest.compat import final
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config import Config from _pytest.config import Config
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
@ -498,6 +499,7 @@ class FDCapture(FDCaptureBinary):
# pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can # pertinent parts of a namedtuple. If the mypy limitation is ever lifted, can
# make it a namedtuple again. # make it a namedtuple again.
# [0]: https://github.com/python/mypy/issues/685 # [0]: https://github.com/python/mypy/issues/685
@final
@functools.total_ordering @functools.total_ordering
class CaptureResult(Generic[AnyStr]): class CaptureResult(Generic[AnyStr]):
"""The result of :method:`CaptureFixture.readouterr`.""" """The result of :method:`CaptureFixture.readouterr`."""

View File

@ -19,7 +19,6 @@ from typing import Union
import attr import attr
from _pytest._io.saferepr import saferepr
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME from _pytest.outcomes import TEST_OUTCOME
@ -297,6 +296,8 @@ def get_real_func(obj):
break break
obj = new_obj obj = new_obj
else: else:
from _pytest._io.saferepr import saferepr
raise ValueError( raise ValueError(
("could not find real function of {start}\nstopped at {current}").format( ("could not find real function of {start}\nstopped at {current}").format(
start=saferepr(start_obj), current=saferepr(obj) start=saferepr(start_obj), current=saferepr(obj)
@ -357,6 +358,19 @@ if sys.version_info < (3, 5, 2):
return f return f
if TYPE_CHECKING:
if sys.version_info >= (3, 8):
from typing import final as final
else:
from typing_extensions import final as final
elif sys.version_info >= (3, 8):
from typing import final as final
else:
def final(f): # noqa: F811
return f
if getattr(attr, "__version_info__", ()) >= (19, 2): if getattr(attr, "__version_info__", ()) >= (19, 2):
ATTRS_EQ_FIELD = "eq" ATTRS_EQ_FIELD = "eq"
else: else:

View File

@ -43,6 +43,7 @@ from .findpaths import determine_setup
from _pytest._code import ExceptionInfo from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback from _pytest._code import filter_traceback
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import importlib_metadata from _pytest.compat import importlib_metadata
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -76,6 +77,7 @@ hookimpl = HookimplMarker("pytest")
hookspec = HookspecMarker("pytest") hookspec = HookspecMarker("pytest")
@final
class ExitCode(enum.IntEnum): class ExitCode(enum.IntEnum):
"""Encodes the valid exit codes by pytest. """Encodes the valid exit codes by pytest.
@ -322,6 +324,7 @@ def _prepareconfig(
raise raise
@final
class PytestPluginManager(PluginManager): class PytestPluginManager(PluginManager):
"""A :py:class:`pluggy.PluginManager <pluggy.PluginManager>` with """A :py:class:`pluggy.PluginManager <pluggy.PluginManager>` with
additional pytest-specific functionality: additional pytest-specific functionality:
@ -815,6 +818,7 @@ def _args_converter(args: Iterable[str]) -> Tuple[str, ...]:
return tuple(args) return tuple(args)
@final
class Config: class Config:
"""Access to configuration values, pluginmanager and plugin hooks. """Access to configuration values, pluginmanager and plugin hooks.
@ -825,6 +829,7 @@ class Config:
invocation. invocation.
""" """
@final
@attr.s(frozen=True) @attr.s(frozen=True)
class InvocationParams: class InvocationParams:
"""Holds parameters passed during :func:`pytest.main`. """Holds parameters passed during :func:`pytest.main`.

View File

@ -16,6 +16,7 @@ from typing import Union
import py import py
import _pytest._io import _pytest._io
from _pytest.compat import final
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config.exceptions import UsageError from _pytest.config.exceptions import UsageError
@ -26,6 +27,7 @@ if TYPE_CHECKING:
FILE_OR_DIR = "file_or_dir" FILE_OR_DIR = "file_or_dir"
@final
class Parser: class Parser:
"""Parser for command line arguments and ini-file values. """Parser for command line arguments and ini-file values.

View File

@ -1,3 +1,7 @@
from _pytest.compat import final
@final
class UsageError(Exception): class UsageError(Exception):
"""Error in pytest usage or invocation.""" """Error in pytest usage or invocation."""

View File

@ -32,6 +32,7 @@ from _pytest._code.code import TerminalRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import _format_args from _pytest.compat import _format_args
from _pytest.compat import _PytestWrapper from _pytest.compat import _PytestWrapper
from _pytest.compat import final
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import get_real_method from _pytest.compat import get_real_method
from _pytest.compat import getfuncargnames from _pytest.compat import getfuncargnames
@ -730,6 +731,7 @@ class FixtureRequest:
return "<FixtureRequest for %r>" % (self.node) return "<FixtureRequest for %r>" % (self.node)
@final
class SubRequest(FixtureRequest): class SubRequest(FixtureRequest):
"""A sub request for handling getting a fixture from a test function/fixture.""" """A sub request for handling getting a fixture from a test function/fixture."""
@ -796,6 +798,7 @@ def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int:
) )
@final
class FixtureLookupError(LookupError): class FixtureLookupError(LookupError):
"""Could not return a requested fixture (missing or invalid).""" """Could not return a requested fixture (missing or invalid)."""
@ -952,6 +955,7 @@ def _eval_scope_callable(
return result return result
@final
class FixtureDef(Generic[_FixtureValue]): class FixtureDef(Generic[_FixtureValue]):
"""A container for a factory definition.""" """A container for a factory definition."""
@ -1161,6 +1165,7 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
return result return result
@final
@attr.s(frozen=True) @attr.s(frozen=True)
class FixtureFunctionMarker: class FixtureFunctionMarker:
scope = attr.ib(type="Union[_Scope, Callable[[str, Config], _Scope]]") scope = attr.ib(type="Union[_Scope, Callable[[str, Config], _Scope]]")

View File

@ -19,6 +19,7 @@ import pytest
from _pytest import nodes from _pytest import nodes
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.capture import CaptureManager from _pytest.capture import CaptureManager
from _pytest.compat import final
from _pytest.compat import nullcontext from _pytest.compat import nullcontext
from _pytest.config import _strtobool from _pytest.config import _strtobool
from _pytest.config import Config from _pytest.config import Config
@ -339,6 +340,7 @@ class LogCaptureHandler(logging.StreamHandler):
raise raise
@final
class LogCaptureFixture: class LogCaptureFixture:
"""Provides access and control of log capturing.""" """Provides access and control of log capturing."""

View File

@ -21,6 +21,7 @@ import py
import _pytest._code import _pytest._code
from _pytest import nodes from _pytest import nodes
from _pytest.compat import final
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config import Config from _pytest.config import Config
@ -435,6 +436,7 @@ class _bestrelpath_cache(Dict[Path, str]):
return r return r
@final
class Session(nodes.FSCollector): class Session(nodes.FSCollector):
Interrupted = Interrupted Interrupted = Interrupted
Failed = Failed Failed = Failed

View File

@ -20,6 +20,7 @@ import attr
from .._code import getfslineno from .._code import getfslineno
from ..compat import ascii_escaped from ..compat import ascii_escaped
from ..compat import final
from ..compat import NOTSET from ..compat import NOTSET
from ..compat import NotSetType from ..compat import NotSetType
from ..compat import overload from ..compat import overload
@ -199,6 +200,7 @@ class ParameterSet(
return argnames, parameters return argnames, parameters
@final
@attr.s(frozen=True) @attr.s(frozen=True)
class Mark: class Mark:
#: Name of the mark. #: Name of the mark.
@ -452,6 +454,7 @@ if TYPE_CHECKING:
... ...
@final
class MarkGenerator: class MarkGenerator:
"""Factory for :class:`MarkDecorator` objects - exposed as """Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. a ``pytest.mark`` singleton instance.
@ -525,6 +528,7 @@ MARK_GEN = MarkGenerator()
# TODO(py36): inherit from typing.MutableMapping[str, Any]. # TODO(py36): inherit from typing.MutableMapping[str, Any].
@final
class NodeKeywords(collections.abc.MutableMapping): # type: ignore[type-arg] class NodeKeywords(collections.abc.MutableMapping): # type: ignore[type-arg]
def __init__(self, node: "Node") -> None: def __init__(self, node: "Node") -> None:
self.node = node self.node = node

View File

@ -14,6 +14,7 @@ from typing import TypeVar
from typing import Union from typing import Union
import pytest import pytest
from _pytest.compat import final
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.pathlib import Path from _pytest.pathlib import Path
@ -110,6 +111,7 @@ class Notset:
notset = Notset() notset = Notset()
@final
class MonkeyPatch: class MonkeyPatch:
"""Object returned by the ``monkeypatch`` fixture keeping a record of """Object returned by the ``monkeypatch`` fixture keeping a record of
setattr/item/env/syspath changes.""" setattr/item/env/syspath changes."""

View File

@ -28,6 +28,7 @@ import pytest
from _pytest import timing from _pytest import timing
from _pytest._code import Source from _pytest._code import Source
from _pytest.capture import _get_multicapture from _pytest.capture import _get_multicapture
from _pytest.compat import final
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin
@ -597,6 +598,7 @@ class SysPathsSnapshot:
sys.path[:], sys.meta_path[:] = self.__saved sys.path[:], sys.meta_path[:] = self.__saved
@final
class Testdir: class Testdir:
"""Temporary test directory with tools to test/run pytest itself. """Temporary test directory with tools to test/run pytest itself.

View File

@ -37,6 +37,7 @@ from _pytest._code.code import TerminalRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest.compat import ascii_escaped from _pytest.compat import ascii_escaped
from _pytest.compat import final
from _pytest.compat import get_default_arg_names from _pytest.compat import get_default_arg_names
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import getimfunc from _pytest.compat import getimfunc
@ -864,6 +865,7 @@ def hasnew(obj: object) -> bool:
return False return False
@final
class CallSpec2: class CallSpec2:
def __init__(self, metafunc: "Metafunc") -> None: def __init__(self, metafunc: "Metafunc") -> None:
self.metafunc = metafunc self.metafunc = metafunc
@ -924,6 +926,7 @@ class CallSpec2:
self.marks.extend(normalize_mark_list(marks)) self.marks.extend(normalize_mark_list(marks))
@final
class Metafunc: class Metafunc:
"""Objects passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook. """Objects passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.

View File

@ -17,6 +17,7 @@ from typing import TypeVar
from typing import Union from typing import Union
import _pytest._code import _pytest._code
from _pytest.compat import final
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import STRING_TYPES from _pytest.compat import STRING_TYPES
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
@ -699,6 +700,7 @@ def raises( # noqa: F811
raises.Exception = fail.Exception # type: ignore raises.Exception = fail.Exception # type: ignore
@final
class RaisesContext(Generic[_E]): class RaisesContext(Generic[_E]):
def __init__( def __init__(
self, self,

View File

@ -13,6 +13,7 @@ from typing import Tuple
from typing import TypeVar from typing import TypeVar
from typing import Union from typing import Union
from _pytest.compat import final
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
@ -228,6 +229,7 @@ class WarningsRecorder(warnings.catch_warnings):
self._entered = False self._entered = False
@final
class WarningsChecker(WarningsRecorder): class WarningsChecker(WarningsRecorder):
def __init__( def __init__(
self, self,

View File

@ -26,6 +26,7 @@ from _pytest._code.code import ReprLocals
from _pytest._code.code import ReprTraceback from _pytest._code.code import ReprTraceback
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config import Config from _pytest.config import Config
from _pytest.nodes import Collector from _pytest.nodes import Collector
@ -225,6 +226,7 @@ def _report_unserialization_failure(
raise RuntimeError(stream.getvalue()) raise RuntimeError(stream.getvalue())
@final
class TestReport(BaseReport): class TestReport(BaseReport):
"""Basic test report object (also used for setup and teardown calls if """Basic test report object (also used for setup and teardown calls if
they fail).""" they fail)."""
@ -333,6 +335,7 @@ class TestReport(BaseReport):
) )
@final
class CollectReport(BaseReport): class CollectReport(BaseReport):
"""Collection report object.""" """Collection report object."""

View File

@ -22,6 +22,7 @@ from _pytest import timing
from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest.compat import final
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.nodes import Collector from _pytest.nodes import Collector
@ -259,6 +260,7 @@ def call_runtest_hook(
TResult = TypeVar("TResult", covariant=True) TResult = TypeVar("TResult", covariant=True)
@final
@attr.s(repr=False) @attr.s(repr=False)
class CallInfo(Generic[TResult]): class CallInfo(Generic[TResult]):
"""Result/Exception info a function invocation. """Result/Exception info a function invocation.

View File

@ -32,6 +32,7 @@ from _pytest import timing
from _pytest._code import ExceptionInfo from _pytest._code import ExceptionInfo
from _pytest._code.code import ExceptionRepr from _pytest._code.code import ExceptionRepr
from _pytest._io.wcwidth import wcswidth from _pytest._io.wcwidth import wcswidth
from _pytest.compat import final
from _pytest.compat import order_preserving_dict from _pytest.compat import order_preserving_dict
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin
@ -309,6 +310,7 @@ class WarningReport:
return None return None
@final
class TerminalReporter: class TerminalReporter:
def __init__(self, config: Config, file: Optional[TextIO] = None) -> None: def __init__(self, config: Config, file: Optional[TextIO] = None) -> None:
import _pytest.config import _pytest.config

View File

@ -13,11 +13,13 @@ from .pathlib import LOCK_TIMEOUT
from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import Path from .pathlib import Path
from _pytest.compat import final
from _pytest.config import Config from _pytest.config import Config
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
@final
@attr.s @attr.s
class TempPathFactory: class TempPathFactory:
"""Factory for temporary directories under the common base temp directory. """Factory for temporary directories under the common base temp directory.
@ -103,6 +105,7 @@ class TempPathFactory:
return t return t
@final
@attr.s @attr.s
class TempdirFactory: class TempdirFactory:
"""Backward comptibility wrapper that implements :class:``py.path.local`` """Backward comptibility wrapper that implements :class:``py.path.local``

View File

@ -4,6 +4,7 @@ from typing import TypeVar
import attr import attr
from _pytest.compat import final
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
@ -16,36 +17,42 @@ class PytestWarning(UserWarning):
__module__ = "pytest" __module__ = "pytest"
@final
class PytestAssertRewriteWarning(PytestWarning): class PytestAssertRewriteWarning(PytestWarning):
"""Warning emitted by the pytest assert rewrite module.""" """Warning emitted by the pytest assert rewrite module."""
__module__ = "pytest" __module__ = "pytest"
@final
class PytestCacheWarning(PytestWarning): class PytestCacheWarning(PytestWarning):
"""Warning emitted by the cache plugin in various situations.""" """Warning emitted by the cache plugin in various situations."""
__module__ = "pytest" __module__ = "pytest"
@final
class PytestConfigWarning(PytestWarning): class PytestConfigWarning(PytestWarning):
"""Warning emitted for configuration issues.""" """Warning emitted for configuration issues."""
__module__ = "pytest" __module__ = "pytest"
@final
class PytestCollectionWarning(PytestWarning): class PytestCollectionWarning(PytestWarning):
"""Warning emitted when pytest is not able to collect a file or symbol in a module.""" """Warning emitted when pytest is not able to collect a file or symbol in a module."""
__module__ = "pytest" __module__ = "pytest"
@final
class PytestDeprecationWarning(PytestWarning, DeprecationWarning): class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
"""Warning class for features that will be removed in a future version.""" """Warning class for features that will be removed in a future version."""
__module__ = "pytest" __module__ = "pytest"
@final
class PytestExperimentalApiWarning(PytestWarning, FutureWarning): class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
"""Warning category used to denote experiments in pytest. """Warning category used to denote experiments in pytest.
@ -64,6 +71,7 @@ class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
) )
@final
class PytestUnhandledCoroutineWarning(PytestWarning): class PytestUnhandledCoroutineWarning(PytestWarning):
"""Warning emitted for an unhandled coroutine. """Warning emitted for an unhandled coroutine.
@ -75,6 +83,7 @@ class PytestUnhandledCoroutineWarning(PytestWarning):
__module__ = "pytest" __module__ = "pytest"
@final
class PytestUnknownMarkWarning(PytestWarning): class PytestUnknownMarkWarning(PytestWarning):
"""Warning emitted on use of unknown markers. """Warning emitted on use of unknown markers.
@ -87,6 +96,7 @@ class PytestUnknownMarkWarning(PytestWarning):
_W = TypeVar("_W", bound=PytestWarning) _W = TypeVar("_W", bound=PytestWarning)
@final
@attr.s @attr.s
class UnformattedWarning(Generic[_W]): class UnformattedWarning(Generic[_W]):
"""A warning meant to be formatted during runtime. """A warning meant to be formatted during runtime.