Merge pull request #7613 from bluetech/typing-warn-unreachable

typing: set warn_unreachable
This commit is contained in:
Ran Benita 2020-08-04 22:44:21 +03:00 committed by GitHub
commit 303030c141
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 60 additions and 46 deletions

View File

@ -103,5 +103,6 @@ show_error_codes = True
strict_equality = True
warn_redundant_casts = True
warn_return_any = True
warn_unreachable = True
warn_unused_configs = True
no_implicit_reexport = True

View File

@ -676,7 +676,7 @@ class FormattedExcinfo:
def get_source(
self,
source: "Source",
source: Optional["Source"],
line_index: int = -1,
excinfo: Optional[ExceptionInfo[BaseException]] = None,
short: bool = False,

View File

@ -57,7 +57,7 @@ def register_assert_rewrite(*names: str) -> None:
"""
for name in names:
if not isinstance(name, str):
msg = "expected module names as *args, got {0} instead"
msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable]
raise TypeError(msg.format(repr(names)))
for hook in sys.meta_path:
if isinstance(hook, rewrite.AssertionRewritingHook):

View File

@ -16,6 +16,7 @@ import types
from typing import Callable
from typing import Dict
from typing import IO
from typing import Iterable
from typing import List
from typing import Optional
from typing import Sequence
@ -455,12 +456,9 @@ def _should_repr_global_name(obj: object) -> bool:
return True
def _format_boolop(explanations, is_or: bool):
def _format_boolop(explanations: Iterable[str], is_or: bool) -> str:
explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
if isinstance(explanation, str):
return explanation.replace("%", "%%")
else:
return explanation.replace(b"%", b"%%")
return explanation.replace("%", "%%")
def _call_reprcompare(

View File

@ -116,7 +116,7 @@ def _py36_windowsconsoleio_workaround(stream: TextIO) -> None:
return
# Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666).
if not hasattr(stream, "buffer"):
if not hasattr(stream, "buffer"): # type: ignore[unreachable]
return
buffered = hasattr(stream.buffer, "raw")

View File

@ -176,8 +176,9 @@ def getfuncargnames(
p.name
for p in parameters.values()
if (
p.kind is Parameter.POSITIONAL_OR_KEYWORD
or p.kind is Parameter.KEYWORD_ONLY
# TODO: Remove type ignore after https://github.com/python/typeshed/pull/4383
p.kind is Parameter.POSITIONAL_OR_KEYWORD # type: ignore[unreachable]
or p.kind is Parameter.KEYWORD_ONLY # type: ignore[unreachable]
)
and p.default is Parameter.empty
)

View File

@ -1390,7 +1390,7 @@ def _assertion_supported() -> bool:
except AssertionError:
return True
else:
return False
return False # type: ignore[unreachable]
def create_terminal_writer(

View File

@ -107,7 +107,7 @@ def locate_config(
def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local:
common_ancestor = None
common_ancestor = None # type: Optional[py.path.local]
for path in paths:
if not path.exists():
continue

View File

@ -7,6 +7,7 @@ from typing import Any
from typing import Callable
from typing import Generator
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union
@ -23,6 +24,8 @@ from _pytest.nodes import Node
from _pytest.reports import BaseReport
if TYPE_CHECKING:
from typing import Type
from _pytest.capture import CaptureManager
from _pytest.runner import CallInfo
@ -92,20 +95,22 @@ def pytest_configure(config: Config) -> None:
class pytestPDB:
"""Pseudo PDB that defers to the real pdb."""
_pluginmanager = None # type: PytestPluginManager
_pluginmanager = None # type: Optional[PytestPluginManager]
_config = None # type: Config
_saved = [] # type: List[Tuple[Callable[..., None], PytestPluginManager, Config]]
_saved = (
[]
) # type: List[Tuple[Callable[..., None], Optional[PytestPluginManager], Config]]
_recursive_debug = 0
_wrapped_pdb_cls = None
_wrapped_pdb_cls = None # type: Optional[Tuple[Type[Any], Type[Any]]]
@classmethod
def _is_capturing(cls, capman: "CaptureManager") -> Union[str, bool]:
def _is_capturing(cls, capman: Optional["CaptureManager"]) -> Union[str, bool]:
if capman:
return capman.is_capturing()
return False
@classmethod
def _import_pdb_cls(cls, capman: "CaptureManager"):
def _import_pdb_cls(cls, capman: Optional["CaptureManager"]):
if not cls._config:
import pdb
@ -144,7 +149,7 @@ class pytestPDB:
return wrapped_cls
@classmethod
def _get_pdb_wrapper_class(cls, pdb_cls, capman: "CaptureManager"):
def _get_pdb_wrapper_class(cls, pdb_cls, capman: Optional["CaptureManager"]):
import _pytest.config
# Type ignored because mypy doesn't support "dynamic"
@ -176,9 +181,11 @@ class pytestPDB:
"PDB continue (IO-capturing resumed for %s)"
% capturing,
)
assert capman is not None
capman.resume()
else:
tw.sep(">", "PDB continue")
assert cls._pluginmanager is not None
cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self)
self._continued = True
return ret
@ -232,10 +239,10 @@ class pytestPDB:
"""Initialize PDB debugging, dropping any IO capturing."""
import _pytest.config
if cls._pluginmanager is not None:
capman = cls._pluginmanager.getplugin("capturemanager")
if cls._pluginmanager is None:
capman = None # type: Optional[CaptureManager]
else:
capman = None
capman = cls._pluginmanager.getplugin("capturemanager")
if capman:
capman.suspend(in_=True)

View File

@ -635,8 +635,8 @@ def _init_checker_class() -> "Type[doctest.OutputChecker]":
return got
offset = 0
for w, g in zip(wants, gots):
fraction = w.group("fraction")
exponent = w.group("exponent1")
fraction = w.group("fraction") # type: Optional[str]
exponent = w.group("exponent1") # type: Optional[str]
if exponent is None:
exponent = w.group("exponent2")
if fraction is None:

View File

@ -993,7 +993,8 @@ class FixtureDef(Generic[_FixtureValue]):
else:
scope_ = scope
self.scopenum = scope2index(
scope_ or "function",
# TODO: Check if the `or` here is really necessary.
scope_ or "function", # type: ignore[unreachable]
descr="Fixture '{}'".format(func.__name__),
where=baseid,
)
@ -1319,7 +1320,7 @@ def fixture( # noqa: F811
# **kwargs and check `in`, but that obfuscates the function signature.
if isinstance(fixture_function, str):
# It's actually the first positional argument, scope.
args = (fixture_function, *args)
args = (fixture_function, *args) # type: ignore[unreachable]
fixture_function = None
duplicated_args = []
if len(args) > 0:

View File

@ -330,7 +330,7 @@ def _check_record_param_type(param: str, v: str) -> None:
type."""
__tracebackhide__ = True
if not isinstance(v, str):
msg = "{param} parameter needs to be a string, but {g} given"
msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable]
raise TypeError(msg.format(param=param, g=type(v).__name__))

