# HG changeset patch # Parent 024827a9db64990865d29f9d525694f51197e770 # User Stephen Thorne Issue #11104: Read and respect the MANIFEST when no MANIFEST.in used. When MANIFEST does not contain the comment that indicates that it is created from a MANIFEST.in ignore any MANIFEST.in and read the files out of MANIFEST. Previous to this change, only files that would be auto-detected if no MANIFEST* files were present at all would be used. Based on the patch from jdennis diff --git a/Doc/distutils/sourcedist.rst b/Doc/distutils/sourcedist.rst --- a/Doc/distutils/sourcedist.rst +++ b/Doc/distutils/sourcedist.rst @@ -185,8 +185,12 @@ Manifest-related options The normal course of operations for the :command:`sdist` command is as follows: -* if the manifest file, :file:`MANIFEST` doesn't exist, read :file:`MANIFEST.in` - and create the manifest +* if the manifest file, :file:`MANIFEST` exists and the first line does not + have a comment indicating it is generated from :file:`MANIFEST.in`, then it + is used as is, unaltered + +* if the manifest file, :file:`MANIFEST` doesn't exist or has been previously + automatically generated, read :file:`MANIFEST.in` and create the manifest * if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest with just the default file set @@ -208,4 +212,5 @@ distribution:: .. versionchanged:: 3.1 An existing generated :file:`MANIFEST` will be regenerated without :command:`sdist` comparing its modification time to the one of - :file:`MANIFEST.in` or :file:`setup.py`. + :file:`MANIFEST.in` or :file:`setup.py` if it has the comment at the + top indicating it was previously automatically generated. diff --git a/Lib/distutils/command/sdist.py b/Lib/distutils/command/sdist.py --- a/Lib/distutils/command/sdist.py +++ b/Lib/distutils/command/sdist.py @@ -176,14 +176,20 @@ class sdist(Command): reading the manifest, or just using the default file set -- it all depends on the user's options. """ - # new behavior: + # new behavior when using a template: # the file list is recalculated everytime because # even if MANIFEST.in or setup.py are not changed # the user might have added some files in the tree that # need to be included. # - # This makes --force the default and only behavior. + # This makes --force the default and only behavior with templates. template_exists = os.path.isfile(self.template) + if not template_exists and not self._is_manifest_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + if not template_exists: self.warn(("manifest template '%s' does not exist " + "(using default file list)") % @@ -333,10 +339,9 @@ class sdist(Command): vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) - def write_manifest(self): - """Write the file list in 'self.filelist' (presumably as filled in - by 'add_defaults()' and 'read_template()') to the manifest file - named by 'self.manifest'. + def _is_manifest_generated(self): + """Look at 'self.manifest' to check if it is dynamically generated from + 'self.template', and return True if it is. """ if os.path.isfile(self.manifest): fp = open(self.manifest) @@ -345,10 +350,18 @@ class sdist(Command): finally: fp.close() - if first_line != '# file GENERATED by distutils, do NOT edit\n': - log.info("not writing to manually maintained " - "manifest file '%s'" % self.manifest) - return + return first_line == '# file GENERATED by distutils, do NOT edit\n' + return True + + def write_manifest(self): + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. + """ + if not self._is_manifest_generated(): + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return content = self.filelist.files[:] content.insert(0, '# file GENERATED by distutils, do NOT edit') @@ -362,12 +375,11 @@ class sdist(Command): """ log.info("reading manifest file '%s'", self.manifest) manifest = open(self.manifest) - while True: - line = manifest.readline() - if line == '': # end of file - break - if line[-1] == '\n': - line = line[0:-1] + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue self.filelist.append(line) manifest.close() diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -7,6 +7,7 @@ from os.path import join import sys import tempfile import warnings +import tarfile from test.support import captured_stdout, check_warnings, run_unittest @@ -363,6 +364,24 @@ class SDistTestCase(PyPIRCCommandTestCas self.assertEqual(manifest, ['README.manual']) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_manifest_respected(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'test-file') + self.write_file((self.tmp_dir, 'test-file'), '') + cmd.run() + + self.assertEqual(cmd.filelist.files, ['test-file']) + + + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + filenames = [tarinfo.name for tarinfo in archive] + self.assertEqual(filenames, + ['fake-1.0', 'fake-1.0/PKG-INFO', 'fake-1.0/test-file']) + def test_suite(): return unittest.makeSuite(SDistTestCase)