summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-03-22 23:49:07 +0000
committerGerrit Code Review <review@openstack.org>2016-03-22 23:49:07 +0000
commit7cdcc023caffa0dfb08f02b7dca9cf3fc892bbe5 (patch)
tree62b0ac1f1a5cfa8046917116d6bb801346b94d40
parent38977864e1fcef9e0ea3a9c470c20b80513f65df (diff)
parent139110c89b4635ec1cc9cbc21e202b985d5c1343 (diff)
downloadpbr-7cdcc023caffa0dfb08f02b7dca9cf3fc892bbe5.tar.gz
Merge "Include wsgi_scripts in generated wheels"
-rw-r--r--pbr/packaging.py61
-rw-r--r--pbr/tests/test_packaging.py108
-rw-r--r--pbr/tests/testpackage/src/testext.c9
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*