diff options
author | Jenkins <jenkins@review.openstack.org> | 2016-03-22 23:49:07 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2016-03-22 23:49:07 +0000 |
commit | 7cdcc023caffa0dfb08f02b7dca9cf3fc892bbe5 (patch) | |
tree | 62b0ac1f1a5cfa8046917116d6bb801346b94d40 | |
parent | 38977864e1fcef9e0ea3a9c470c20b80513f65df (diff) | |
parent | 139110c89b4635ec1cc9cbc21e202b985d5c1343 (diff) | |
download | pbr-7cdcc023caffa0dfb08f02b7dca9cf3fc892bbe5.tar.gz |
Merge "Include wsgi_scripts in generated wheels"
-rw-r--r-- | pbr/packaging.py | 61 | ||||
-rw-r--r-- | pbr/tests/test_packaging.py | 108 | ||||
-rw-r--r-- | pbr/tests/testpackage/src/testext.c | 9 |
3 files changed, 161 insertions, 17 deletions
diff --git a/pbr/packaging.py b/pbr/packaging.py index f929b4c..b4ca493 100644 --- a/pbr/packaging.py +++ b/pbr/packaging.py @@ -308,22 +308,39 @@ ENTRY_POINTS_MAP = { } +def generate_script(group, entry_point, header, template): + """Generate the script based on the template. + + :param str group: + The entry-point group name, e.g., "console_scripts". + :param str header: + The first line of the script, e.g., "!#/usr/bin/env python". + :param str template: + The script template. + :returns: + The templated script content + :rtype: + str + """ + if not entry_point.attrs or len(entry_point.attrs) > 2: + raise ValueError("Script targets must be of the form " + "'func' or 'Class.class_method'.") + script_text = template % dict( + group=group, + module_name=entry_point.module_name, + import_target=entry_point.attrs[0], + invoke_target='.'.join(entry_point.attrs), + ) + return header + script_text + + def override_get_script_args( dist, executable=os.path.normpath(sys.executable), is_wininst=False): """Override entrypoints console_script.""" header = easy_install.get_script_header("", executable, is_wininst) for group, template in ENTRY_POINTS_MAP.items(): for name, ep in dist.get_entry_map(group).items(): - if not ep.attrs or len(ep.attrs) > 2: - raise ValueError("Script targets must be of the form " - "'func' or 'Class.class_method'.") - script_text = template % dict( - group=group, - module_name=ep.module_name, - import_target=ep.attrs[0], - invoke_target='.'.join(ep.attrs), - ) - yield (name, header + script_text) + yield (name, generate_script(group, ep, header, template)) class LocalDevelop(develop.develop): @@ -342,6 +359,14 @@ class LocalInstallScripts(install_scripts.install_scripts): """Intercepts console scripts entry_points.""" command_name = 'install_scripts' + def _make_wsgi_scripts_only(self, dist, executable, is_wininst): + header = easy_install.get_script_header("", executable, is_wininst) + wsgi_script_template = ENTRY_POINTS_MAP['wsgi_scripts'] + for name, ep in dist.get_entry_map('wsgi_scripts').items(): + content = generate_script( + 'wsgi_scripts', ep, header, wsgi_script_template) + self.write_script(name, content) + def run(self): import distutils.command.install_scripts @@ -351,9 +376,6 @@ class LocalInstallScripts(install_scripts.install_scripts): distutils.command.install_scripts.install_scripts.run(self) else: self.outfiles = [] - if self.no_ep: - # don't install entry point scripts into .egg file! - return ei_cmd = self.get_finalized_command("egg_info") dist = pkg_resources.Distribution( @@ -368,6 +390,19 @@ class LocalInstallScripts(install_scripts.install_scripts): self.get_finalized_command("bdist_wininst"), '_is_running', False ) + if 'bdist_wheel' in self.distribution.have_run: + # We're building a wheel which has no way of generating mod_wsgi + # scripts for us. Let's build them. + # NOTE(sigmavirus24): This needs to happen here because, as the + # comment below indicates, no_ep is True when building a wheel. + self._make_wsgi_scripts_only(dist, executable, is_wininst) + + if self.no_ep: + # no_ep is True if we're installing into an .egg file or building + # a .whl file, in those cases, we do not want to build all of the + # entry-points listed for this package. + return + if os.name != 'nt': get_script_args = override_get_script_args else: diff --git a/pbr/tests/test_packaging.py b/pbr/tests/test_packaging.py index fbddffd..5d9bbef 100644 --- a/pbr/tests/test_packaging.py +++ b/pbr/tests/test_packaging.py @@ -38,8 +38,10 @@ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +import imp import os import re +import sysconfig import tempfile import textwrap @@ -47,8 +49,10 @@ import fixtures import mock import pkg_resources import six +import testtools from testtools import matchers import virtualenv +import wheel.install from pbr import git from pbr import packaging @@ -318,6 +322,94 @@ class TestPackagingInGitRepoWithoutCommit(base.BaseTestCase): self.assertEqual(body, 'CHANGES\n=======\n\n') +class TestPackagingWheels(base.BaseTestCase): + + def setUp(self): + super(TestPackagingWheels, self).setUp() + self.useFixture(TestRepo(self.package_dir)) + # Build the wheel + self.run_setup('bdist_wheel', allow_fail=False) + # Slowly construct the path to the generated whl + dist_dir = os.path.join(self.package_dir, 'dist') + relative_wheel_filename = os.listdir(dist_dir)[0] + absolute_wheel_filename = os.path.join( + dist_dir, relative_wheel_filename) + wheel_file = wheel.install.WheelFile(absolute_wheel_filename) + wheel_name = wheel_file.parsed_filename.group('namever') + # Create a directory path to unpack the wheel to + self.extracted_wheel_dir = os.path.join(dist_dir, wheel_name) + # Extract the wheel contents to the directory we just created + wheel_file.zipfile.extractall(self.extracted_wheel_dir) + wheel_file.zipfile.close() + + def test_data_directory_has_wsgi_scripts(self): + # Build the path to the scripts directory + scripts_dir = os.path.join( + self.extracted_wheel_dir, 'pbr_testpackage-0.0.data/scripts') + self.assertTrue(os.path.exists(scripts_dir)) + scripts = os.listdir(scripts_dir) + + self.assertIn('pbr_test_wsgi', scripts) + self.assertIn('pbr_test_wsgi_with_class', scripts) + self.assertNotIn('pbr_test_cmd', scripts) + self.assertNotIn('pbr_test_cmd_with_class', scripts) + + def test_generates_c_extensions(self): + built_package_dir = os.path.join( + self.extracted_wheel_dir, 'pbr_testpackage') + static_object_filename = 'testext.so' + soabi = get_soabi() + if soabi: + static_object_filename = 'testext.{0}.so'.format(soabi) + static_object_path = os.path.join( + built_package_dir, static_object_filename) + + self.assertTrue(os.path.exists(built_package_dir)) + self.assertTrue(os.path.exists(static_object_path)) + + +class TestPackagingHelpers(testtools.TestCase): + + def test_generate_script(self): + group = 'console_scripts' + entry_point = pkg_resources.EntryPoint( + name='test-ep', + module_name='pbr.packaging', + attrs=('LocalInstallScripts',)) + header = '#!/usr/bin/env fake-header\n' + template = ('%(group)s %(module_name)s %(import_target)s ' + '%(invoke_target)s') + + generated_script = packaging.generate_script( + group, entry_point, header, template) + + expected_script = ( + '#!/usr/bin/env fake-header\nconsole_scripts pbr.packaging ' + 'LocalInstallScripts LocalInstallScripts' + ) + self.assertEqual(expected_script, generated_script) + + def test_generate_script_validates_expectations(self): + group = 'console_scripts' + entry_point = pkg_resources.EntryPoint( + name='test-ep', + module_name='pbr.packaging') + header = '#!/usr/bin/env fake-header\n' + template = ('%(group)s %(module_name)s %(import_target)s ' + '%(invoke_target)s') + self.assertRaises( + ValueError, packaging.generate_script, group, entry_point, header, + template) + + entry_point = pkg_resources.EntryPoint( + name='test-ep', + module_name='pbr.packaging', + attrs=('attr1', 'attr2', 'attr3')) + self.assertRaises( + ValueError, packaging.generate_script, group, entry_point, header, + template) + + class TestPackagingInPlainDirectory(base.BaseTestCase): def setUp(self): @@ -618,3 +710,19 @@ class TestRequirementParsing(base.BaseTestCase): pkg_resources.split_sections(requires)) self.assertEqual(expected_requirements, generated_requirements) + + +def get_soabi(): + try: + return sysconfig.get_config_var('SOABI') + except IOError: + pass + + if 'pypy' in sysconfig.get_scheme_names(): + # NOTE(sigmavirus24): PyPy only added support for the SOABI config var + # to sysconfig in 2015. That was well after 2.2.1 was published in the + # Ubuntu 14.04 archive. + for suffix, _, _ in imp.get_suffixes(): + if suffix.startswith('.pypy') and suffix.endswith('.so'): + return suffix.split('.')[1] + return None diff --git a/pbr/tests/testpackage/src/testext.c b/pbr/tests/testpackage/src/testext.c index 872d43c..1b366e9 100644 --- a/pbr/tests/testpackage/src/testext.c +++ b/pbr/tests/testpackage/src/testext.c @@ -8,10 +8,11 @@ static PyMethodDef TestextMethods[] = { #if PY_MAJOR_VERSION >=3 static struct PyModuleDef testextmodule = { - PyModuleDef_HEAD_INIT, - "testext", - -1, - TestextMethods + PyModuleDef_HEAD_INIT, /* This should correspond to a PyModuleDef_Base type */ + "testext", /* This is the module name */ + "Test extension module", /* This is the module docstring */ + -1, /* This defines the size of the module and says everything is global */ + TestextMethods /* This is the method definition */ }; PyObject* |