diff --git a/src/python/pants/bin/daemon_pants_runner.py b/src/python/pants/bin/daemon_pants_runner.py index ad90fc68170..fb5e9e1c542 100644 --- a/src/python/pants/bin/daemon_pants_runner.py +++ b/src/python/pants/bin/daemon_pants_runner.py @@ -340,9 +340,6 @@ def post_fork_child(self): # Clean global state. clean_global_runtime_state(reset_subsystem=True) - # Re-raise any deferred exceptions, if present. - self._raise_deferred_exc() - # Otherwise, conduct a normal run. runner = LocalPantsRunner.create( self._exiter, @@ -353,6 +350,10 @@ def post_fork_child(self): self._options_bootstrapper ) runner.set_start_time(self._maybe_get_client_start_time_from_env(self._env)) + + # Re-raise any deferred exceptions, if present. + self._raise_deferred_exc() + runner.run() except KeyboardInterrupt: self._exiter.exit_and_fail('Interrupted by user.\n') diff --git a/src/python/pants/bin/local_pants_runner.py b/src/python/pants/bin/local_pants_runner.py index 7b05205374c..16cd3c4a90c 100644 --- a/src/python/pants/bin/local_pants_runner.py +++ b/src/python/pants/bin/local_pants_runner.py @@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function, unicode_literals import logging -import traceback from builtins import object, str from pants.base.build_environment import get_buildroot @@ -271,18 +270,11 @@ def _maybe_run_v2(self): if not goals: return PANTS_SUCCEEDED_EXIT_CODE - try: - return self._graph_session.run_console_rules( - self._options_bootstrapper, - goals, - self._target_roots, - ) - except Exception: - logger.warning('Encountered unhandled exception during rule execution!') - logger.warning(traceback.format_exc()) - return PANTS_FAILED_EXIT_CODE - else: - return PANTS_SUCCEEDED_EXIT_CODE + return self._graph_session.run_console_rules( + self._options_bootstrapper, + goals, + self._target_roots, + ) @staticmethod def _compute_final_exit_code(*codes): diff --git a/src/python/pants/engine/goal.py b/src/python/pants/engine/goal.py index 10531af06f7..dcbd908c995 100644 --- a/src/python/pants/engine/goal.py +++ b/src/python/pants/engine/goal.py @@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals +from abc import abstractmethod from contextlib import contextmanager from pants.cache.cache_setup import CacheSetup @@ -29,13 +30,20 @@ class Goal(datatype([('exit_code', int)]), AbstractClass): class. """ - # Subclasser-defined. See the class pydoc. - name = None + @classproperty + @abstractmethod + def name(cls): + """The name used to select this Goal on the commandline, and for its options.""" - # If this Goal should have an associated deprecated instance of `CacheSetup` (which was implicitly - # required by all v1 Tasks), subclasses may set this to a valid deprecation version to create - # that association. - deprecated_cache_setup_removal_version = None + @classproperty + def deprecated_cache_setup_removal_version(cls): + """Optionally defines a deprecation version for a CacheSetup dependency. + + If this Goal should have an associated deprecated instance of `CacheSetup` (which was implicitly + required by all v1 Tasks), subclasses may set this to a valid deprecation version to create + that association. + """ + return None @classmethod def register_options(cls, register): @@ -55,10 +63,6 @@ def Options(cls): class _Options(SubsystemClientMixin, Optionable, _GoalOptions): @classproperty def options_scope(cls): - if not outer_cls.name: - # TODO: Would it be unnecessarily magical to have `outer_cls.__name__.lower()` always be - # the name? - raise AssertionError('{} must have a `Goal.name` defined.'.format(outer_cls.__name__)) return outer_cls.name @classmethod @@ -98,6 +102,7 @@ def __init__(self, scope, scoped_options): def values(self): """Returns the option values for these Goal.Options.""" return self._scoped_options + _Options.__doc__ = outer_cls.__doc__ return _Options diff --git a/src/python/pants/help/help_printer.py b/src/python/pants/help/help_printer.py index d0bbc05a511..41cdee1fd85 100644 --- a/src/python/pants/help/help_printer.py +++ b/src/python/pants/help/help_printer.py @@ -54,11 +54,9 @@ def _print_goals_help(self): print('\nUse `pants help $goal` to get help for a particular goal.\n') goal_descriptions = {} for scope_info in self._options.known_scope_to_info.values(): - if scope_info.category not in (ScopeInfo.GOAL,): + if scope_info.category not in (ScopeInfo.GOAL, ScopeInfo.GOAL_V1): continue - components = scope_info.scope.split('.', 1) - if len(components) == 1 and scope_info.description: - goal_descriptions[scope_info.scope] = scope_info.description + goal_descriptions[scope_info.scope] = scope_info.description goal_descriptions.update({goal.name: goal.description_first_line for goal in Goal.all() if goal.description}) diff --git a/tests/python/pants_test/base/test_exception_sink_integration.py b/tests/python/pants_test/base/test_exception_sink_integration.py index 655f109dec9..996f81a996d 100644 --- a/tests/python/pants_test/base/test_exception_sink_integration.py +++ b/tests/python/pants_test/base/test_exception_sink_integration.py @@ -25,10 +25,10 @@ def _assert_unhandled_exception_log_matches(self, pid, file_contents): process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} -Exception caught: \\(pants\\.build_graph\\.address_lookup_error\\.AddressLookupError\\) +Exception caught: \\([^)]*\\) (.|\n)* -Exception message: Build graph construction failed: ExecutionError 1 Exception encountered: +Exception message:.* 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """.format(pid=pid)) # Ensure we write all output such as stderr and reporting files before closing any streams. @@ -57,8 +57,8 @@ def test_logs_unhandled_exception(self): self.assert_failure(pants_run) assertRegex(self, pants_run.stderr_data, """\ timestamp: ([^\n]+) -Exception caught: \\(pants\\.build_graph\\.address_lookup_error\\.AddressLookupError\\) \\(backtrace omitted\\) -Exception message: Build graph construction failed: ExecutionError 1 Exception encountered: +Exception caught: \\(pants\\.engine\\.scheduler\\.ExecutionError\\) \\(backtrace omitted\\) +Exception message: 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """) pid_specific_log_file, shared_log_file = self._get_log_file_paths(tmpdir, pants_run) diff --git a/tests/python/pants_test/engine/legacy/test_console_rule_integration.py b/tests/python/pants_test/engine/legacy/test_console_rule_integration.py index 7c274b5fec8..164892eb21a 100644 --- a/tests/python/pants_test/engine/legacy/test_console_rule_integration.py +++ b/tests/python/pants_test/engine/legacy/test_console_rule_integration.py @@ -51,14 +51,14 @@ def test_v2_goal_validation(self): result = self.do_command( '--no-v1', '--v2', - 'lint', + 'blah', '::', success=False ) self.assertIn( - 'could not satisfy the following goals with @console_rules: lint', - result.stderr_data + 'Unknown goals: blah', + result.stdout_data ) @ensure_daemon diff --git a/tests/python/pants_test/pantsd/pantsd_integration_test_base.py b/tests/python/pants_test/pantsd/pantsd_integration_test_base.py index b278a819456..94b9607a65c 100644 --- a/tests/python/pants_test/pantsd/pantsd_integration_test_base.py +++ b/tests/python/pants_test/pantsd/pantsd_integration_test_base.py @@ -115,11 +115,6 @@ def pantsd_test_context(self, log_level='info', extra_config=None): self.assert_runner(workdir, pantsd_config, ['kill-pantsd'], expected_runs=1) try: yield workdir, pantsd_config, checker - finally: - banner('BEGIN pantsd.log') - for line in read_pantsd_log(workdir): - print(line) - banner('END pantsd.log') self.assert_runner( workdir, pantsd_config, @@ -127,6 +122,11 @@ def pantsd_test_context(self, log_level='info', extra_config=None): expected_runs=1, ) checker.assert_stopped() + finally: + banner('BEGIN pantsd.log') + for line in read_pantsd_log(workdir): + print(line) + banner('END pantsd.log') @contextmanager def pantsd_successful_run_context(self, *args, **kwargs):