summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Finucane <sfinucan@redhat.com>2017-10-06 15:44:13 +0100
committerStephen Finucane <sfinucan@redhat.com>2017-10-09 10:48:33 +0100
commit32c90ba598d7740e52bf21bc5e920fb5df08645a (patch)
tree3ec86c93f0aa1e5200b2444a22561b516235b75d
parent87637bdf1d22d5e6aee5650b533e477e8e715b19 (diff)
downloadpbr-32c90ba598d7740e52bf21bc5e920fb5df08645a.tar.gz
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
-rw-r--r--pbr/core.py11
-rw-r--r--pbr/tests/test_hooks.py28
-rw-r--r--pbr/tests/testpackage/setup.cfg4
-rw-r--r--pbr/util.py116
-rw-r--r--releasenotes/notes/remove-command-hooks-907d9c2325f306ca.yaml6
5 files changed, 6 insertions, 159 deletions
diff --git a/pbr/core.py b/pbr/core.py
index 71d1e56..753387e 100644
--- a/pbr/core.py
+++ b/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()
diff --git a/pbr/tests/test_hooks.py b/pbr/tests/test_hooks.py
index 0759706..0fcf96c 100644
--- a/pbr/tests/test_hooks.py
+++ b/pbr/tests/test_hooks.py
@@ -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)
diff --git a/pbr/tests/testpackage/setup.cfg b/pbr/tests/testpackage/setup.cfg
index 242c9c6..d10a87e 100644
--- a/pbr/tests/testpackage/setup.cfg
+++ b/pbr/tests/testpackage/setup.cfg
@@ -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
diff --git a/pbr/util.py b/pbr/util.py
index c07376e..dc8995e 100644
--- a/pbr/util.py
+++ b/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)
diff --git a/releasenotes/notes/remove-command-hooks-907d9c2325f306ca.yaml b/releasenotes/notes/remove-command-hooks-907d9c2325f306ca.yaml
new file mode 100644
index 0000000..c6ce753
--- /dev/null
+++ b/releasenotes/notes/remove-command-hooks-907d9c2325f306ca.yaml
@@ -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.