Remove support for command hooks
distutils2 is long dead and many of its best ideas have been incorporated into setuptools. One of the ideas that *has not* been incorporated is the idea of command hooks, of either the pre or post kind. pbr is still carrying the code for this, and there are several issues with this: - No one is using this functionality in OpenStack and, given the complete lack of documentation on the matter, it's very doubtful that it's being used anywhere else [1] - It's causing issues for projects attempting to hook into the 'distutils.commands' entry point on Python 2.7, which it seems no else must have attempted yet [2]. - distutils2 is dead and advanced features like this that we don't explicitly need should not be retained We could attempt to fix this but why bother? Good riddance, I say. [1] http://codesearch.openstack.org/?q=pre_hook%5C.&i=nope&files=&repos= [2] http://codesearch.openstack.org/?q=distutils.commands&i=nope&files=&repos= Change-Id: I01f657034cffbf55ce830b7e8dbb6b3d94c1fd18
This commit is contained in:
parent
87637bdf1d
commit
32c90ba598
11
pbr/core.py
11
pbr/core.py
|
@ -141,16 +141,5 @@ def pbr(dist, attr, value):
|
|||
if isinstance(dist.metadata.version, integer_types + (float,)):
|
||||
# Some people apparently take "version number" too literally :)
|
||||
dist.metadata.version = str(dist.metadata.version)
|
||||
|
||||
# This bit of hackery is necessary so that the Distribution will ignore
|
||||
# normally unsupport command options (namely pre-hooks and post-hooks).
|
||||
# dist.command_options is normally a dict mapping command names to
|
||||
# dicts of their options. Now it will be a defaultdict that returns
|
||||
# IgnoreDicts for the each command's options so we can pass through the
|
||||
# unsupported options
|
||||
ignore = ['pre_hook.*', 'post_hook.*']
|
||||
dist.command_options = util.DefaultGetDict(
|
||||
lambda: util.IgnoreDict(ignore)
|
||||
)
|
||||
finally:
|
||||
_restore_distribution_monkeypatch()
|
||||
|
|
|
@ -39,9 +39,7 @@
|
|||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
from testtools import content
|
||||
from testtools import matchers
|
||||
|
||||
from pbr.tests import base
|
||||
|
@ -56,10 +54,6 @@ class TestHooks(base.BaseTestCase):
|
|||
cfg.set('global', 'setup-hooks',
|
||||
'pbr_testpackage._setup_hooks.test_hook_1\n'
|
||||
'pbr_testpackage._setup_hooks.test_hook_2')
|
||||
cfg.set('build_ext', 'pre-hook.test_pre_hook',
|
||||
'pbr_testpackage._setup_hooks.test_pre_hook')
|
||||
cfg.set('build_ext', 'post-hook.test_post_hook',
|
||||
'pbr_testpackage._setup_hooks.test_post_hook')
|
||||
|
||||
def test_global_setup_hooks(self):
|
||||
"""Test setup_hooks.
|
||||
|
@ -72,28 +66,6 @@ class TestHooks(base.BaseTestCase):
|
|||
assert 'test_hook_1\ntest_hook_2' in stdout
|
||||
assert return_code == 0
|
||||
|
||||
def test_command_hooks(self):
|
||||
"""Test command hooks.
|
||||
|
||||
Simple test that the appropriate command hooks run at the
|
||||
beginning/end of the appropriate command.
|
||||
"""
|
||||
|
||||
stdout, _, return_code = self.run_setup('egg_info')
|
||||
assert 'build_ext pre-hook' not in stdout
|
||||
assert 'build_ext post-hook' not in stdout
|
||||
assert return_code == 0
|
||||
|
||||
stdout, stderr, return_code = self.run_setup('build_ext')
|
||||
self.addDetailUniqueName('stderr', content.text_content(stderr))
|
||||
assert textwrap.dedent("""
|
||||
running build_ext
|
||||
running pre_hook pbr_testpackage._setup_hooks.test_pre_hook for command build_ext
|
||||
build_ext pre-hook
|
||||
""") in stdout # flake8: noqa
|
||||
self.expectThat(stdout, matchers.EndsWith('build_ext post-hook'))
|
||||
assert return_code == 0
|
||||
|
||||
def test_custom_commands_known(self):
|
||||
stdout, _, return_code = self.run_setup('--help-commands')
|
||||
self.assertFalse(return_code)
|
||||
|
|
|
@ -51,7 +51,3 @@ optional = True
|
|||
# pbr_testpackage._setup_hooks.test_hook_1
|
||||
# pbr_testpackage._setup_hooks.test_hook_2
|
||||
commands = pbr_testpackage._setup_hooks.test_command
|
||||
|
||||
[build_ext]
|
||||
#pre-hook.test_pre_hook = pbr_testpackage._setup_hooks.test_pre_hook
|
||||
#post-hook.test_post_hook = pbr_testpackage._setup_hooks.test_post_hook
|
||||
|
|
116
pbr/util.py
116
pbr/util.py
|
@ -264,8 +264,6 @@ def cfg_to_args(path='setup.cfg', script_args=()):
|
|||
if entry_points:
|
||||
kwargs['entry_points'] = entry_points
|
||||
|
||||
wrap_commands(kwargs)
|
||||
|
||||
# Handle the [files]/extra_files option
|
||||
files_extra_files = has_get_option(config, 'files', 'extra_files')
|
||||
if files_extra_files:
|
||||
|
@ -552,103 +550,6 @@ def get_entry_points(config):
|
|||
for option, value in config['entry_points'].items())
|
||||
|
||||
|
||||
def wrap_commands(kwargs):
|
||||
dist = st_dist.Distribution()
|
||||
|
||||
# This should suffice to get the same config values and command classes
|
||||
# that the actual Distribution will see (not counting cmdclass, which is
|
||||
# handled below)
|
||||
dist.parse_config_files()
|
||||
|
||||
# Setuptools doesn't patch get_command_list, and as such we do not get
|
||||
# extra commands from entry_points. As we need to be compatable we deal
|
||||
# with this here.
|
||||
for ep in pkg_resources.iter_entry_points('distutils.commands'):
|
||||
if ep.name not in dist.cmdclass:
|
||||
if hasattr(ep, 'resolve'):
|
||||
cmdclass = ep.resolve()
|
||||
else:
|
||||
# Old setuptools does not have ep.resolve, and load with
|
||||
# arguments is depricated in 11+. Use resolve, 12+, if we
|
||||
# can, otherwise fall back to load.
|
||||
# Setuptools 11 will throw a deprication warning, as it
|
||||
# uses _load instead of resolve.
|
||||
cmdclass = ep.load(False)
|
||||
dist.cmdclass[ep.name] = cmdclass
|
||||
|
||||
for cmd, _ in dist.get_command_list():
|
||||
hooks = {}
|
||||
for opt, val in dist.get_option_dict(cmd).items():
|
||||
val = val[1]
|
||||
if opt.startswith('pre_hook.') or opt.startswith('post_hook.'):
|
||||
hook_type, alias = opt.split('.', 1)
|
||||
hook_dict = hooks.setdefault(hook_type, {})
|
||||
hook_dict[alias] = val
|
||||
if not hooks:
|
||||
continue
|
||||
|
||||
if 'cmdclass' in kwargs and cmd in kwargs['cmdclass']:
|
||||
cmdclass = kwargs['cmdclass'][cmd]
|
||||
else:
|
||||
cmdclass = dist.get_command_class(cmd)
|
||||
|
||||
new_cmdclass = wrap_command(cmd, cmdclass, hooks)
|
||||
kwargs.setdefault('cmdclass', {})[cmd] = new_cmdclass
|
||||
|
||||
|
||||
def wrap_command(cmd, cmdclass, hooks):
|
||||
def run(self, cmdclass=cmdclass):
|
||||
self.run_command_hooks('pre_hook')
|
||||
cmdclass.run(self)
|
||||
self.run_command_hooks('post_hook')
|
||||
|
||||
return type(cmd, (cmdclass, object),
|
||||
{'run': run, 'run_command_hooks': run_command_hooks,
|
||||
'pre_hook': hooks.get('pre_hook'),
|
||||
'post_hook': hooks.get('post_hook')})
|
||||
|
||||
|
||||
def run_command_hooks(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, None)
|
||||
|
||||
if hooks is None:
|
||||
return
|
||||
|
||||
for hook in hooks.values():
|
||||
if isinstance(hook, str):
|
||||
try:
|
||||
hook_obj = resolve_name(hook)
|
||||
except ImportError:
|
||||
err = sys.exc_info()[1] # For py3k
|
||||
raise errors.DistutilsModuleError('cannot find hook %s: %s' %
|
||||
(hook,err))
|
||||
else:
|
||||
hook_obj = hook
|
||||
|
||||
if not hasattr(hook_obj, '__call__'):
|
||||
raise errors.DistutilsOptionError('hook %r is not callable' % hook)
|
||||
|
||||
log.info('running %s %s for command %s',
|
||||
hook_kind, hook, cmd_obj.get_command_name())
|
||||
|
||||
try :
|
||||
hook_obj(cmd_obj)
|
||||
except:
|
||||
e = sys.exc_info()[1]
|
||||
log.error('hook %s raised exception: %s\n' % (hook, e))
|
||||
log.error(traceback.format_exc())
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def has_get_option(config, section, option):
|
||||
if section in config and option in config[section]:
|
||||
return config[section][option]
|
||||
|
@ -684,20 +585,3 @@ class DefaultGetDict(defaultdict):
|
|||
if default is None:
|
||||
default = self.default_factory()
|
||||
return super(DefaultGetDict, self).setdefault(key, default)
|
||||
|
||||
|
||||
class IgnoreDict(dict):
|
||||
"""A dictionary that ignores any insertions in which the key is a string
|
||||
matching any string in `ignore`. The ignore list can also contain wildcard
|
||||
patterns using '*'.
|
||||
"""
|
||||
|
||||
def __init__(self, ignore):
|
||||
self.__ignore = re.compile(r'(%s)' % ('|'.join(
|
||||
[pat.replace('*', '.*')
|
||||
for pat in ignore])))
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
if self.__ignore.match(key):
|
||||
return
|
||||
super(IgnoreDict, self).__setitem__(key, val)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
Support for entry point command hooks has been removed. This feature was
|
||||
poorly tested, poorly documented, and broken in some environments.
|
||||
Support for global hooks is not affected.
|
Loading…
Reference in New Issue