[Python-checkins] cpython: Minor improvement to extensions in setup.cfg: check parent package

eric.araujo python-checkins at python.org
Fri Sep 2 17:45:07 CEST 2011


http://hg.python.org/cpython/rev/c1949bde461e
changeset:   72181:c1949bde461e
user:        Éric Araujo <merwok at netwok.org>
date:        Thu Sep 01 07:01:13 2011 +0200
summary:
  Minor improvement to extensions in setup.cfg: check parent package

files:
  Doc/packaging/setupcfg.rst         |   4 +-
  Lib/packaging/config.py            |  20 ++++++++++-
  Lib/packaging/tests/test_config.py |  31 ++++++++++++++++-
  3 files changed, 50 insertions(+), 5 deletions(-)


diff --git a/Doc/packaging/setupcfg.rst b/Doc/packaging/setupcfg.rst
--- a/Doc/packaging/setupcfg.rst
+++ b/Doc/packaging/setupcfg.rst
@@ -769,7 +769,9 @@
 
 The section name must start with ``extension:``; the right-hand part is used as
 the full name (including a parent package, if any) of the extension.  Whitespace
-around the extension name is allowed.
+around the extension name is allowed.  If the extension module is not standalone
+(e.g. ``_bisect``) but part of a package (e.g. ``thing._speedups``), the parent
+package must be listed in the ``packages`` field.
 Valid fields and their values are listed in the documentation of the
 :class:`packaging.compiler.extension.Extension` class; values documented as
 Python lists translate to multi-line values in the configuration file.  In
diff --git a/Lib/packaging/config.py b/Lib/packaging/config.py
--- a/Lib/packaging/config.py
+++ b/Lib/packaging/config.py
@@ -16,6 +16,19 @@
 from packaging.markers import interpret
 
 
+def _check_name(name, packages):
+    if '.' not in name:
+        return
+    parts = name.split('.')
+    modname = parts[-1]
+    parent = '.'.join(parts[:-1])
+    if parent not in packages:
+        # we could log a warning instead of raising, but what's the use
+        # of letting people build modules they can't import?
+        raise PackagingOptionError(
+            'parent package for extension %r not found' % name)
+
+
 def _pop_values(values_dct, key):
     """Remove values from the dictionary and convert them as a list"""
     vals_str = values_dct.pop(key, '')
@@ -142,7 +155,8 @@
                         try:
                             hook = resolve_name(line)
                         except ImportError as e:
-                            logger.warning('cannot find setup hook: %s', e.args[0])
+                            logger.warning('cannot find setup hook: %s',
+                                           e.args[0])
                         else:
                             self.setup_hooks.append(hook)
                     self.run_hooks(content)
@@ -259,8 +273,10 @@
                     raise PackagingOptionError(
                         'extension name should be given as [extension: name], '
                         'not as key')
+                name = labels[1].strip()
+                _check_name(name, self.dist.packages)
                 ext_modules.append(Extension(
-                    labels[1].strip(),
+                    name,
                     _pop_values(values_dct, 'sources'),
                     _pop_values(values_dct, 'include_dirs'),
                     _pop_values(values_dct, 'define_macros'),
diff --git a/Lib/packaging/tests/test_config.py b/Lib/packaging/tests/test_config.py
--- a/Lib/packaging/tests/test_config.py
+++ b/Lib/packaging/tests/test_config.py
@@ -105,6 +105,7 @@
 [files]
 packages = one
            two
+           parent.undeclared
 
 [extension:one.speed_coconuts]
 sources = c_src/speed_coconuts.c
@@ -122,6 +123,11 @@
     -DGECODE_VERSION=$(./gecode_version) -- sys.platform != 'win32'
     /DGECODE_VERSION='win32' -- sys.platform == 'win32'
 language = cxx
+
+# corner case: if the parent package of an extension is declared but
+# not its grandparent, it's legal
+[extension: parent.undeclared._speed]
+sources = parent/undeclared/_speed.c
 """
 
 EXT_SETUP_CFG_BUGGY_1 = """
@@ -129,6 +135,21 @@
 name = crash_here
 """
 
+EXT_SETUP_CFG_BUGGY_2 = """
+[files]
+packages = ham
+
+[extension: spam.eggs]
+"""
+
+EXT_SETUP_CFG_BUGGY_3 = """
+[files]
+packages = ok
+           ok.works
+
+[extension: ok.works.breaks._ext]
+"""
+
 HOOKS_MODULE = """
 import logging
 
@@ -314,7 +335,7 @@
         dist = self.get_dist()
 
         ext_modules = dict((mod.name, mod) for mod in dist.ext_modules)
-        self.assertEqual(len(ext_modules), 2)
+        self.assertEqual(len(ext_modules), 3)
         ext = ext_modules.get('one.speed_coconuts')
         self.assertEqual(ext.sources, ['c_src/speed_coconuts.c'])
         self.assertEqual(ext.define_macros, ['HAVE_CAIRO', 'HAVE_GTK2'])
@@ -341,6 +362,12 @@
         self.write_file('setup.cfg', EXT_SETUP_CFG_BUGGY_1)
         self.assertRaises(PackagingOptionError, self.get_dist)
 
+        self.write_file('setup.cfg', EXT_SETUP_CFG_BUGGY_2)
+        self.assertRaises(PackagingOptionError, self.get_dist)
+
+        self.write_file('setup.cfg', EXT_SETUP_CFG_BUGGY_3)
+        self.assertRaises(PackagingOptionError, self.get_dist)
+
     def test_project_setup_hook_works(self):
         # Bug #11637: ensure the project directory is on sys.path to allow
         # project-specific hooks
@@ -364,7 +391,7 @@
         self.write_setup({
             'setup-hooks': '\n  packaging.tests.test_config.first_hook'
                            '\n  packaging.tests.test_config.missing_hook'
-                           '\n  packaging.tests.test_config.third_hook'
+                           '\n  packaging.tests.test_config.third_hook',
         })
         self.write_file('README', 'yeah')
         dist = self.get_dist()

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list