View File

@ -91,7 +91,7 @@ def annotated_getattr(obj: object, name: str, ann: str) -> object:
def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]:
if not isinstance(import_path, str) or "." not in import_path:
if not isinstance(import_path, str) or "." not in import_path: # type: ignore[unreachable]
raise TypeError(
"must be absolute import path string, not {!r}".format(import_path)
)
@ -272,7 +272,7 @@ class MonkeyPatch:
character.
"""
if not isinstance(value, str):
warnings.warn(
warnings.warn( # type: ignore[unreachable]
pytest.PytestWarning(
"Value of environment variable {name} type should be str, but got "
"{value!r} (type: {type}); converted to str implicitly".format(

View File

@ -28,7 +28,7 @@ class OutcomeException(BaseException):
def __init__(self, msg: Optional[str] = None, pytrace: bool = True) -> None:
if msg is not None and not isinstance(msg, str):
error_msg = (
error_msg = ( # type: ignore[unreachable]
"{} expected string as 'msg' parameter, got '{}' instead.\n"
"Perhaps you meant to use a mark?"
)

View File

@ -367,7 +367,6 @@ def make_numbered_dir_with_cleanup(
def resolve_from_str(input: str, root: py.path.local) -> Path:
assert not isinstance(input, Path), "would break on py2"
rootpath = Path(root)
input = expanduser(input)
input = expandvars(input)

View File

@ -1097,7 +1097,10 @@ class Metafunc:
elif isinstance(id_value, (float, int, bool)):
new_ids.append(str(id_value))
else:
msg = "In {}: ids must be list of string/float/int/bool, found: {} (type: {!r}) at index {}"
msg = ( # type: ignore[unreachable]
"In {}: ids must be list of string/float/int/bool, "
"found: {} (type: {!r}) at index {}"
)
fail(
msg.format(func_name, saferepr(id_value), type(id_value), idx),
pytrace=False,

View File

@ -495,7 +495,8 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
elif (
isinstance(expected, Iterable)
and isinstance(expected, Sized)
and not isinstance(expected, STRING_TYPES)
# Type ignored because the error is wrong -- not unreachable.
and not isinstance(expected, STRING_TYPES) # type: ignore[unreachable]
):
cls = ApproxSequencelike
else:
@ -662,8 +663,8 @@ def raises( # noqa: F811
else:
excepted_exceptions = expected_exception
for exc in excepted_exceptions:
if not isinstance(exc, type) or not issubclass(exc, BaseException):
msg = "expected exception must be a BaseException type, not {}"
if not isinstance(exc, type) or not issubclass(exc, BaseException): # type: ignore[unreachable]
msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable]
not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__
raise TypeError(msg.format(not_a))

View File

@ -386,7 +386,8 @@ def pytest_report_to_serializable(
data = report._to_json()
data["$report_type"] = report.__class__.__name__
return data
return None
# TODO: Check if this is actually reachable.
return None # type: ignore[unreachable]
def pytest_report_from_serializable(

View File

@ -522,12 +522,12 @@ class TerminalReporter:
rep = report
res = self.config.hook.pytest_report_teststatus(
report=rep, config=self.config
) # type: Tuple[str, str, str]
) # type: Tuple[str, str, Union[str, Tuple[str, Mapping[str, bool]]]]
category, letter, word = res
if isinstance(word, tuple):
word, markup = word
else:
if not isinstance(word, tuple):
markup = None
else:
word, markup = word
self._add_stats(category, [rep])
if not letter and not word:
# Probably passed setup/teardown.

View File

@ -26,7 +26,7 @@ class TempPathFactory:
"""
_given_basetemp = attr.ib(
type=Path,
type=Optional[Path],
# Use os.path.abspath() to get absolute path instead of resolve() as it
# does not work the same in all platforms (see #4427).
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012).

