[Python-checkins] distutils2: Hook system improvements.
tarek.ziade
python-checkins at python.org
Thu Aug 19 08:34:14 CEST 2010
tarek.ziade pushed 8db1ac878831 to distutils2:
http://hg.python.org/distutils2/rev/8db1ac878831
changeset: 588:8db1ac878831
user: ?ric Araujo <merwok at netwok.org>
date: Sun Aug 15 06:09:25 2010 +0200
summary: Hook system improvements.
files: src/distutils2/command/cmd.py, src/distutils2/dist.py, src/distutils2/tests/test_dist.py
diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py
--- a/src/distutils2/command/cmd.py
+++ b/src/distutils2/command/cmd.py
@@ -53,7 +53,7 @@
# 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
--- a/src/distutils2/dist.py
+++ b/src/distutils2/dist.py
@@ -375,14 +375,13 @@
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 @@
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
--- a/src/distutils2/tests/test_dist.py
+++ b/src/distutils2/tests/test_dist.py
@@ -7,9 +7,10 @@
import warnings
import textwrap
-from distutils2.dist import Distribution, fix_help_options, DistributionMetadata
+import distutils2.dist
+from distutils2.dist import Distribution, fix_help_options
from distutils2.command.cmd import Command
-import distutils2.dist
+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 @@
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 @@
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 @@
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):
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list