diff --git a/Lib/packaging/config.py b/Lib/packaging/config.py --- a/Lib/packaging/config.py +++ b/Lib/packaging/config.py @@ -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', []): diff --git a/Lib/packaging/tests/test_command_build_py.py b/Lib/packaging/tests/test_command_build_py.py --- a/Lib/packaging/tests/test_command_build_py.py +++ b/Lib/packaging/tests/test_command_build_py.py @@ -24,11 +24,17 @@ class BuildPyTestCase(support.TempdirMan f.write("# Pretend this is a package.") finally: f.close() + # let's have two files to make sure globbing works f = open(os.path.join(pkg_dir, "README.txt"), "w") try: f.write("Info about this package") finally: f.close() + f = open(os.path.join(pkg_dir, "HACKING.txt"), "w") + try: + f.write("How to contribute") + finally: + f.close() destination = self.mkdtemp() @@ -42,7 +48,7 @@ class BuildPyTestCase(support.TempdirMan convert_2to3_doctests=None, use_2to3=False) dist.packages = ["pkg"] - dist.package_data = {"pkg": ["README.txt"]} + dist.package_data = {"pkg": ["*.txt"]} dist.package_dir = sources cmd = build_py(dist) @@ -55,15 +61,19 @@ class BuildPyTestCase(support.TempdirMan # This makes sure the list of outputs includes byte-compiled # files for Python modules but not for package data files # (there shouldn't *be* byte-code files for those!). - # - self.assertEqual(len(cmd.get_outputs()), 3) + # FIXME the test below is not doing what the comment above says, and + # if it did it would show a code bug: if we add a demo.py file to + # package_data, it gets byte-compiled! + outputs = cmd.get_outputs() + self.assertEqual(len(outputs), 4, outputs) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) pycache_dir = os.path.join(pkgdest, "__pycache__") self.assertIn("__init__.py", files) self.assertIn("README.txt", files) + self.assertIn("HACKING.txt", files) pyc_files = os.listdir(pycache_dir) - self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files) + self.assertEqual(["__init__.%s.pyc" % imp.get_tag()], pyc_files) def test_empty_package_dir(self): # See SF 1668596/1720897. diff --git a/Lib/packaging/tests/test_command_sdist.py b/Lib/packaging/tests/test_command_sdist.py --- a/Lib/packaging/tests/test_command_sdist.py +++ b/Lib/packaging/tests/test_command_sdist.py @@ -73,7 +73,6 @@ class SDistTestCase(support.TempdirManag 'author_email': 'xxx'} dist = Distribution(metadata) dist.packages = ['somecode'] - dist.include_package_data = True cmd = sdist(dist) cmd.dist_dir = 'dist' return dist, cmd 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 @@ -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.TempdirMana 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.TempdirMana # 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.TempdirMana 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')