From e5e47c1097e6f9e7bd30e28d508dca489f0629c6 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 17 Oct 2020 18:18:24 +0300 Subject: [PATCH 1/3] Fix typing related to iniconfig iniconfig now has typing stubs which reveal a couple issues. --- .pre-commit-config.yaml | 2 ++ src/_pytest/config/findpaths.py | 2 +- src/_pytest/pytester.py | 9 +++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 75941dcd9..1574651c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,6 +54,8 @@ repos: - id: mypy files: ^(src/|testing/) args: [] + additional_dependencies: + - iniconfig>=1.1.0 - repo: local hooks: - id: rst diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 04fa8f375..2edf54536 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -27,7 +27,7 @@ def _parse_ini_config(path: Path) -> iniconfig.IniConfig: Raise UsageError if the file cannot be parsed. """ try: - return iniconfig.IniConfig(path) + return iniconfig.IniConfig(str(path)) except iniconfig.ParseError as exc: raise UsageError(str(exc)) from exc diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index e3094a9df..a4142037d 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -32,6 +32,7 @@ from weakref import WeakKeyDictionary import attr import py from iniconfig import IniConfig +from iniconfig import SectionWrapper import pytest from _pytest import timing @@ -785,10 +786,10 @@ class Pytester: """Write a tox.ini file with 'source' as contents.""" return self.makefile(".ini", tox=source) - def getinicfg(self, source: str) -> IniConfig: + def getinicfg(self, source: str) -> SectionWrapper: """Return the pytest section from the tox.ini config file.""" p = self.makeini(source) - return IniConfig(p)["pytest"] + return IniConfig(str(p))["pytest"] def makepyprojecttoml(self, source: str) -> Path: """Write a pyproject.toml file with 'source' as contents. @@ -1541,9 +1542,9 @@ class Testdir: """See :meth:`Pytester.makeini`.""" return py.path.local(str(self._pytester.makeini(source))) - def getinicfg(self, source) -> py.path.local: + def getinicfg(self, source: str) -> SectionWrapper: """See :meth:`Pytester.getinicfg`.""" - return py.path.local(str(self._pytester.getinicfg(source))) + return self._pytester.getinicfg(source) def makepyprojecttoml(self, source) -> py.path.local: """See :meth:`Pytester.makepyprojecttoml`.""" From 1b23a111d2056e56c4a9038bc272e6f1f1698f85 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 17 Oct 2020 16:54:54 +0300 Subject: [PATCH 2/3] Update mypy 0.782 -> 0.790 --- .pre-commit-config.yaml | 2 +- setup.cfg | 2 +- src/_pytest/cacheprovider.py | 2 +- src/_pytest/capture.py | 3 +-- src/_pytest/compat.py | 5 ++--- src/_pytest/pytester.py | 4 +++- src/_pytest/python_api.py | 8 ++++---- src/_pytest/runner.py | 2 +- testing/test_assertrewrite.py | 2 +- testing/test_capture.py | 2 +- testing/test_debugging.py | 7 +------ testing/test_doctest.py | 2 +- testing/test_pastebin.py | 3 ++- 13 files changed, 20 insertions(+), 24 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1574651c8..2d351182e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,7 +49,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.782 # NOTE: keep this in sync with setup.cfg. + rev: v0.790 # NOTE: keep this in sync with setup.cfg. hooks: - id: mypy files: ^(src/|testing/) diff --git a/setup.cfg b/setup.cfg index 28ec061e0..134a80b3e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,7 +63,7 @@ console_scripts = [options.extras_require] checkqa-mypy = - mypy==0.780 + mypy==0.790 testing = argcomplete hypothesis>=3.56 diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 2c8128f61..20f0c71d3 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -382,7 +382,7 @@ class NFPlugin: self.cached_nodeids.update(item.nodeid for item in items) def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]: - return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) + return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) # type: ignore[no-any-return] def pytest_sessionfinish(self) -> None: config = self.config diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index dbb6d478f..4d314998c 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -380,8 +380,7 @@ class FDCaptureBinary: self.syscapture = SysCapture(targetfd) else: self.tmpfile = EncodedFile( - # TODO: Remove type ignore, fixed in next mypy release. - TemporaryFile(buffering=0), # type: ignore[arg-type] + TemporaryFile(buffering=0), encoding="utf-8", errors="replace", newline="", diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 0b6b1ca07..c7f86ea9c 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -150,9 +150,8 @@ def getfuncargnames( p.name for p in parameters.values() if ( - # 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] + p.kind is Parameter.POSITIONAL_OR_KEYWORD + or p.kind is Parameter.KEYWORD_ONLY ) and p.default is Parameter.empty ) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index a4142037d..dc234dc63 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1322,8 +1322,10 @@ class Pytester: """ __tracebackhide__ = True + # TODO: Remove type ignore in next mypy release. + # https://github.com/python/typeshed/pull/4582 cmdargs = tuple( - os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in cmdargs + os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in cmdargs # type: ignore[misc] ) p1 = self.path.joinpath("stdout") p2 = self.path.joinpath("stderr") diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index c4d029c0d..373435236 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -210,7 +210,7 @@ class ApproxScalar(ApproxBase): # tolerances, i.e. non-numerics and infinities. Need to call abs to # handle complex numbers, e.g. (inf + 1j). if (not isinstance(self.expected, (Complex, Decimal))) or math.isinf( - abs(self.expected) + abs(self.expected) # type: ignore[arg-type] ): return str(self.expected) @@ -253,8 +253,8 @@ class ApproxScalar(ApproxBase): # Allow the user to control whether NaNs are considered equal to each # other or not. The abs() calls are for compatibility with complex # numbers. - if math.isnan(abs(self.expected)): - return self.nan_ok and math.isnan(abs(actual)) + if math.isnan(abs(self.expected)): # type: ignore[arg-type] + return self.nan_ok and math.isnan(abs(actual)) # type: ignore[arg-type] # Infinity shouldn't be approximately equal to anything but itself, but # if there's a relative tolerance, it will be infinite and infinity @@ -262,7 +262,7 @@ class ApproxScalar(ApproxBase): # case would have been short circuited above, so here we can just # return false if the expected value is infinite. The abs() call is # for compatibility with complex numbers. - if math.isinf(abs(self.expected)): + if math.isinf(abs(self.expected)): # type: ignore[arg-type] return False # Return true if the two numbers are within the tolerance. diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index cce9bdd97..833d288f0 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -77,7 +77,7 @@ def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: dlist.append(rep) if not dlist: return - dlist.sort(key=lambda x: x.duration) + dlist.sort(key=lambda x: x.duration) # type: ignore[no-any-return] dlist.reverse() if not durations: tr.write_sep("=", "slowest durations") diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 9f0aa31d1..f95fd54b3 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1595,7 +1595,7 @@ class TestPyCacheDir: if prefix: if sys.version_info < (3, 8): pytest.skip("pycache_prefix not available in py<38") - monkeypatch.setattr(sys, "pycache_prefix", prefix) # type:ignore + monkeypatch.setattr(sys, "pycache_prefix", prefix) assert get_cache_dir(Path(source)) == Path(expected) diff --git a/testing/test_capture.py b/testing/test_capture.py index 7aeb2d8ac..e6bbc9a5d 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1606,7 +1606,7 @@ def test_stderr_write_returns_len(capsys): def test_encodedfile_writelines(tmpfile: BinaryIO) -> None: ef = capture.EncodedFile(tmpfile, encoding="utf-8") with pytest.raises(TypeError): - ef.writelines([b"line1", b"line2"]) + ef.writelines([b"line1", b"line2"]) # type: ignore[list-item] assert ef.writelines(["line3", "line4"]) is None # type: ignore[func-returns-value] ef.flush() tmpfile.seek(0) diff --git a/testing/test_debugging.py b/testing/test_debugging.py index f22d5d724..2760930ef 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -886,14 +886,9 @@ class TestPDB: class TestDebuggingBreakpoints: def test_supports_breakpoint_module_global(self): - """ - Test that supports breakpoint global marks on Python 3.7+ and not on - CPython 3.5, 2.7 - """ + """Test that supports breakpoint global marks on Python 3.7+.""" if sys.version_info >= (3, 7): assert SUPPORTS_BREAKPOINT_BUILTIN is True - if sys.version_info.major == 3 and sys.version_info.minor == 5: - assert SUPPORTS_BREAKPOINT_BUILTIN is False # type: ignore[comparison-overlap] @pytest.mark.skipif( not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 67e93b76a..8f31cb606 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1049,7 +1049,7 @@ class TestLiterals: ("1e3", "999"), # The current implementation doesn't understand that numbers inside # strings shouldn't be treated as numbers: - pytest.param("'3.1416'", "'3.14'", marks=pytest.mark.xfail), # type: ignore + pytest.param("'3.1416'", "'3.14'", marks=pytest.mark.xfail), ], ) def test_number_non_matches(self, pytester, expression, output): diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index 2a96f29a1..2a22f4056 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -1,3 +1,4 @@ +import io from typing import List from typing import Union @@ -95,7 +96,7 @@ class TestPaste: def mocked(url, data): calls.append((url, data)) - raise urllib.error.HTTPError(url, 400, "Bad request", None, None) + raise urllib.error.HTTPError(url, 400, "Bad request", {}, io.BytesIO()) monkeypatch.setattr(urllib.request, "urlopen", mocked) return calls From 09e38b1697b62490c9c2a9a1b2f11ed8c355e089 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 19 Oct 2020 00:00:22 +0300 Subject: [PATCH 3/3] runner: combine a sort+reverse to a sort(reverse=True) Suggested by Zac-HD. --- src/_pytest/runner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 833d288f0..794690ddb 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -77,8 +77,7 @@ def pytest_terminal_summary(terminalreporter: "TerminalReporter") -> None: dlist.append(rep) if not dlist: return - dlist.sort(key=lambda x: x.duration) # type: ignore[no-any-return] - dlist.reverse() + dlist.sort(key=lambda x: x.duration, reverse=True) # type: ignore[no-any-return] if not durations: tr.write_sep("=", "slowest durations") else: