Add rudimentary mypy type checking
Add a very lax mypy configuration, add it to tox -e linting, and fix/ignore the few errors that come up. The idea is to get it running before diving in too much. This enables: - Progressively adding type annotations and enabling more strict options, which will improve the codebase (IMO). - Annotating the public API in-line, and eventually exposing it to library users who use type checkers (with a py.typed file). Though, none of this is done yet. Refs https://github.com/pytest-dev/pytest/issues/3342.
This commit is contained in:
parent
628ff4d619
commit
89dfde9535
|
@ -35,6 +35,7 @@ env/
|
||||||
.tox
|
.tox
|
||||||
.cache
|
.cache
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
|
.mypy_cache
|
||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
.coverage.*
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
|
|
@ -28,6 +28,7 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
language_version: python3
|
language_version: python3
|
||||||
|
additional_dependencies: [flake8-typing-imports]
|
||||||
- repo: https://github.com/asottile/reorder_python_imports
|
- repo: https://github.com/asottile/reorder_python_imports
|
||||||
rev: v1.4.0
|
rev: v1.4.0
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -42,6 +43,17 @@ repos:
|
||||||
rev: v1.4.0
|
rev: v1.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: rst-backticks
|
- id: rst-backticks
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v0.711
|
||||||
|
hooks:
|
||||||
|
- id: mypy
|
||||||
|
name: mypy (src)
|
||||||
|
files: ^src/
|
||||||
|
args: []
|
||||||
|
- id: mypy
|
||||||
|
name: mypy (testing)
|
||||||
|
files: ^testing/
|
||||||
|
args: []
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
- id: rst
|
- id: rst
|
||||||
|
|
|
@ -6,7 +6,7 @@ if __name__ == "__main__":
|
||||||
import pstats
|
import pstats
|
||||||
|
|
||||||
script = sys.argv[1:] if len(sys.argv) > 1 else ["empty.py"]
|
script = sys.argv[1:] if len(sys.argv) > 1 else ["empty.py"]
|
||||||
stats = cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
|
cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
|
||||||
p = pstats.Stats("prof")
|
p = pstats.Stats("prof")
|
||||||
p.strip_dirs()
|
p.strip_dirs()
|
||||||
p.sort_stats("cumulative")
|
p.sort_stats("cumulative")
|
||||||
|
|
|
@ -61,3 +61,11 @@ ignore =
|
||||||
|
|
||||||
[devpi:upload]
|
[devpi:upload]
|
||||||
formats = sdist.tgz,bdist_wheel
|
formats = sdist.tgz,bdist_wheel
|
||||||
|
|
||||||
|
[mypy]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
no_implicit_optional = True
|
||||||
|
strict_equality = True
|
||||||
|
warn_redundant_casts = True
|
||||||
|
warn_return_any = True
|
||||||
|
warn_unused_configs = True
|
||||||
|
|
|
@ -56,6 +56,7 @@ If things do not work right away:
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class FastFilesCompleter:
|
class FastFilesCompleter:
|
||||||
|
@ -91,7 +92,7 @@ if os.environ.get("_ARGCOMPLETE"):
|
||||||
import argcomplete.completers
|
import argcomplete.completers
|
||||||
except ImportError:
|
except ImportError:
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
filescompleter = FastFilesCompleter()
|
filescompleter = FastFilesCompleter() # type: Optional[FastFilesCompleter]
|
||||||
|
|
||||||
def try_argcomplete(parser):
|
def try_argcomplete(parser):
|
||||||
argcomplete.autocomplete(parser, always_complete_options=False)
|
argcomplete.autocomplete(parser, always_complete_options=False)
|
||||||
|
|
|
@ -33,7 +33,8 @@ class Code:
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.raw == other.raw
|
return self.raw == other.raw
|
||||||
|
|
||||||
__hash__ = None
|
# Ignore type because of https://github.com/python/mypy/issues/4266.
|
||||||
|
__hash__ = None # type: ignore
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self == other
|
return not self == other
|
||||||
|
@ -188,11 +189,11 @@ class TracebackEntry:
|
||||||
""" path to the source code """
|
""" path to the source code """
|
||||||
return self.frame.code.path
|
return self.frame.code.path
|
||||||
|
|
||||||
def getlocals(self):
|
@property
|
||||||
|
def locals(self):
|
||||||
|
""" locals of underlaying frame """
|
||||||
return self.frame.f_locals
|
return self.frame.f_locals
|
||||||
|
|
||||||
locals = property(getlocals, None, None, "locals of underlaying frame")
|
|
||||||
|
|
||||||
def getfirstlinesource(self):
|
def getfirstlinesource(self):
|
||||||
return self.frame.code.firstlineno
|
return self.frame.code.firstlineno
|
||||||
|
|
||||||
|
@ -255,11 +256,11 @@ class TracebackEntry:
|
||||||
line = "???"
|
line = "???"
|
||||||
return " File %r:%d in %s\n %s\n" % (fn, self.lineno + 1, name, line)
|
return " File %r:%d in %s\n %s\n" % (fn, self.lineno + 1, name, line)
|
||||||
|
|
||||||
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
""" co_name of underlaying code """
|
||||||
return self.frame.code.raw.co_name
|
return self.frame.code.raw.co_name
|
||||||
|
|
||||||
name = property(name, None, None, "co_name of underlaying code")
|
|
||||||
|
|
||||||
|
|
||||||
class Traceback(list):
|
class Traceback(list):
|
||||||
""" Traceback objects encapsulate and offer higher level
|
""" Traceback objects encapsulate and offer higher level
|
||||||
|
|
|
@ -44,7 +44,8 @@ class Source:
|
||||||
return str(self) == other
|
return str(self) == other
|
||||||
return False
|
return False
|
||||||
|
|
||||||
__hash__ = None
|
# Ignore type because of https://github.com/python/mypy/issues/4266.
|
||||||
|
__hash__ = None # type: ignore
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if isinstance(key, int):
|
if isinstance(key, int):
|
||||||
|
|
|
@ -12,6 +12,10 @@ import struct
|
||||||
import sys
|
import sys
|
||||||
import tokenize
|
import tokenize
|
||||||
import types
|
import types
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
import atomicwrites
|
import atomicwrites
|
||||||
|
|
||||||
|
@ -459,17 +463,18 @@ def set_location(node, lineno, col_offset):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
def _get_assertion_exprs(src: bytes): # -> Dict[int, str]
|
def _get_assertion_exprs(src: bytes) -> Dict[int, str]:
|
||||||
"""Returns a mapping from {lineno: "assertion test expression"}"""
|
"""Returns a mapping from {lineno: "assertion test expression"}"""
|
||||||
ret = {}
|
ret = {} # type: Dict[int, str]
|
||||||
|
|
||||||
depth = 0
|
depth = 0
|
||||||
lines = []
|
lines = [] # type: List[str]
|
||||||
assert_lineno = None
|
assert_lineno = None # type: Optional[int]
|
||||||
seen_lines = set()
|
seen_lines = set() # type: Set[int]
|
||||||
|
|
||||||
def _write_and_reset() -> None:
|
def _write_and_reset() -> None:
|
||||||
nonlocal depth, lines, assert_lineno, seen_lines
|
nonlocal depth, lines, assert_lineno, seen_lines
|
||||||
|
assert assert_lineno is not None
|
||||||
ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\")
|
ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\")
|
||||||
depth = 0
|
depth = 0
|
||||||
lines = []
|
lines = []
|
||||||
|
@ -477,21 +482,21 @@ def _get_assertion_exprs(src: bytes): # -> Dict[int, str]
|
||||||
seen_lines = set()
|
seen_lines = set()
|
||||||
|
|
||||||
tokens = tokenize.tokenize(io.BytesIO(src).readline)
|
tokens = tokenize.tokenize(io.BytesIO(src).readline)
|
||||||
for tp, src, (lineno, offset), _, line in tokens:
|
for tp, source, (lineno, offset), _, line in tokens:
|
||||||
if tp == tokenize.NAME and src == "assert":
|
if tp == tokenize.NAME and source == "assert":
|
||||||
assert_lineno = lineno
|
assert_lineno = lineno
|
||||||
elif assert_lineno is not None:
|
elif assert_lineno is not None:
|
||||||
# keep track of depth for the assert-message `,` lookup
|
# keep track of depth for the assert-message `,` lookup
|
||||||
if tp == tokenize.OP and src in "([{":
|
if tp == tokenize.OP and source in "([{":
|
||||||
depth += 1
|
depth += 1
|
||||||
elif tp == tokenize.OP and src in ")]}":
|
elif tp == tokenize.OP and source in ")]}":
|
||||||
depth -= 1
|
depth -= 1
|
||||||
|
|
||||||
if not lines:
|
if not lines:
|
||||||
lines.append(line[offset:])
|
lines.append(line[offset:])
|
||||||
seen_lines.add(lineno)
|
seen_lines.add(lineno)
|
||||||
# a non-nested comma separates the expression from the message
|
# a non-nested comma separates the expression from the message
|
||||||
elif depth == 0 and tp == tokenize.OP and src == ",":
|
elif depth == 0 and tp == tokenize.OP and source == ",":
|
||||||
# one line assert with message
|
# one line assert with message
|
||||||
if lineno in seen_lines and len(lines) == 1:
|
if lineno in seen_lines and len(lines) == 1:
|
||||||
offset_in_trimmed = offset + len(lines[-1]) - len(line)
|
offset_in_trimmed = offset + len(lines[-1]) - len(line)
|
||||||
|
|
|
@ -547,6 +547,8 @@ class FDCaptureBinary:
|
||||||
self.start = lambda: None
|
self.start = lambda: None
|
||||||
self.done = lambda: None
|
self.done = lambda: None
|
||||||
else:
|
else:
|
||||||
|
self.start = self._start
|
||||||
|
self.done = self._done
|
||||||
if targetfd == 0:
|
if targetfd == 0:
|
||||||
assert not tmpfile, "cannot set tmpfile with stdin"
|
assert not tmpfile, "cannot set tmpfile with stdin"
|
||||||
tmpfile = open(os.devnull, "r")
|
tmpfile = open(os.devnull, "r")
|
||||||
|
@ -568,7 +570,7 @@ class FDCaptureBinary:
|
||||||
self.targetfd, getattr(self, "targetfd_save", None), self._state
|
self.targetfd, getattr(self, "targetfd_save", None), self._state
|
||||||
)
|
)
|
||||||
|
|
||||||
def start(self):
|
def _start(self):
|
||||||
""" Start capturing on targetfd using memorized tmpfile. """
|
""" Start capturing on targetfd using memorized tmpfile. """
|
||||||
try:
|
try:
|
||||||
os.fstat(self.targetfd_save)
|
os.fstat(self.targetfd_save)
|
||||||
|
@ -585,7 +587,7 @@ class FDCaptureBinary:
|
||||||
self.tmpfile.truncate()
|
self.tmpfile.truncate()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def done(self):
|
def _done(self):
|
||||||
""" stop capturing, restore streams, return original capture file,
|
""" stop capturing, restore streams, return original capture file,
|
||||||
seeked to position zero. """
|
seeked to position zero. """
|
||||||
targetfd_save = self.__dict__.pop("targetfd_save")
|
targetfd_save = self.__dict__.pop("targetfd_save")
|
||||||
|
@ -618,7 +620,8 @@ class FDCapture(FDCaptureBinary):
|
||||||
snap() produces text
|
snap() produces text
|
||||||
"""
|
"""
|
||||||
|
|
||||||
EMPTY_BUFFER = str()
|
# Ignore type because it doesn't match the type in the superclass (bytes).
|
||||||
|
EMPTY_BUFFER = str() # type: ignore
|
||||||
|
|
||||||
def snap(self):
|
def snap(self):
|
||||||
res = super().snap()
|
res = super().snap()
|
||||||
|
@ -679,7 +682,8 @@ class SysCapture:
|
||||||
|
|
||||||
|
|
||||||
class SysCaptureBinary(SysCapture):
|
class SysCaptureBinary(SysCapture):
|
||||||
EMPTY_BUFFER = b""
|
# Ignore type because it doesn't match the type in the superclass (str).
|
||||||
|
EMPTY_BUFFER = b"" # type: ignore
|
||||||
|
|
||||||
def snap(self):
|
def snap(self):
|
||||||
res = self.tmpfile.buffer.getvalue()
|
res = self.tmpfile.buffer.getvalue()
|
||||||
|
|
|
@ -74,7 +74,7 @@ class pytestPDB:
|
||||||
|
|
||||||
_pluginmanager = None
|
_pluginmanager = None
|
||||||
_config = None
|
_config = None
|
||||||
_saved = []
|
_saved = [] # type: list
|
||||||
_recursive_debug = 0
|
_recursive_debug = 0
|
||||||
_wrapped_pdb_cls = None
|
_wrapped_pdb_cls = None
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from typing import Dict
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import py
|
import py
|
||||||
|
@ -31,6 +33,9 @@ from _pytest.deprecated import FIXTURE_NAMED_REQUEST
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import TEST_OUTCOME
|
from _pytest.outcomes import TEST_OUTCOME
|
||||||
|
|
||||||
|
if False: # TYPE_CHECKING
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
|
||||||
@attr.s(frozen=True)
|
@attr.s(frozen=True)
|
||||||
class PseudoFixtureDef:
|
class PseudoFixtureDef:
|
||||||
|
@ -54,10 +59,10 @@ def pytest_sessionstart(session):
|
||||||
session._fixturemanager = FixtureManager(session)
|
session._fixturemanager = FixtureManager(session)
|
||||||
|
|
||||||
|
|
||||||
scopename2class = {}
|
scopename2class = {} # type: Dict[str, Type[nodes.Node]]
|
||||||
|
|
||||||
|
|
||||||
scope2props = dict(session=())
|
scope2props = dict(session=()) # type: Dict[str, Tuple[str, ...]]
|
||||||
scope2props["package"] = ("fspath",)
|
scope2props["package"] = ("fspath",)
|
||||||
scope2props["module"] = ("fspath", "module")
|
scope2props["module"] = ("fspath", "module")
|
||||||
scope2props["class"] = scope2props["module"] + ("cls",)
|
scope2props["class"] = scope2props["module"] + ("cls",)
|
||||||
|
@ -960,7 +965,8 @@ class FixtureFunctionMarker:
|
||||||
scope = attr.ib()
|
scope = attr.ib()
|
||||||
params = attr.ib(converter=attr.converters.optional(tuple))
|
params = attr.ib(converter=attr.converters.optional(tuple))
|
||||||
autouse = attr.ib(default=False)
|
autouse = attr.ib(default=False)
|
||||||
ids = attr.ib(default=None, converter=_ensure_immutable_ids)
|
# Ignore type because of https://github.com/python/mypy/issues/6172.
|
||||||
|
ids = attr.ib(default=None, converter=_ensure_immutable_ids) # type: ignore
|
||||||
name = attr.ib(default=None)
|
name = attr.ib(default=None)
|
||||||
|
|
||||||
def __call__(self, function):
|
def __call__(self, function):
|
||||||
|
|
|
@ -91,7 +91,8 @@ def pytest_cmdline_main(config):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
pytest_cmdline_main.tryfirst = True
|
# Ignore type because of https://github.com/python/mypy/issues/2087.
|
||||||
|
pytest_cmdline_main.tryfirst = True # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def deselect_by_keyword(items, config):
|
def deselect_by_keyword(items, config):
|
||||||
|
|
|
@ -3,6 +3,7 @@ import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
|
@ -298,7 +299,7 @@ class MarkGenerator:
|
||||||
on the ``test_function`` object. """
|
on the ``test_function`` object. """
|
||||||
|
|
||||||
_config = None
|
_config = None
|
||||||
_markers = set()
|
_markers = set() # type: Set[str]
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name[0] == "_":
|
if name[0] == "_":
|
||||||
|
|
|
@ -280,7 +280,8 @@ class Node:
|
||||||
truncate_locals=truncate_locals,
|
truncate_locals=truncate_locals,
|
||||||
)
|
)
|
||||||
|
|
||||||
repr_failure = _repr_failure_py
|
def repr_failure(self, excinfo, style=None):
|
||||||
|
return self._repr_failure_py(excinfo, style)
|
||||||
|
|
||||||
|
|
||||||
def get_fslocation_from_item(item):
|
def get_fslocation_from_item(item):
|
||||||
|
|
|
@ -70,7 +70,8 @@ def exit(msg, returncode=None):
|
||||||
raise Exit(msg, returncode)
|
raise Exit(msg, returncode)
|
||||||
|
|
||||||
|
|
||||||
exit.Exception = Exit
|
# Ignore type because of https://github.com/python/mypy/issues/2087.
|
||||||
|
exit.Exception = Exit # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def skip(msg="", *, allow_module_level=False):
|
def skip(msg="", *, allow_module_level=False):
|
||||||
|
@ -96,7 +97,8 @@ def skip(msg="", *, allow_module_level=False):
|
||||||
raise Skipped(msg=msg, allow_module_level=allow_module_level)
|
raise Skipped(msg=msg, allow_module_level=allow_module_level)
|
||||||
|
|
||||||
|
|
||||||
skip.Exception = Skipped
|
# Ignore type because of https://github.com/python/mypy/issues/2087.
|
||||||
|
skip.Exception = Skipped # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def fail(msg="", pytrace=True):
|
def fail(msg="", pytrace=True):
|
||||||
|
@ -111,7 +113,8 @@ def fail(msg="", pytrace=True):
|
||||||
raise Failed(msg=msg, pytrace=pytrace)
|
raise Failed(msg=msg, pytrace=pytrace)
|
||||||
|
|
||||||
|
|
||||||
fail.Exception = Failed
|
# Ignore type because of https://github.com/python/mypy/issues/2087.
|
||||||
|
fail.Exception = Failed # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class XFailed(Failed):
|
class XFailed(Failed):
|
||||||
|
@ -132,7 +135,8 @@ def xfail(reason=""):
|
||||||
raise XFailed(reason)
|
raise XFailed(reason)
|
||||||
|
|
||||||
|
|
||||||
xfail.Exception = XFailed
|
# Ignore type because of https://github.com/python/mypy/issues/2087.
|
||||||
|
xfail.Exception = XFailed # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def importorskip(modname, minversion=None, reason=None):
|
def importorskip(modname, minversion=None, reason=None):
|
||||||
|
|
|
@ -9,6 +9,7 @@ from collections.abc import Sized
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from itertools import filterfalse
|
from itertools import filterfalse
|
||||||
from numbers import Number
|
from numbers import Number
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from more_itertools.more import always_iterable
|
from more_itertools.more import always_iterable
|
||||||
|
|
||||||
|
@ -58,7 +59,8 @@ class ApproxBase:
|
||||||
a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual)
|
a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual)
|
||||||
)
|
)
|
||||||
|
|
||||||
__hash__ = None
|
# Ignore type because of https://github.com/python/mypy/issues/4266.
|
||||||
|
__hash__ = None # type: ignore
|
||||||
|
|
||||||
def __ne__(self, actual):
|
def __ne__(self, actual):
|
||||||
return not (actual == self)
|
return not (actual == self)
|
||||||
|
@ -202,8 +204,10 @@ class ApproxScalar(ApproxBase):
|
||||||
Perform approximate comparisons where the expected value is a single number.
|
Perform approximate comparisons where the expected value is a single number.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
|
# Using Real should be better than this Union, but not possible yet:
|
||||||
DEFAULT_RELATIVE_TOLERANCE = 1e-6
|
# https://github.com/python/typeshed/pull/3108
|
||||||
|
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12 # type: Union[float, Decimal]
|
||||||
|
DEFAULT_RELATIVE_TOLERANCE = 1e-6 # type: Union[float, Decimal]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -261,7 +265,8 @@ class ApproxScalar(ApproxBase):
|
||||||
# Return true if the two numbers are within the tolerance.
|
# Return true if the two numbers are within the tolerance.
|
||||||
return abs(self.expected - actual) <= self.tolerance
|
return abs(self.expected - actual) <= self.tolerance
|
||||||
|
|
||||||
__hash__ = None
|
# Ignore type because of https://github.com/python/mypy/issues/4266.
|
||||||
|
__hash__ = None # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tolerance(self):
|
def tolerance(self):
|
||||||
|
@ -691,7 +696,7 @@ def raises(expected_exception, *args, **kwargs):
|
||||||
fail(message)
|
fail(message)
|
||||||
|
|
||||||
|
|
||||||
raises.Exception = fail.Exception
|
raises.Exception = fail.Exception # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class RaisesContext:
|
class RaisesContext:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ def getslaveinfoline(node):
|
||||||
|
|
||||||
|
|
||||||
class BaseReport:
|
class BaseReport:
|
||||||
when = None
|
when = None # type: Optional[str]
|
||||||
location = None
|
location = None
|
||||||
|
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw):
|
||||||
|
|
|
@ -26,7 +26,10 @@ class TempPathFactory:
|
||||||
# using os.path.abspath() to get absolute path instead of resolve() as it
|
# using os.path.abspath() to get absolute path instead of resolve() as it
|
||||||
# does not work the same in all platforms (see #4427)
|
# does not work the same in all platforms (see #4427)
|
||||||
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
|
# Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012)
|
||||||
converter=attr.converters.optional(lambda p: Path(os.path.abspath(str(p))))
|
# Ignore type because of https://github.com/python/mypy/issues/6172.
|
||||||
|
converter=attr.converters.optional(
|
||||||
|
lambda p: Path(os.path.abspath(str(p))) # type: ignore
|
||||||
|
)
|
||||||
)
|
)
|
||||||
_trace = attr.ib()
|
_trace = attr.ib()
|
||||||
_basetemp = attr.ib(default=None)
|
_basetemp = attr.ib(default=None)
|
||||||
|
|
|
@ -589,7 +589,8 @@ raise ValueError()
|
||||||
|
|
||||||
def test_repr_local_with_exception_in_class_property(self):
|
def test_repr_local_with_exception_in_class_property(self):
|
||||||
class ExceptionWithBrokenClass(Exception):
|
class ExceptionWithBrokenClass(Exception):
|
||||||
@property
|
# Type ignored because it's bypassed intentionally.
|
||||||
|
@property # type: ignore
|
||||||
def __class__(self):
|
def __class__(self):
|
||||||
raise TypeError("boom!")
|
raise TypeError("boom!")
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,8 @@ class TestRaises:
|
||||||
"""Test current behavior with regard to exceptions via __class__ (#4284)."""
|
"""Test current behavior with regard to exceptions via __class__ (#4284)."""
|
||||||
|
|
||||||
class CrappyClass(Exception):
|
class CrappyClass(Exception):
|
||||||
@property
|
# Type ignored because it's bypassed intentionally.
|
||||||
|
@property # type: ignore
|
||||||
def __class__(self):
|
def __class__(self):
|
||||||
assert False, "via __class__"
|
assert False, "via __class__"
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,8 @@ def test_safe_isclass():
|
||||||
assert safe_isclass(type) is True
|
assert safe_isclass(type) is True
|
||||||
|
|
||||||
class CrappyClass(Exception):
|
class CrappyClass(Exception):
|
||||||
@property
|
# Type ignored because it's bypassed intentionally.
|
||||||
|
@property # type: ignore
|
||||||
def __class__(self):
|
def __class__(self):
|
||||||
assert False, "Should be ignored"
|
assert False, "Should be ignored"
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ import pytest
|
||||||
from _pytest.debugging import _validate_usepdb_cls
|
from _pytest.debugging import _validate_usepdb_cls
|
||||||
|
|
||||||
try:
|
try:
|
||||||
breakpoint
|
# Type ignored for Python <= 3.6.
|
||||||
|
breakpoint # type: ignore
|
||||||
except NameError:
|
except NameError:
|
||||||
SUPPORTS_BREAKPOINT_BUILTIN = False
|
SUPPORTS_BREAKPOINT_BUILTIN = False
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue