Skip to content

Commit

Permalink
Switch away from sys.exit(error_msg) in dmypy (#5982)
Browse files Browse the repository at this point in the history
This kills a couple birds with one stone:
 1. It fixes an interaction with the new run_dmypy api where
    that message winds up as the return code instead of in
    stderr.
    This additionally fixes a type unsoundness/mypy_mypyc crash
    caused by SystemExit.code being typed as int.
 2. It allows us to standardize the irregular exit codes of dmypy
    as 2.
  • Loading branch information
msullivan authored and JukkaL committed Dec 3, 2018
1 parent da1d5a8 commit 8d354c9
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 17 deletions.
23 changes: 14 additions & 9 deletions mypy/dmypy.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,19 @@ def main(argv: List[str]) -> None:
try:
args.action(args)
except BadStatus as err:
sys.exit(err.args[0])
fail(err.args[0])
except Exception:
# We do this explicitly to avoid exceptions percolating up
# through mypy.api invocations
traceback.print_exc()
sys.exit(2)


def fail(msg: str) -> None:
print(msg, file=sys.stderr)
sys.exit(2)


ActionFunction = Callable[[argparse.Namespace], None]


Expand Down Expand Up @@ -163,7 +168,7 @@ def do_start(args: argparse.Namespace) -> None:
# Bad or missing status file or dead process; good to start.
pass
else:
sys.exit("Daemon is still alive")
fail("Daemon is still alive")
start_server(args)


Expand Down Expand Up @@ -193,7 +198,7 @@ def start_server(args: argparse.Namespace, allow_sources: bool = False) -> None:
from mypy.dmypy_server import daemonize, process_start_options
start_options = process_start_options(args.flags, allow_sources)
if daemonize(start_options, timeout=args.timeout, log_file=args.log_file):
sys.exit(1)
sys.exit(2)
wait_for_server()


Expand All @@ -214,7 +219,7 @@ def wait_for_server(timeout: float = 5.0) -> None:
check_status(data)
print("Daemon started")
return
sys.exit("Timed out waiting for daemon to start")
fail("Timed out waiting for daemon to start")


@action(run_parser)
Expand Down Expand Up @@ -263,7 +268,7 @@ def do_status(args: argparse.Namespace) -> None:
if args.verbose or 'error' in response:
show_stats(response)
if 'error' in response:
sys.exit("Daemon is stuck; consider %s kill" % sys.argv[0])
fail("Daemon is stuck; consider %s kill" % sys.argv[0])
print("Daemon is up and running")


Expand All @@ -274,7 +279,7 @@ def do_stop(args: argparse.Namespace) -> None:
response = request('stop', timeout=5)
if response:
show_stats(response)
sys.exit("Daemon is stuck; consider %s kill" % sys.argv[0])
fail("Daemon is stuck; consider %s kill" % sys.argv[0])
else:
print("Daemon stopped")

Expand All @@ -286,7 +291,7 @@ def do_kill(args: argparse.Namespace) -> None:
try:
kill(pid)
except OSError as err:
sys.exit(str(err))
fail(str(err))
else:
print("Daemon killed")

Expand Down Expand Up @@ -332,11 +337,11 @@ def check_output(response: Dict[str, Any], verbose: bool, junit_xml: Optional[st
Call sys.exit() unless the status code is zero.
"""
if 'error' in response:
sys.exit(response['error'])
fail(response['error'])
try:
out, err, status_code = response['out'], response['err'], response['status']
except KeyError:
sys.exit("Response: %s" % str(response))
fail("Response: %s" % str(response))
sys.stdout.write(out)
sys.stderr.write(err)
if verbose:
Expand Down
16 changes: 8 additions & 8 deletions test-data/unit/daemon.test
Original file line number Diff line number Diff line change
Expand Up @@ -68,41 +68,41 @@ def plugin(version): return Dummy
[case testDaemonStatusKillRestartRecheck]
$ dmypy status
No status file found
== Return code: 1
== Return code: 2
$ dmypy stop
No status file found
== Return code: 1
== Return code: 2
$ dmypy kill
No status file found
== Return code: 1
== Return code: 2
$ dmypy recheck
No status file found
== Return code: 1
== Return code: 2
$ dmypy start -- --follow-imports=error
Daemon started
$ dmypy status
Daemon is up and running
$ dmypy start
Daemon is still alive
== Return code: 1
== Return code: 2
$ dmypy restart -- --follow-imports=error
Daemon stopped
Daemon started
$ dmypy stop
Daemon stopped
$ dmypy status
No status file found
== Return code: 1
== Return code: 2
$ dmypy restart -- --follow-imports=error
Daemon started
$ dmypy recheck
Command 'recheck' is only valid after a 'check' command
== Return code: 1
== Return code: 2
$ dmypy kill
Daemon killed
$ dmypy status
Daemon has died
== Return code: 1
== Return code: 2

[case testDaemonRecheck]
$ dmypy start -- --follow-imports=error
Expand Down

0 comments on commit 8d354c9

Please sign in to comment.