Bug 799262 - Formal API for loading mach command modules; r=jhammel

This commit is contained in:
Gregory Szorc 2012-10-10 11:08:09 -07:00
parent 56a91bbd83
commit d37ec03dc8
2 changed files with 49 additions and 34 deletions

1
mach
View File

@ -44,4 +44,5 @@ except ImportError:
# All of the code is in a module because EVERYTHING IS A LIBRARY.
mach = mach.main.Mach(our_dir)
mach.load_commands_from_sys_path()
sys.exit(mach.run(sys.argv[1:]))

View File

@ -13,6 +13,7 @@ import imp
import logging
import os
import sys
import uuid
from mozbuild.base import BuildConfig
from mozbuild.config import ConfigSettings
@ -40,8 +41,6 @@ CONSUMED_ARGUMENTS = [
'func',
]
MODULES_SCANNED = False
class ArgumentParser(argparse.ArgumentParser):
"""Custom implementation argument parser to make things look pretty."""
@ -113,10 +112,54 @@ To see more help for a specific command, run:
self.log_manager.register_structured_logger(self.logger)
if not MODULES_SCANNED:
self._load_modules()
def load_commands_from_sys_path(self):
"""Discover and load mach command modules from sys.path.
MODULES_SCANNED = True
This iterates over all paths on sys.path. If the path contains a
"mach/commands" subdirectory, all .py files in that directory will be
loaded and examined for mach commands.
"""
# Create parent module otherwise Python complains when the parent is
# missing.
if b'mach.commands' not in sys.modules:
mod = imp.new_module(b'mach.commands')
sys.modules[b'mach.commands'] = mod
for path in sys.path:
# We only support importing .py files from directories.
commands_path = os.path.join(path, 'mach', 'commands')
if not os.path.isdir(commands_path):
continue
self.load_commands_from_directory(commands_path)
def load_commands_from_directory(self, path):
"""Scan for mach commands from modules in a directory.
This takes a path to a directory, loads the .py files in it, and
registers and found mach command providers with this mach instance.
"""
for f in sorted(os.listdir(path)):
if not f.endswith('.py') or f == '__init__.py':
continue
full_path = os.path.join(path, f)
module_name = 'mach.commands.%s' % f[0:-3]
self.load_commands_from_file(full_path, module_name=module_name)
def load_commands_from_file(self, path, module_name=None):
"""Scan for mach commands from a file.
This takes a path to a file and loads it as a Python module under the
module name specified. If no name is specified, a random one will be
chosen.
"""
if module_name is None:
module_name = 'mach.commands.%s' % uuid.uuid1().get_hex()
imp.load_source(module_name, path)
def run(self, argv):
"""Runs mach with arguments provided from the command line.
@ -217,35 +260,6 @@ To see more help for a specific command, run:
self.logger.log(level, format_str,
extra={'action': action, 'params': params})
def _load_modules(self):
"""Scan over Python modules looking for mach command providers."""
# Create parent module otherwise Python complains when the parent is
# missing.
if b'mach.commands' not in sys.modules:
mod = imp.new_module(b'mach.commands')
sys.modules[b'mach.commands'] = mod
for path in sys.path:
# We only support importing .py files from directories.
commands_path = os.path.join(path, 'mach', 'commands')
if not os.path.isdir(commands_path):
continue
# We only support loading modules in the immediate mach.commands
# module, not sub-modules. Walking the tree would be trivial to
# implement if it were ever desired.
for f in sorted(os.listdir(commands_path)):
if not f.endswith('.py') or f == '__init__.py':
continue
full_path = os.path.join(commands_path, f)
module_name = 'mach.commands.%s' % f[0:-3]
imp.load_source(module_name, full_path)
def load_settings(self, args):
"""Determine which settings files apply and load them.