-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
conversion of exit codes to enum + exposure #5420
Changes from all commits
2b92fee
2bd619e
065fa17
103d614
8b3b10b
1cfea5f
ab6ed38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
``Session.exitcode`` values are now coded in ``pytest.ExitCode``, an ``IntEnum``. This makes the exit code available for consumer code and are more explicit other than just documentation. User defined exit codes are still valid, but should be used with caution. | ||
|
||
The team doesn't expect this change to break test suites or plugins in general, except in esoteric/specific scenarios. | ||
|
||
**pytest-xdist** users should upgrade to ``1.29.0`` or later, as ``pytest-xdist`` required a compatibility fix because of this change. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
""" core implementation of testing process: init, session, runtest loop. """ | ||
import enum | ||
import fnmatch | ||
import functools | ||
import os | ||
|
@@ -18,13 +19,26 @@ | |
from _pytest.outcomes import exit | ||
from _pytest.runner import collect_one_node | ||
|
||
# exitcodes for the command line | ||
EXIT_OK = 0 | ||
EXIT_TESTSFAILED = 1 | ||
EXIT_INTERRUPTED = 2 | ||
EXIT_INTERNALERROR = 3 | ||
EXIT_USAGEERROR = 4 | ||
EXIT_NOTESTSCOLLECTED = 5 | ||
|
||
class ExitCode(enum.IntEnum): | ||
RonnyPfannschmidt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just occurred to me: is there a consensus about how to name the enumerate members? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm all examples in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. using PEP8 for constants There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. basically enum members are constants, thus follow the same rules as constants ^^ |
||
#: an internal error got in the way | ||
INTERNAL_ERROR = 3 | ||
#: pytest was missused | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. *misused |
||
USAGE_ERROR = 4 | ||
#: pytest couldnt find tests | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. *couldn't |
||
NO_TESTS_COLLECTED = 5 | ||
|
||
|
||
def pytest_addoption(parser): | ||
|
@@ -188,7 +202,7 @@ def pytest_configure(config): | |
def wrap_session(config, doit): | ||
"""Skeleton command line program""" | ||
session = Session(config) | ||
session.exitstatus = EXIT_OK | ||
session.exitstatus = ExitCode.OK | ||
initstate = 0 | ||
try: | ||
try: | ||
|
@@ -198,13 +212,13 @@ def wrap_session(config, doit): | |
initstate = 2 | ||
session.exitstatus = doit(config, session) or 0 | ||
except UsageError: | ||
session.exitstatus = EXIT_USAGEERROR | ||
session.exitstatus = ExitCode.USAGE_ERROR | ||
raise | ||
except Failed: | ||
session.exitstatus = EXIT_TESTSFAILED | ||
session.exitstatus = ExitCode.TESTS_FAILED | ||
except (KeyboardInterrupt, exit.Exception): | ||
excinfo = _pytest._code.ExceptionInfo.from_current() | ||
exitstatus = EXIT_INTERRUPTED | ||
exitstatus = ExitCode.INTERRUPTED | ||
if isinstance(excinfo.value, exit.Exception): | ||
if excinfo.value.returncode is not None: | ||
exitstatus = excinfo.value.returncode | ||
|
@@ -217,7 +231,7 @@ def wrap_session(config, doit): | |
except: # noqa | ||
excinfo = _pytest._code.ExceptionInfo.from_current() | ||
config.notify_exception(excinfo, config.option) | ||
session.exitstatus = EXIT_INTERNALERROR | ||
session.exitstatus = ExitCode.INTERNAL_ERROR | ||
if excinfo.errisinstance(SystemExit): | ||
sys.stderr.write("mainloop: caught unexpected SystemExit!\n") | ||
|
||
|
@@ -243,9 +257,9 @@ def _main(config, session): | |
config.hook.pytest_runtestloop(session=session) | ||
|
||
if session.testsfailed: | ||
return EXIT_TESTSFAILED | ||
return ExitCode.TESTS_FAILED | ||
elif session.testscollected == 0: | ||
return EXIT_NOTESTSCOLLECTED | ||
return ExitCode.NO_TESTS_COLLECTED | ||
|
||
|
||
def pytest_collection(session): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,8 +19,7 @@ | |
from _pytest.assertion.rewrite import AssertionRewritingHook | ||
from _pytest.capture import MultiCapture | ||
from _pytest.capture import SysCapture | ||
from _pytest.main import EXIT_INTERRUPTED | ||
from _pytest.main import EXIT_OK | ||
from _pytest.main import ExitCode | ||
from _pytest.main import Session | ||
from _pytest.monkeypatch import MonkeyPatch | ||
from _pytest.pathlib import Path | ||
|
@@ -691,7 +690,7 @@ def getnode(self, config, arg): | |
p = py.path.local(arg) | ||
config.hook.pytest_sessionstart(session=session) | ||
res = session.perform_collect([str(p)], genitems=False)[0] | ||
config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK) | ||
config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) | ||
return res | ||
|
||
def getpathnode(self, path): | ||
|
@@ -708,11 +707,11 @@ def getpathnode(self, path): | |
x = session.fspath.bestrelpath(path) | ||
config.hook.pytest_sessionstart(session=session) | ||
res = session.perform_collect([x], genitems=False)[0] | ||
config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK) | ||
config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) | ||
return res | ||
|
||
def genitems(self, colitems): | ||
"""Generate all test items from a collection node. | ||
"""Generate all test items from a collection node.src/_pytest/main.py | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. paste error? |
||
|
||
This recurses into the collection node and returns a list of all the | ||
test items contained within. | ||
|
@@ -841,7 +840,7 @@ class reprec: | |
|
||
# typically we reraise keyboard interrupts from the child run | ||
# because it's our user requesting interruption of the testing | ||
if ret == EXIT_INTERRUPTED and not no_reraise_ctrlc: | ||
if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: | ||
calls = reprec.getcalls("pytest_keyboard_interrupt") | ||
if calls and calls[-1].excinfo.type == KeyboardInterrupt: | ||
raise KeyboardInterrupt() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*represented