View File

@ -238,7 +238,7 @@ def test_getline_finally() -> None:
c(1) # type: ignore
finally:
if teardown:
teardown()
teardown() # type: ignore[unreachable]
source = excinfo.traceback[-1].statement
assert str(source).strip() == "c(1) # type: ignore"

View File

@ -381,7 +381,7 @@ class TestAssertionRewrite:
)
def f7() -> None:
assert False or x()
assert False or x() # type: ignore[unreachable]
assert (
getmsg(f7, {"x": x})
@ -416,7 +416,7 @@ class TestAssertionRewrite:
def test_short_circuit_evaluation(self) -> None:
def f1() -> None:
assert True or explode # type: ignore[name-defined] # noqa: F821
assert True or explode # type: ignore[name-defined,unreachable] # noqa: F821
getmsg(f1, must_pass=True)
@ -471,7 +471,7 @@ class TestAssertionRewrite:
assert getmsg(f1) == "assert ((3 % 2) and False)"
def f2() -> None:
assert False or 4 % 2
assert False or 4 % 2 # type: ignore[unreachable]
assert getmsg(f2) == "assert (False or (4 % 2))"

View File

@ -360,7 +360,7 @@ def test_issue156_undo_staticmethod(Sample: "Type[Sample]") -> None:
monkeypatch.setattr(Sample, "hello", None)
assert Sample.hello is None
monkeypatch.undo()
monkeypatch.undo() # type: ignore[unreachable]
assert Sample.hello()

View File

@ -23,7 +23,9 @@ def test_make_hook_recorder(testdir) -> None:
recorder = testdir.make_hook_recorder(item.config.pluginmanager)
assert not recorder.getfailures()
pytest.xfail("internal reportrecorder tests need refactoring")
# (The silly condition is to fool mypy that the code below this is reachable)
if 1 + 1 == 2:
pytest.xfail("internal reportrecorder tests need refactoring")
class rep:
excinfo = None