diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/distutils2/command/cmd.py | 2 | ||||
| -rw-r--r-- | src/distutils2/dist.py | 39 | ||||
| -rw-r--r-- | src/distutils2/tests/test_dist.py | 85 |
3 files changed, 102 insertions, 24 deletions
diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py index 462bbd2..17a4379 100644 --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -53,7 +53,7 @@ class Command(object): # Pre and post command hooks are run just before or just after the command # itself. They are simple functions that receive the command instance. They - # should be specified as dotted strings. + # are specified as callable objects or dotted strings (for lazy loading). pre_hook = None post_hook = None diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py index 6dd1b6e..65ce631 100644 --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -375,14 +375,13 @@ Common commands: (see '--help-commands' for more) val = parser.get(section, opt) opt = opt.replace('-', '_') - # ... although practicality beats purity :( + # Hooks use a suffix system to prevent being overriden + # by a config file processed later (i.e. a hook set in + # the user config file cannot be replaced by a hook + # set in a project config file, unless they have the + # same suffix). if opt.startswith("pre_hook.") or opt.startswith("post_hook."): hook_type, alias = opt.split(".") - # Hooks are somewhat exceptional, as they are - # gathered from many config files (not overriden as - # other options). - # The option_dict expects {"command": ("filename", # "value")} - # so for hooks, we store only the last config file processed hook_dict = opt_dict.setdefault(hook_type, (filename, {}))[1] hook_dict[alias] = val else: @@ -963,12 +962,34 @@ Common commands: (see '--help-commands' for more) self.have_run[command] = 1 def run_command_hooks(self, cmd_obj, hook_kind): + """Run hooks registered for that command and phase. + + *cmd_obj* is a finalized command object; *hook_kind* is either + 'pre_hook' or 'post_hook'. + """ + if hook_kind not in ('pre_hook', 'post_hook'): + raise ValueError('invalid hook kind: %r' % hook_kind) + hooks = getattr(cmd_obj, hook_kind) + if hooks is None: return - for hook in hooks.values(): - hook_func = resolve_name(hook) - hook_func(cmd_obj) + + for hook in hooks.itervalues(): + if isinstance(hook, basestring): + try: + hook_obj = resolve_name(hook) + except ImportError, e: + raise DistutilsModuleError(e) + else: + hook_obj = hook + + if not hasattr(hook_obj, '__call__'): + raise DistutilsOptionError('hook %r is not callable' % hook) + + log.info('running %s %s for command %s', + hook_kind, hook, cmd_obj.get_command_name()) + hook_obj(cmd_obj) # -- Distribution query methods ------------------------------------ def has_pure_modules(self): diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py index 254ce3b..b7ffa13 100644 --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -7,9 +7,10 @@ import sys import warnings import textwrap -from distutils2.dist import Distribution, fix_help_options, DistributionMetadata -from distutils2.command.cmd import Command import distutils2.dist +from distutils2.dist import Distribution, fix_help_options +from distutils2.command.cmd import Command +from distutils2.errors import DistutilsModuleError, DistutilsOptionError from distutils2.tests import TESTFN, captured_stdout from distutils2.tests import support from distutils2.tests.support import unittest @@ -24,6 +25,9 @@ class test_dist(Command): def initialize_options(self): self.sample_option = None + def finalize_options(self): + pass + class TestDistribution(Distribution): """Distribution subclasses that avoids the default search for @@ -295,11 +299,22 @@ class DistributionTestCase(support.TempdirManager, def test_hooks_get_run(self): temp_home = self.mkdtemp() config_file = os.path.join(temp_home, "config1.cfg") + hooks_module = os.path.join(temp_home, "testhooks.py") - self.write_file((temp_home, "config1.cfg"), textwrap.dedent(''' + self.write_file(config_file, textwrap.dedent(''' [test_dist] - pre-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_pre_call - post-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_post_call''')) + pre-hook.test = testhooks.log_pre_call + post-hook.test = testhooks.log_post_call''')) + + self.write_file(hooks_module, textwrap.dedent(''' + record = [] + + def log_pre_call(cmd): + record.append('pre-%s' % cmd.get_command_name()) + + def log_post_call(cmd): + record.append('post-%s' % cmd.get_command_name()) + ''')) sys.argv.extend(["--command-packages", "distutils2.tests", @@ -308,17 +323,59 @@ class DistributionTestCase(support.TempdirManager, cmd = d.get_command_obj("test_dist") # prepare the call recorders - record = [] - DistributionTestCase.log_pre_call = staticmethod(lambda _cmd: record.append(('pre', _cmd))) - DistributionTestCase.log_post_call = staticmethod(lambda _cmd: record.append(('post', _cmd))) - test_dist.run = lambda _cmd: record.append(('run', _cmd)) - test_dist.finalize_options = lambda _cmd: record.append(('finalize_options', _cmd)) + sys.path.append(temp_home) + from testhooks import record + + self.addCleanup(setattr, cmd, 'run', cmd.run) + self.addCleanup(setattr, cmd, 'finalize_options', + cmd.finalize_options) + + cmd.run = lambda: record.append('run') + cmd.finalize_options = lambda: record.append('finalize') d.run_command('test_dist') - self.assertEqual(record, [('finalize_options', cmd), - ('pre', cmd), - ('run', cmd), - ('post', cmd)]) + + self.assertEqual(record, ['finalize', + 'pre-test_dist', + 'run', + 'post-test_dist']) + + def test_hooks_importable(self): + temp_home = self.mkdtemp() + config_file = os.path.join(temp_home, "config1.cfg") + + self.write_file(config_file, textwrap.dedent(''' + [test_dist] + pre-hook.test = nonexistent.dotted.name''')) + + sys.argv.extend(["--command-packages", + "distutils2.tests", + "test_dist"]) + + d = self.create_distribution([config_file]) + cmd = d.get_command_obj("test_dist") + cmd.ensure_finalized() + + self.assertRaises(DistutilsModuleError, d.run_command, 'test_dist') + + def test_hooks_callable(self): + temp_home = self.mkdtemp() + config_file = os.path.join(temp_home, "config1.cfg") + + self.write_file(config_file, textwrap.dedent(''' + [test_dist] + pre-hook.test = distutils2.tests.test_dist.__doc__''')) + + sys.argv.extend(["--command-packages", + "distutils2.tests", + "test_dist"]) + + d = self.create_distribution([config_file]) + cmd = d.get_command_obj("test_dist") + cmd.ensure_finalized() + + self.assertRaises(DistutilsOptionError, d.run_command, 'test_dist') + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): |
