diff --git a/src/allmydata/scripts/runner.py b/src/allmydata/scripts/runner.py index 8b730d9cd..bc887e11a 100644 --- a/src/allmydata/scripts/runner.py +++ b/src/allmydata/scripts/runner.py @@ -106,8 +106,7 @@ def parse_options(argv, config=None): config.parseOptions(argv) # may raise usage.error return config -def parse_or_exit_with_explanation(argv, stdout=sys.stdout): - config = Options() +def parse_or_exit_with_explanation_with_config(config, argv, stdout, stderr): try: parse_options(argv[1:], config=config) except usage.error as e: @@ -171,7 +170,7 @@ def _maybe_enable_eliot_logging(options, reactor): # Pass on the options so we can dispatch the subcommand. return options -def run(argv=sys.argv, stderr=sys.stderr): +def run(configFactory=Options, argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr): # TODO(3035): Remove tox-check when error becomes a warning if 'TOX_ENV_NAME' not in os.environ: assert sys.version_info < (3,), u"Tahoe-LAFS does not run under Python 3. Please use Python 2.7.x." @@ -180,7 +179,15 @@ def run(argv=sys.argv, stderr=sys.stderr): from allmydata.windows.fixups import initialize initialize() # doesn't return: calls sys.exit(rc) - task.react(lambda reactor: _run_with_reactor(reactor, argv, stderr)) + task.react( + lambda reactor: _run_with_reactor( + reactor, + configFactory(), + argv, + stdout, + stderr, + ), + ) def _setup_coverage(reactor, argv): @@ -223,13 +230,19 @@ def _setup_coverage(reactor, argv): reactor.addSystemEventTrigger('after', 'shutdown', write_coverage_data) -def _run_with_reactor(reactor, argv, stderr): +def _run_with_reactor(reactor, config, argv, stdout, stderr): _setup_coverage(reactor, argv) - d = defer.maybeDeferred(parse_or_exit_with_explanation, argv) + d = defer.maybeDeferred( + parse_or_exit_with_explanation_with_config, + config, + argv, + stdout, + stderr, + ) d.addCallback(_maybe_enable_eliot_logging, reactor) - d.addCallback(dispatch) + d.addCallback(dispatch, stdout=stdout, stderr=stderr) def _show_exception(f): # when task.react() notices a non-SystemExit exception, it does # log.err() with the failure and then exits with rc=1. We want this diff --git a/src/allmydata/test/cli/test_cli.py b/src/allmydata/test/cli/test_cli.py index d1bcfc128..f0e6fd2e4 100644 --- a/src/allmydata/test/cli/test_cli.py +++ b/src/allmydata/test/cli/test_cli.py @@ -510,14 +510,13 @@ class CLI(CLITestMixin, unittest.TestCase): def test_exception_catcher(self): self.basedir = "cli/exception_catcher" - stderr = StringIO() exc = Exception("canary") - ns = Namespace() + class BrokenOptions(object): + def parseOptions(self, argv): + raise exc - ns.parse_called = False - def call_parse_or_exit(args): - ns.parse_called = True - raise exc + stderr = StringIO() + ns = Namespace() ns.sys_exit_called = False def call_sys_exit(exitcode): @@ -531,19 +530,17 @@ class CLI(CLITestMixin, unittest.TestCase): # it's safe to drop it on the floor. f(reactor) - patcher = MonkeyPatcher((runner, 'parse_or_exit_with_explanation', - call_parse_or_exit), - (sys, 'exit', call_sys_exit), + patcher = MonkeyPatcher((sys, 'exit', call_sys_exit), (task, 'react', fake_react), ) patcher.runWithPatches( lambda: runner.run( + configFactory=BrokenOptions, argv=["tahoe"], stderr=stderr, ), ) - self.failUnless(ns.parse_called) self.failUnless(ns.sys_exit_called) self.failUnlessIn(str(exc), stderr.getvalue()) diff --git a/src/allmydata/test/common_util.py b/src/allmydata/test/common_util.py index 6a221f509..8885e067e 100644 --- a/src/allmydata/test/common_util.py +++ b/src/allmydata/test/common_util.py @@ -3,6 +3,9 @@ from __future__ import print_function import os import time import signal +from functools import ( + partial, +) from random import randrange from six.moves import StringIO from io import ( @@ -100,10 +103,20 @@ def run_cli_bytes(verb, *args, **kwargs): stdout = TextIOWrapper(BytesIO(), encoding) stderr = TextIOWrapper(BytesIO(), encoding) d = defer.succeed(argv) - d.addCallback(runner.parse_or_exit_with_explanation, stdout=stdout) - d.addCallback(runner.dispatch, - stdin=StringIO(stdin), - stdout=stdout, stderr=stderr) + d.addCallback( + partial( + runner.parse_or_exit_with_explanation_with_config, + runner.Options(), + ), + stdout=stdout, + stderr=stderr, + ) + d.addCallback( + runner.dispatch, + stdin=StringIO(stdin), + stdout=stdout, + stderr=stderr, + ) def _done(rc): return 0, _getvalue(stdout), _getvalue(stderr) def _err(f):