fix: propagate exit code (#108)

* fix(pytest): propagate exit code

* feat: derive exit code for unittest
This commit is contained in:
Salomon Popp
2026-08-18 16:39:57 +03:00
committed by Itai Bohadana
parent 7d7cacb91a
commit b720009f3e
6 changed files with 21 additions and 20 deletions
+1 -1
View File
@@ -17,4 +17,4 @@ with add_to_path():
from neotest_python import main from neotest_python import main
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv[1:]) sys.exit(main(sys.argv[1:]))
+6 -6
View File
@@ -50,20 +50,18 @@ parser.add_argument(
parser.add_argument("args", nargs="*") parser.add_argument("args", nargs="*")
def main(argv: List[str]): def main(argv: List[str]) -> int:
if "--pytest-collect" in argv: if "--pytest-collect" in argv:
argv.remove("--pytest-collect") argv.remove("--pytest-collect")
from .pytest_ import collect from .pytest_ import collect
collect(argv) return collect(argv)
return
if "--pytest-extract-test-name-template" in argv: if "--pytest-extract-test-name-template" in argv:
argv.remove("--pytest-extract-test-name-template") argv.remove("--pytest-extract-test-name-template")
from .pytest_ import extract_test_name_template from .pytest_ import extract_test_name_template
extract_test_name_template(argv) return extract_test_name_template(argv)
return
args = parser.parse_args(argv) args = parser.parse_args(argv)
adapter = get_adapter(TestRunner(args.runner), args.emit_parameterized_ids) adapter = get_adapter(TestRunner(args.runner), args.emit_parameterized_ids)
@@ -74,7 +72,9 @@ def main(argv: List[str]):
stream_file.write(json.dumps({"id": pos_id, "result": result}) + "\n") stream_file.write(json.dumps({"id": pos_id, "result": result}) + "\n")
stream_file.flush() stream_file.flush()
results = adapter.run(args.args, stream) results, exit_code = adapter.run(args.args, stream)
with open(args.results_file, "w") as results_file: with open(args.results_file, "w") as results_file:
json.dump(results, results_file) json.dump(results, results_file)
return exit_code
+2 -2
View File
@@ -1,6 +1,6 @@
import abc import abc
from enum import Enum from enum import Enum
from typing import TYPE_CHECKING, Callable, Dict, List, Optional from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
class NeotestResultStatus(str, Enum): class NeotestResultStatus(str, Enum):
@@ -43,6 +43,6 @@ class NeotestAdapter(abc.ABC):
} }
@abc.abstractmethod @abc.abstractmethod
def run(self, args: List[str], stream: Callable): def run(self, args: List[str], stream: Callable) -> Tuple[Dict, int]:
del args, stream del args, stream
raise NotImplementedError raise NotImplementedError
+4 -3
View File
@@ -55,7 +55,7 @@ class DjangoNeotestAdapter(CaseUtilsMixin, NeotestAdapter):
relative_dotted = relative_stem.replace(os.sep, ".") relative_dotted = relative_stem.replace(os.sep, ".")
return [*args, ".".join([relative_dotted, *child_ids])] return [*args, ".".join([relative_dotted, *child_ids])]
def run(self, args: List[str], _) -> Dict: def run(self, args: List[str], _) -> Tuple[Dict, int]:
errs: Dict[str, Tuple[Exception, Any, TracebackType]] = {} errs: Dict[str, Tuple[Exception, Any, TracebackType]] = {}
results = {} results = {}
@@ -146,5 +146,6 @@ class DjangoNeotestAdapter(CaseUtilsMixin, NeotestAdapter):
runner = DjangoUnittestRunner( runner = DjangoUnittestRunner(
**vars(parser.parse_args(argv[1:-1])) # parse plugin config args **vars(parser.parse_args(argv[1:-1])) # parse plugin config args
) )
runner.run_tests(test_labels=[argv[-1]]) # pass test label failures = runner.run_tests(test_labels=[argv[-1]]) # pass test label
return results exit_code = 0 if failures == 0 else 1
return results, exit_code
+4 -4
View File
@@ -4,7 +4,7 @@ from typing import Callable, Dict, List, Optional, Union
from . import params_getter from . import params_getter
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from typing import Callable, Dict, Generator, List, Optional, Union from typing import Callable, Dict, Generator, List, Optional, Tuple, Union
import pytest import pytest
from _pytest._code.code import ExceptionRepr from _pytest._code.code import ExceptionRepr
@@ -24,18 +24,18 @@ class PytestNeotestAdapter(NeotestAdapter):
self, self,
args: List[str], args: List[str],
stream: Callable[[str, NeotestResult], None], stream: Callable[[str, NeotestResult], None],
) -> Dict[str, NeotestResult]: ) -> Tuple[Dict[str, NeotestResult], int]:
result_collector = NeotestResultCollector( result_collector = NeotestResultCollector(
self, stream=stream, emit_parameterized_ids=self.emit_parameterized_ids self, stream=stream, emit_parameterized_ids=self.emit_parameterized_ids
) )
pytest.main( exit_code = pytest.main(
args=args, args=args,
plugins=[ plugins=[
result_collector, result_collector,
NeotestDebugpyPlugin(), NeotestDebugpyPlugin(),
], ],
) )
return result_collector.results return result_collector.results, int(exit_code)
class NeotestResultCollector: class NeotestResultCollector:
+4 -4
View File
@@ -45,7 +45,7 @@ class UnittestNeotestAdapter(NeotestAdapter):
return [*args, ".".join([relative_dotted, *child_ids])] return [*args, ".".join([relative_dotted, *child_ids])]
# TODO: Stream results # TODO: Stream results
def run(self, args: List[str], _) -> Dict: def run(self, args: List[str], _) -> Tuple[Dict, int]:
results = {} results = {}
errs: Dict[str, Tuple[Exception, Any, TracebackType]] = {} errs: Dict[str, Tuple[Exception, Any, TracebackType]] = {}
@@ -96,11 +96,11 @@ class UnittestNeotestAdapter(NeotestAdapter):
# Prepend an executable name which is just used in output # Prepend an executable name which is just used in output
argv = ["neotest-python"] + self.convert_args(args[-1], args[:-1]) argv = ["neotest-python"] + self.convert_args(args[-1], args[:-1])
unittest.main( program = unittest.main(
module=None, module=None,
argv=argv, argv=argv,
testRunner=NeotestUnittestRunner(resultclass=NeotestTextTestResult), testRunner=NeotestUnittestRunner(resultclass=NeotestTextTestResult),
exit=False, exit=False,
) )
exit_code = 0 if program.result.wasSuccessful() else 1
return results return results, exit_code