# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import absolute_import, unicode_literals from .base import MachError INVALID_COMMAND_CONTEXT = r''' It looks like you tried to run a mach command from an invalid context. The %s command failed to meet the following conditions: %s Run |mach help| to show a list of all commands available to the current context. '''.lstrip() class MachRegistrar(object): """Container for mach command and config providers.""" def __init__(self): self.command_handlers = {} self.commands_by_category = {} self.settings_providers = set() self.categories = {} self.require_conditions = False def register_command_handler(self, handler): name = handler.name if not handler.category: raise MachError('Cannot register a mach command without a ' 'category: %s' % name) if handler.category not in self.categories: raise MachError('Cannot register a command to an undefined ' 'category: %s -> %s' % (name, handler.category)) self.command_handlers[name] = handler self.commands_by_category[handler.category].add(name) def register_settings_provider(self, cls): self.settings_providers.add(cls) def register_category(self, name, title, description, priority=50): self.categories[name] = (title, description, priority) self.commands_by_category[name] = set() @classmethod def _condition_failed_message(cls, name, conditions): msg = ['\n'] for c in conditions: part = [' %s' % c.__name__] if c.__doc__ is not None: part.append(c.__doc__) msg.append(' - '.join(part)) return INVALID_COMMAND_CONTEXT % (name, '\n'.join(msg)) def _run_command_handler(self, handler, context=None, debug_command=False, **kwargs): cls = handler.cls if handler.pass_context and not context: raise Exception('mach command class requires context.') if handler.pass_context: instance = cls(context) else: instance = cls() if handler.conditions: fail_conditions = [] for c in handler.conditions: if not c(instance): fail_conditions.append(c) if fail_conditions: print(self._condition_failed_message(handler.name, fail_conditions)) return 1 fn = getattr(instance, handler.method) if debug_command: import pdb result = pdb.runcall(fn, **kwargs) else: result = fn(**kwargs) result = result or 0 assert isinstance(result, (int, long)) return result def dispatch(self, name, context=None, argv=None, **kwargs): """Dispatch/run a command. Commands can use this to call other commands. """ # TODO handler.subcommand_handlers are ignored handler = self.command_handlers[name] if handler.parser: parser = handler.parser # save and restore existing defaults so **kwargs don't persist across # subsequent invocations of Registrar.dispatch() old_defaults = parser._defaults.copy() parser.set_defaults(**kwargs) kwargs, _ = parser.parse_known_args(argv or []) kwargs = vars(kwargs) parser._defaults = old_defaults return self._run_command_handler(handler, context=context, **kwargs) Registrar = MachRegistrar()