diff --git a/CHANGES.rst b/CHANGES.rst index bb38a4061..4d2af1432 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ These features will be included in the next release: Added ----- +- New exit codes 2 for file not found, 3 for invalid command line arguments, 4 for + missing dependencies and 123 for unknown failures. +- Display exit code in parentheses after error message. Fixed ----- diff --git a/constraints-oldest.txt b/constraints-oldest.txt index 624813769..2567a1575 100644 --- a/constraints-oldest.txt +++ b/constraints-oldest.txt @@ -4,13 +4,13 @@ # interpreter and Python ependencies. Keep this up-to-date with minimum # versions in `setup.cfg`. black==22.3.0 -darkgraylib==1.3.2 +darkgraylib==2.0.0 defusedxml==0.7.1 flake8-2020==1.6.1 flake8-bugbear==22.1.11 flake8-comprehensions==3.7.0 flynt==0.76 -graylint==1.1.2 +graylint==2.0.0 mypy==0.990 Pygments==2.4.0 pytest==6.2.0 diff --git a/setup.cfg b/setup.cfg index 49f5bc70a..be8eb3148 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,8 +29,8 @@ packages = find: install_requires = # NOTE: remember to keep `constraints-oldest.txt` in sync with these black>=22.3.0 - darkgraylib~=1.3.2 - graylint~=1.1.2 + darkgraylib~=2.0.0 + graylint~=2.0.0 toml>=0.10.0 # NOTE: remember to keep `.github/workflows/python-package.yml` in sync # with the minimum required Python version diff --git a/src/darker/__main__.py b/src/darker/__main__.py index ba6060015..cf4b72cad 100644 --- a/src/darker/__main__.py +++ b/src/darker/__main__.py @@ -34,6 +34,12 @@ from darker.import_sorting import apply_isort, isort from darker.utils import debug_dump, glob_any from darker.verification import ASTVerifier, BinarySearch, NotEquivalentError +from darkgraylib.command_line import ( + EXIT_CODE_CMDLINE_ERROR, + EXIT_CODE_DEPENDENCY, + EXIT_CODE_FILE_NOT_FOUND, + EXIT_CODE_UNKNOWN, +) from darkgraylib.config import show_config_if_debug from darkgraylib.files import find_project_root from darkgraylib.git import ( @@ -445,7 +451,8 @@ def _import_pygments(): # type: ignore return highlight, TerminalFormatter, PythonLexer -def main( # pylint: disable=too-many-locals,too-many-branches,too-many-statements +# pylint: disable=too-many-locals,too-many-branches,too-many-statements +def main( # noqa: C901,PLR0912,PLR0915 argv: List[str] = None, ) -> int: """Parse the command line and reformat and optionally lint each source file @@ -549,10 +556,8 @@ def main( # pylint: disable=too-many-locals,too-many-branches,too-many-statemen rev2_repr = ( "the working tree" if revrange.rev2 == WORKTREE else revrange.rev2 ) - raise ArgumentError( - Action(["PATH"], "path"), - f"Error: Path(s) {missing_reprs} do not exist in {rev2_repr}", - ) + msg = f"Path(s) {missing_reprs} do not exist in {rev2_repr}" + raise FileNotFoundError(msg) # These paths are relative to `common_root`: files_to_process = filter_python_files(paths, common_root, {}) @@ -631,10 +636,18 @@ def main_with_error_handling() -> int: """Entry point for console script""" try: return main() - except (ArgumentError, DependencyError) as exc_info: - if logger.root.level < logging.WARNING: - raise - sys.exit(str(exc_info)) + except FileNotFoundError as exc_info: + logger.exception("%s (%d)", exc_info, EXIT_CODE_FILE_NOT_FOUND) # noqa: TRY401 + return EXIT_CODE_FILE_NOT_FOUND + except ArgumentError as exc_info: + logger.exception("%s (%d)", exc_info, EXIT_CODE_CMDLINE_ERROR) # noqa: TRY401 + return EXIT_CODE_CMDLINE_ERROR + except DependencyError as exc_info: + logger.exception("%s (%d)", exc_info, EXIT_CODE_DEPENDENCY) # noqa: TRY401 + return EXIT_CODE_DEPENDENCY + except Exception as exc_info: # pylint: disable=broad-exception-caught + logger.exception("%s (%d)", exc_info, EXIT_CODE_UNKNOWN) # noqa: TRY401 + return EXIT_CODE_UNKNOWN if __name__ == "__main__": diff --git a/src/darker/tests/test_command_line.py b/src/darker/tests/test_command_line.py index 2ce04c9e4..9a16a6a99 100644 --- a/src/darker/tests/test_command_line.py +++ b/src/darker/tests/test_command_line.py @@ -4,7 +4,6 @@ import os import re -from argparse import ArgumentError from importlib import reload from pathlib import Path from textwrap import dedent @@ -711,10 +710,8 @@ def test_main_missing_in_worktree(git_repo): paths["a.py"].unlink() with pytest.raises( - ArgumentError, - match=re.escape( - "argument PATH: Error: Path(s) 'a.py' do not exist in the working tree" - ), + FileNotFoundError, + match=re.escape("Path(s) 'a.py' do not exist in the working tree"), ): main(["a.py"]) @@ -727,8 +724,8 @@ def test_main_missing_in_revision(git_repo): paths["a.py"].touch() with pytest.raises( - ArgumentError, - match=re.escape("argument PATH: Error: Path(s) 'a.py' do not exist in HEAD"), + FileNotFoundError, + match=re.escape("Path(s) 'a.py' do not exist in HEAD"), ): main(["--diff", "--revision", "..HEAD", "a.py"]) diff --git a/src/darker/tests/test_main_stdin_filename.py b/src/darker/tests/test_main_stdin_filename.py index 6f1080f57..5b1b3bf47 100644 --- a/src/darker/tests/test_main_stdin_filename.py +++ b/src/darker/tests/test_main_stdin_filename.py @@ -10,6 +10,7 @@ import toml import darker.__main__ +from darkgraylib.command_line import EXIT_CODE_CMDLINE_ERROR from darkgraylib.config import ConfigurationError from darkgraylib.testtools.git_repo_plugin import GitRepoFixture from darkgraylib.testtools.helpers import raises_if_exception @@ -18,7 +19,7 @@ @pytest.mark.kwparametrize( - dict(expect=SystemExit(2)), + dict(expect=SystemExit(EXIT_CODE_CMDLINE_ERROR)), dict(config_src=["a.py"], expect_a_py='modified = "a.py worktree"\n'), dict(config_src=["b.py"], src=["a.py"], expect_a_py='modified = "a.py worktree"\n'), dict( @@ -125,8 +126,8 @@ " ':WORKTREE:'" ), ), - dict(revision="..:STDIN:", expect=SystemExit(2)), - dict(revision="..:WORKTREE:", expect=SystemExit(2)), + dict(revision="..:STDIN:", expect=SystemExit(EXIT_CODE_CMDLINE_ERROR)), + dict(revision="..:WORKTREE:", expect=SystemExit(EXIT_CODE_CMDLINE_ERROR)), config_src=None, src=[], stdin_filename=None,