Allow multiple values for package_data in setup.cfg (#11805).
authorÉric Araujo <merwok@netwok.org>
Sat, 4 Feb 2012 20:53:07 +0000 (21:53 +0100)
committerÉric Araujo <merwok@netwok.org>
Sat, 4 Feb 2012 20:53:07 +0000 (21:53 +0100)
Even though the resources system obsoletes data_files and package_data
(see bug discussion), package_data still exists to allow compatibility
with distutils and thus an easier transition.  In setup.py, the values
are lists of glob patterns, so the setup.cfg syntax needed a way to
express multiple values too.

Doc for this option will be added later as part of the big packaging doc
patches.  For now, the test serves as example.

Reported by Erik Bray.

Lib/packaging/config.py
Lib/packaging/tests/test_config.py
Misc/NEWS

index 366faea4ede9a99e8e7d9d7700313c873cca3dba..ab026a83b6876198b9377f54700a21a5cab37edc 100644 (file)
@@ -20,7 +20,6 @@ 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
@@ -227,13 +226,25 @@ class Config:
                 self.dist.scripts = [self.dist.scripts]
 
             self.dist.package_data = {}
+            # bookkeeping for the loop below
+            firstline = True
+            prev = None
+
             for line in files.get('package_data', []):
-                data = line.split('=')
-                if len(data) != 2:
-                    raise ValueError('invalid line for package_data: %s '
-                                     '(misses "=")' % line)
-                key, value = data
-                self.dist.package_data[key.strip()] = value.strip()
+                if '=' in line:
+                    # package name -- file globs or specs
+                    key, value = line.split('=')
+                    prev = self.dist.package_data[key.strip()] = value.split()
+                elif firstline:
+                    # invalid continuation on the first line
+                    raise PackagingOptionError(
+                        'malformed package_data first line: %r (misses "=")' %
+                        line)
+                else:
+                    # continuation, add to last seen package name
+                    prev.extend(line.split())
+
+                firstline = False
 
             self.dist.data_files = []
             for data in files.get('data_files', []):
index 613d493916f4ceda3c97314ba7158f31ac83d8e4..0d76b29a046471958f39a3557f16e7ed45ec21c6 100644 (file)
@@ -66,11 +66,15 @@ scripts =
   bin/taunt
 
 package_data =
-  cheese = data/templates/*
+  cheese = data/templates/* doc/*
+      doc/images/*.png
+
 
 extra_files = %(extra-files)s
 
 # Replaces MANIFEST.in
+# FIXME no, it's extra_files
+# (but sdist_extra is a better name, should use it)
 sdist_extra =
   include THANKS HACKING
   recursive-include examples *.txt *.py
@@ -96,6 +100,17 @@ setup_hooks = %(setup-hooks)s
 sub_commands = foo
 """
 
+SETUP_CFG_PKGDATA_BUGGY_1 = """
+[files]
+package_data = foo.*
+"""
+
+SETUP_CFG_PKGDATA_BUGGY_2 = """
+[files]
+package_data =
+    foo.*
+"""
+
 # Can not be merged with SETUP_CFG else install_dist
 # command will fail when trying to compile C sources
 # TODO use a DummyCommand to mock build_ext
@@ -276,13 +291,14 @@ class ConfigTestCase(support.TempdirManager,
 
         self.assertEqual(dist.packages, ['one', 'two', 'three'])
         self.assertEqual(dist.py_modules, ['haven'])
-        self.assertEqual(dist.package_data, {'cheese': 'data/templates/*'})
-        self.assertEqual(
+        self.assertEqual(dist.package_data,
+                         {'cheese': ['data/templates/*', 'doc/*',
+                                     'doc/images/*.png']})
+        self.assertEqual(dist.data_files,
             {'bm/b1.gif': '{icon}/b1.gif',
              'bm/b2.gif': '{icon}/b2.gif',
              'Cfg/data.CFG': '{config}/baBar/data.CFG',
-             'init_script': '{script}/JunGle/init_script'},
-             dist.data_files)
+             'init_script': '{script}/JunGle/init_script'})
 
         self.assertEqual(dist.package_dir, 'src')
 
@@ -293,8 +309,8 @@ class ConfigTestCase(support.TempdirManager,
         # this file would be __main__.Foo when run as "python test_config.py".
         # The name FooBarBazTest should be unique enough to prevent
         # collisions.
-        self.assertEqual('FooBarBazTest',
-                         dist.get_command_obj('foo').__class__.__name__)
+        self.assertEqual(dist.get_command_obj('foo').__class__.__name__,
+                         'FooBarBazTest')
 
         # did the README got loaded ?
         self.assertEqual(dist.metadata['description'], 'yeah')
@@ -304,6 +320,13 @@ class ConfigTestCase(support.TempdirManager,
         d = new_compiler(compiler='d')
         self.assertEqual(d.description, 'D Compiler')
 
+        # check error reporting for invalid package_data value
+        self.write_file('setup.cfg', SETUP_CFG_PKGDATA_BUGGY_1)
+        self.assertRaises(PackagingOptionError, self.get_dist)
+
+        self.write_file('setup.cfg', SETUP_CFG_PKGDATA_BUGGY_2)
+        self.assertRaises(PackagingOptionError, self.get_dist)
+
     def test_multiple_description_file(self):
         self.write_setup({'description-file': 'README  CHANGES'})
         self.write_file('README', 'yeah')
index 72a943d62a42f3da0b61a5f987d24e889152a30c..91972bf2b2ea34225e83ad236682322dcf23008c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -466,6 +466,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #11805: package_data in setup.cfg should allow more than one value.
+
 - Issue #13901: Prevent test_distutils failures on OS X with --enable-shared.
 
 - Issue #13676: Handle strings with embedded zeros correctly in sqlite3.