From 8d98de8f8aef70a68ec98c1dd7ed0291efb37429 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 31 Jul 2020 09:46:56 +0300 Subject: [PATCH] typing: set no_implicit_reexport In Python, if module A defines a name `name`, and module B does `import name from A`, then another module C can `import name from B`. Sometimes it is intentional -- module B is meant to "reexport" `name`. But sometimes it is just confusion/inconsistency on where `name` should be imported from. mypy has a flag `--no-implicit-reexport` which puts some order into this. A name can only be imported from a module if 1. The module defines the name 2. The module's `__all__` includes the name 3. The module imports the name as `from ... import .. as name`. This flag is included in mypy's `--strict` flag. I like this flag, but I realize it is a bit controversial, and in particular item 3 above is a bit unfriendly to contributors who don't know about it. So I didn't intend to add it to pytest. But while investigating issue 7589 I came upon mypy issue 8754 which causes `--no-implicit-reexport` to leak into installed libraries and causes some unexpected typing differences *in pytest* if the user uses this flag. Since the diff mostly makes sense, let's just conform to it. --- setup.cfg | 1 + src/_pytest/_code/__init__.py | 2 +- src/_pytest/compat.py | 6 +++--- src/_pytest/config/__init__.py | 4 ++-- src/_pytest/mark/__init__.py | 9 ++++++++- src/_pytest/python.py | 4 ++-- testing/python/collect.py | 4 ++-- testing/python/fixtures.py | 17 +++++++++-------- testing/python/integration.py | 6 +++--- testing/python/metafunc.py | 12 +++++++----- testing/test_mark.py | 2 +- 11 files changed, 39 insertions(+), 28 deletions(-) diff --git a/setup.cfg b/setup.cfg index 31123f28e2e..4a86e7ec194 100644 --- a/setup.cfg +++ b/setup.cfg @@ -104,3 +104,4 @@ strict_equality = True warn_redundant_casts = True warn_return_any = True warn_unused_configs = True +no_implicit_reexport = True diff --git a/src/_pytest/_code/__init__.py b/src/_pytest/_code/__init__.py index 136da31959e..511d0dde661 100644 --- a/src/_pytest/_code/__init__.py +++ b/src/_pytest/_code/__init__.py @@ -4,9 +4,9 @@ from .code import filter_traceback from .code import Frame from .code import getfslineno -from .code import getrawcode from .code import Traceback from .code import TracebackEntry +from .source import getrawcode from .source import Source __all__ = [ diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index cd7dca7197a..4ff59e1fb5e 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -14,7 +14,7 @@ from typing import Callable from typing import Generic from typing import Optional -from typing import overload +from typing import overload as overload from typing import Tuple from typing import TypeVar from typing import Union @@ -208,7 +208,7 @@ def nullcontext(): else: - from contextlib import nullcontext # noqa + from contextlib import nullcontext as nullcontext # noqa: F401 def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]: @@ -363,7 +363,7 @@ def overload(f): # noqa: F811 if sys.version_info >= (3, 8): - from functools import cached_property + from functools import cached_property as cached_property else: class cached_property(Generic[_S, _T]): diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index f4e0d5d0fab..188cccd1a65 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -35,8 +35,8 @@ import _pytest._code import _pytest.deprecated import _pytest.hookspec # the extension point definitions -from .exceptions import PrintHelp -from .exceptions import UsageError +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError from .findpaths import determine_setup from _pytest._code import ExceptionInfo from _pytest._code import filter_traceback diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 5d71a772526..bc1dd1a709c 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -28,7 +28,14 @@ from _pytest.nodes import Item -__all__ = ["Mark", "MarkDecorator", "MarkGenerator", "get_empty_parameterset_mark"] +__all__ = [ + "MARK_GEN", + "Mark", + "MarkDecorator", + "MarkGenerator", + "ParameterSet", + "get_empty_parameterset_mark", +] old_mark_config_key = StoreKey[Optional[Config]]() diff --git a/src/_pytest/python.py b/src/_pytest/python.py index e2b6aef9c44..50f03eadb15 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -32,6 +32,7 @@ from _pytest._code import filter_traceback from _pytest._code import getfslineno from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter from _pytest._io.saferepr import saferepr from _pytest.compat import ascii_escaped @@ -66,7 +67,6 @@ from _pytest.pathlib import ImportPathMismatchError from _pytest.pathlib import parts from _pytest.pathlib import visit -from _pytest.reports import TerminalRepr from _pytest.warning_types import PytestCollectionWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning @@ -581,7 +581,7 @@ def _importtestmodule(self): "Traceback:\n" "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) ) from e - except _pytest.runner.Skipped as e: + except skip.Exception as e: if e.allow_module_level: raise raise self.CollectError( diff --git a/testing/python/collect.py b/testing/python/collect.py index f64a1462971..ed778c265ff 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1018,7 +1018,7 @@ def test_filter_traceback_generated_code(self) -> None: See: https://bitbucket.org/pytest-dev/py/issues/71 This fixes #995. """ - from _pytest.python import filter_traceback + from _pytest._code import filter_traceback try: ns = {} # type: Dict[str, Any] @@ -1038,7 +1038,7 @@ def test_filter_traceback_path_no_longer_valid(self, testdir) -> None: In this case, one of the files in the traceback no longer exists. This fixes #1133. """ - from _pytest.python import filter_traceback + from _pytest._code import filter_traceback testdir.syspathinsert() testdir.makepyfile( diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 119c7dedaf6..70370915b0e 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -3,6 +3,7 @@ import pytest from _pytest import fixtures +from _pytest.compat import getfuncargnames from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest from _pytest.pathlib import Path @@ -15,22 +16,22 @@ def test_getfuncargnames_functions(): def f(): raise NotImplementedError() - assert not fixtures.getfuncargnames(f) + assert not getfuncargnames(f) def g(arg): raise NotImplementedError() - assert fixtures.getfuncargnames(g) == ("arg",) + assert getfuncargnames(g) == ("arg",) def h(arg1, arg2="hello"): raise NotImplementedError() - assert fixtures.getfuncargnames(h) == ("arg1",) + assert getfuncargnames(h) == ("arg1",) def j(arg1, arg2, arg3="hello"): raise NotImplementedError() - assert fixtures.getfuncargnames(j) == ("arg1", "arg2") + assert getfuncargnames(j) == ("arg1", "arg2") def test_getfuncargnames_methods(): @@ -40,7 +41,7 @@ class A: def f(self, arg1, arg2="hello"): raise NotImplementedError() - assert fixtures.getfuncargnames(A().f) == ("arg1",) + assert getfuncargnames(A().f) == ("arg1",) def test_getfuncargnames_staticmethod(): @@ -51,7 +52,7 @@ class A: def static(arg1, arg2, x=1): raise NotImplementedError() - assert fixtures.getfuncargnames(A.static, cls=A) == ("arg1", "arg2") + assert getfuncargnames(A.static, cls=A) == ("arg1", "arg2") def test_getfuncargnames_partial(): @@ -64,7 +65,7 @@ def check(arg1, arg2, i): class T: test_ok = functools.partial(check, i=2) - values = fixtures.getfuncargnames(T().test_ok, name="test_ok") + values = getfuncargnames(T().test_ok, name="test_ok") assert values == ("arg1", "arg2") @@ -78,7 +79,7 @@ def check(arg1, arg2, i): class T: test_ok = staticmethod(functools.partial(check, i=2)) - values = fixtures.getfuncargnames(T().test_ok, name="test_ok") + values = getfuncargnames(T().test_ok, name="test_ok") assert values == ("arg1", "arg2") diff --git a/testing/python/integration.py b/testing/python/integration.py index 537057484d0..854593a65c0 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -1,8 +1,8 @@ from typing import Any import pytest -from _pytest import python from _pytest import runner +from _pytest._code import getfslineno class TestOEJSKITSpecials: @@ -87,8 +87,8 @@ def wrap(f): def wrapped_func(x, y, z): pass - fs, lineno = python.getfslineno(wrapped_func) - fs2, lineno2 = python.getfslineno(wrap) + fs, lineno = getfslineno(wrapped_func) + fs2, lineno2 = getfslineno(wrap) assert lineno > lineno2, "getfslineno does not unwrap correctly" diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 4e6cfaf91ba..d254dd3fb33 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -19,6 +19,8 @@ import pytest from _pytest import fixtures from _pytest import python +from _pytest.compat import _format_args +from _pytest.compat import getfuncargnames from _pytest.outcomes import fail from _pytest.pytester import Testdir from _pytest.python import _idval @@ -41,7 +43,7 @@ class DefinitionMock(python.FunctionDefinition): obj = attr.ib() _nodeid = attr.ib() - names = fixtures.getfuncargnames(func) + names = getfuncargnames(func) fixtureinfo = FuncFixtureInfoMock(names) # type: Any definition = DefinitionMock._create(func, "mock::nodeid") # type: Any return python.Metafunc(definition, fixtureinfo, config) @@ -954,22 +956,22 @@ def test_format_args(self) -> None: def function1(): pass - assert fixtures._format_args(function1) == "()" + assert _format_args(function1) == "()" def function2(arg1): pass - assert fixtures._format_args(function2) == "(arg1)" + assert _format_args(function2) == "(arg1)" def function3(arg1, arg2="qwe"): pass - assert fixtures._format_args(function3) == "(arg1, arg2='qwe')" + assert _format_args(function3) == "(arg1, arg2='qwe')" def function4(arg1, *args, **kwargs): pass - assert fixtures._format_args(function4) == "(arg1, *args, **kwargs)" + assert _format_args(function4) == "(arg1, *args, **kwargs)" class TestMetafuncFunctional: diff --git a/testing/test_mark.py b/testing/test_mark.py index f35660093e7..f00c1330e65 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -4,8 +4,8 @@ import pytest from _pytest.config import ExitCode -from _pytest.mark import EMPTY_PARAMETERSET_OPTION from _pytest.mark import MarkGenerator as Mark +from _pytest.mark.structures import EMPTY_PARAMETERSET_OPTION from _pytest.nodes import Collector from _pytest.nodes import Node