From d33da078a8a661c61d9453c282f0b86cc1455d05 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 10 Feb 2020 23:43:30 +0200 Subject: [PATCH] Move ExitCode's definition from _pytest.main to _pytest.config ExitCode is used in several internal modules and hooks and so with type annotations added, needs to be imported a lot. _pytest.main, being the entry point, generally sits at the top of the import tree. So, it's not great to have ExitCode defined in _pytest.main, because it will cause a lot of import cycles once type annotations are added (in fact there is already one, which this change removes). Move it to _pytest.config instead. _pytest.main still imports ExitCode, so importing from there still works, although external users should really be importing from `pytest`. --- doc/en/reference.rst | 2 +- doc/en/usage.rst | 2 +- src/_pytest/config/__init__.py | 28 +++++++++++++++++++++++++--- src/_pytest/main.py | 25 +------------------------ src/_pytest/pytester.py | 2 +- src/_pytest/terminal.py | 2 +- src/pytest/__init__.py | 2 +- testing/acceptance_test.py | 4 ++-- testing/python/collect.py | 2 +- testing/test_assertrewrite.py | 2 +- testing/test_cacheprovider.py | 2 +- testing/test_capture.py | 2 +- testing/test_collection.py | 2 +- testing/test_config.py | 4 ++-- testing/test_conftest.py | 2 +- testing/test_helpconfig.py | 2 +- testing/test_main.py | 2 +- testing/test_mark.py | 2 +- testing/test_pluginmanager.py | 2 +- testing/test_pytester.py | 2 +- testing/test_runner.py | 8 ++++---- testing/test_session.py | 2 +- testing/test_setuponly.py | 2 +- testing/test_terminal.py | 2 +- testing/test_unittest.py | 2 +- 25 files changed, 54 insertions(+), 55 deletions(-) diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 7d4f1dafa..d9b0b4c8d 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -738,7 +738,7 @@ ExceptionInfo pytest.ExitCode ~~~~~~~~~~~~~~~ -.. autoclass:: _pytest.main.ExitCode +.. autoclass:: _pytest.config.ExitCode :members: diff --git a/doc/en/usage.rst b/doc/en/usage.rst index ff8a1748f..961e813cf 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -33,7 +33,7 @@ Running ``pytest`` can result in six different exit codes: :Exit code 4: pytest command line usage error :Exit code 5: No tests were collected -They are represented by the :class:`_pytest.main.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using: +They are represented by the :class:`_pytest.config.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using: .. code-block:: python diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 6fbbf1959..b37ee7916 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1,6 +1,7 @@ """ command line options, ini-file and conftest.py processing. """ import argparse import copy +import enum import inspect import os import shlex @@ -61,6 +62,29 @@ hookimpl = HookimplMarker("pytest") hookspec = HookspecMarker("pytest") +class ExitCode(enum.IntEnum): + """ + .. versionadded:: 5.0 + + Encodes the valid exit codes by pytest. + + Currently users and plugins may supply other exit codes as well. + """ + + #: tests passed + OK = 0 + #: tests failed + TESTS_FAILED = 1 + #: pytest was interrupted + INTERRUPTED = 2 + #: an internal error got in the way + INTERNAL_ERROR = 3 + #: pytest was misused + USAGE_ERROR = 4 + #: pytest couldn't find tests + NO_TESTS_COLLECTED = 5 + + class ConftestImportFailure(Exception): def __init__(self, path, excinfo): Exception.__init__(self, path, excinfo) @@ -68,7 +92,7 @@ class ConftestImportFailure(Exception): self.excinfo = excinfo # type: Tuple[Type[Exception], Exception, TracebackType] -def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]": +def main(args=None, plugins=None) -> Union[int, ExitCode]: """ return exit code, after performing an in-process test run. :arg args: list of command line arguments. @@ -76,8 +100,6 @@ def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]": :arg plugins: list of plugin objects to be auto-registered during initialization. """ - from _pytest.main import ExitCode - try: try: config = _prepareconfig(args, plugins) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 1db97dc55..ea1c48f70 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -1,5 +1,4 @@ """ core implementation of testing process: init, session, runtest loop. """ -import enum import fnmatch import functools import importlib @@ -21,6 +20,7 @@ from _pytest import nodes from _pytest.compat import TYPE_CHECKING from _pytest.config import Config from _pytest.config import directory_arg +from _pytest.config import ExitCode from _pytest.config import hookimpl from _pytest.config import UsageError from _pytest.fixtures import FixtureManager @@ -36,29 +36,6 @@ if TYPE_CHECKING: from _pytest.python import Package -class ExitCode(enum.IntEnum): - """ - .. versionadded:: 5.0 - - Encodes the valid exit codes by pytest. - - Currently users and plugins may supply other exit codes as well. - """ - - #: tests passed - OK = 0 - #: tests failed - TESTS_FAILED = 1 - #: pytest was interrupted - INTERRUPTED = 2 - #: an internal error got in the way - INTERNAL_ERROR = 3 - #: pytest was misused - USAGE_ERROR = 4 - #: pytest couldn't find tests - NO_TESTS_COLLECTED = 5 - - def pytest_addoption(parser): parser.addini( "norecursedirs", diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 0b51f8bb0..08ed29fc8 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -30,8 +30,8 @@ from _pytest.capture import MultiCapture from _pytest.capture import SysCapture from _pytest.compat import TYPE_CHECKING from _pytest.config import _PluggyPlugin +from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest -from _pytest.main import ExitCode from _pytest.main import Session from _pytest.monkeypatch import MonkeyPatch from _pytest.nodes import Collector diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 713f6d91e..9f12015d6 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -26,7 +26,7 @@ from more_itertools import collapse import pytest from _pytest import nodes from _pytest.config import Config -from _pytest.main import ExitCode +from _pytest.config import ExitCode from _pytest.main import Session from _pytest.reports import CollectReport from _pytest.reports import TestReport diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 7b79603af..33bc3d0fb 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -6,6 +6,7 @@ from _pytest import __version__ from _pytest.assertion import register_assert_rewrite from _pytest.compat import _setup_collect_fakemodule from _pytest.config import cmdline +from _pytest.config import ExitCode from _pytest.config import hookimpl from _pytest.config import hookspec from _pytest.config import main @@ -15,7 +16,6 @@ from _pytest.fixtures import fillfixtures as _fillfuncargs from _pytest.fixtures import fixture from _pytest.fixtures import yield_fixture from _pytest.freeze_support import freeze_includes -from _pytest.main import ExitCode from _pytest.main import Session from _pytest.mark import MARK_GEN as mark from _pytest.mark import param diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 68e8a97f8..87cca494b 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -8,7 +8,7 @@ import py import pytest from _pytest.compat import importlib_metadata -from _pytest.main import ExitCode +from _pytest.config import ExitCode def prepend_pythonpath(*dirs): @@ -412,7 +412,7 @@ class TestGeneralUsage: def test_report_all_failed_collections_initargs(self, testdir): testdir.makeconftest( """ - from _pytest.main import ExitCode + from _pytest.config import ExitCode def pytest_sessionfinish(exitstatus): assert exitstatus == ExitCode.USAGE_ERROR diff --git a/testing/python/collect.py b/testing/python/collect.py index 072180226..460905860 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -4,7 +4,7 @@ import textwrap import _pytest._code import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode from _pytest.nodes import Collector diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 8490a59e6..017142255 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -21,7 +21,7 @@ from _pytest.assertion.rewrite import get_cache_dir from _pytest.assertion.rewrite import PYC_TAIL from _pytest.assertion.rewrite import PYTEST_TAG from _pytest.assertion.rewrite import rewrite_asserts -from _pytest.main import ExitCode +from _pytest.config import ExitCode from _pytest.pathlib import Path diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 2690a7de8..d37f18f0f 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -6,7 +6,7 @@ import sys import py import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode pytest_plugins = ("pytester",) diff --git a/testing/test_capture.py b/testing/test_capture.py index 1a70cb1a5..7447d9742 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -15,7 +15,7 @@ from typing import TextIO import pytest from _pytest import capture from _pytest.capture import CaptureManager -from _pytest.main import ExitCode +from _pytest.config import ExitCode # note: py.io capture tests where copied from # pylib 1.4.20.dev2 (rev 13d9af95547e) diff --git a/testing/test_collection.py b/testing/test_collection.py index 56f2efc84..20afa42d0 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -6,8 +6,8 @@ import textwrap import py import pytest +from _pytest.config import ExitCode from _pytest.main import _in_venv -from _pytest.main import ExitCode from _pytest.main import Session from _pytest.pytester import Testdir diff --git a/testing/test_config.py b/testing/test_config.py index 8a1be0b27..993d65a67 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -7,11 +7,11 @@ import pytest from _pytest.compat import importlib_metadata from _pytest.config import _iter_rewritable_modules from _pytest.config import Config +from _pytest.config import ExitCode from _pytest.config.exceptions import UsageError from _pytest.config.findpaths import determine_setup from _pytest.config.findpaths import get_common_ancestor from _pytest.config.findpaths import getcfg -from _pytest.main import ExitCode from _pytest.pathlib import Path @@ -1134,7 +1134,7 @@ class TestOverrideIniArgs: % (testdir.request.config._parser.optparser.prog,) ] ) - assert result.ret == _pytest.main.ExitCode.USAGE_ERROR + assert result.ret == _pytest.config.ExitCode.USAGE_ERROR def test_override_ini_does_not_contain_paths(self, _config_for_test, _sys_snapshot): """Check that -o no longer swallows all options after it (#3103)""" diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 9e893152d..a07af60f6 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -4,8 +4,8 @@ import textwrap import py import pytest +from _pytest.config import ExitCode from _pytest.config import PytestPluginManager -from _pytest.main import ExitCode from _pytest.pathlib import Path diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 1dee5b0f5..b96eeccc3 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -1,5 +1,5 @@ import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode def test_version(testdir, pytestconfig): diff --git a/testing/test_main.py b/testing/test_main.py index b47791b29..eea529f34 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -1,5 +1,5 @@ import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode @pytest.mark.parametrize( diff --git a/testing/test_mark.py b/testing/test_mark.py index 6ac4e0b23..76ee289b6 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -3,7 +3,7 @@ import sys from unittest import mock import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode from _pytest.mark import EMPTY_PARAMETERSET_OPTION from _pytest.mark import MarkGenerator as Mark from _pytest.nodes import Collector diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 43026f0a3..336f468a8 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -3,9 +3,9 @@ import sys import types import pytest +from _pytest.config import ExitCode from _pytest.config import PytestPluginManager from _pytest.config.exceptions import UsageError -from _pytest.main import ExitCode from _pytest.main import Session diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 8b69ce124..3c9e92e3f 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -8,8 +8,8 @@ import py.path import _pytest.pytester as pytester import pytest +from _pytest.config import ExitCode from _pytest.config import PytestPluginManager -from _pytest.main import ExitCode from _pytest.outcomes import Failed from _pytest.pytester import CwdSnapshot from _pytest.pytester import HookRecorder diff --git a/testing/test_runner.py b/testing/test_runner.py index 1600b6b7c..246917950 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -10,10 +10,10 @@ import py import _pytest._code import pytest -from _pytest import main from _pytest import outcomes from _pytest import reports from _pytest import runner +from _pytest.config import ExitCode from _pytest.outcomes import Exit from _pytest.outcomes import Failed from _pytest.outcomes import OutcomeException @@ -681,7 +681,7 @@ def test_pytest_fail_notrace_non_ascii(testdir) -> None: def test_pytest_no_tests_collected_exit_status(testdir) -> None: result = testdir.runpytest() result.stdout.fnmatch_lines(["*collected 0 items*"]) - assert result.ret == main.ExitCode.NO_TESTS_COLLECTED + assert result.ret == ExitCode.NO_TESTS_COLLECTED testdir.makepyfile( test_foo=""" @@ -692,12 +692,12 @@ def test_pytest_no_tests_collected_exit_status(testdir) -> None: result = testdir.runpytest() result.stdout.fnmatch_lines(["*collected 1 item*"]) result.stdout.fnmatch_lines(["*1 passed*"]) - assert result.ret == main.ExitCode.OK + assert result.ret == ExitCode.OK result = testdir.runpytest("-k nonmatch") result.stdout.fnmatch_lines(["*collected 1 item*"]) result.stdout.fnmatch_lines(["*1 deselected*"]) - assert result.ret == main.ExitCode.NO_TESTS_COLLECTED + assert result.ret == ExitCode.NO_TESTS_COLLECTED def test_exception_printing_skip() -> None: diff --git a/testing/test_session.py b/testing/test_session.py index 7b4eb817a..1f17acbbd 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -1,5 +1,5 @@ import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode class SessionTests: diff --git a/testing/test_setuponly.py b/testing/test_setuponly.py index 7549874db..e26a33dee 100644 --- a/testing/test_setuponly.py +++ b/testing/test_setuponly.py @@ -1,5 +1,5 @@ import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode @pytest.fixture(params=["--setup-only", "--setup-plan", "--setup-show"], scope="module") diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 4733469ae..53ae6d9d6 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -12,7 +12,7 @@ import pluggy import py import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode from _pytest.pytester import Testdir from _pytest.reports import BaseReport from _pytest.terminal import _folded_skips diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 4b814532b..c5fc20239 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1,7 +1,7 @@ import gc import pytest -from _pytest.main import ExitCode +from _pytest.config import ExitCode def test_simple_unittest(testdir):