Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ab7c1b3

Browse files
committed
Fix regression with distutils MANIFEST handing (#11104, #8688).
The changed behavior of sdist in 3.1 broke packaging for projects that wanted to use a manually-maintained MANIFEST file (instead of having a MANIFEST.in template and letting distutils generate the MANIFEST). The fixes that were committed for #8688 (76643c286b9f by Tarek and d54da9248ed9 by me) did not fix all issues exposed in the bug report, and also added one problem: the MANIFEST file format gained comments, but the read_manifest method was not updated to handle (i.e. ignore) them. This changeset should fix everything; the tests have been expanded and I successfully tested the 2.7 version with Mercurial, which suffered from this regression. I have grouped the versionchanged directives for these bugs in one place and added micro version numbers to help users know the quirks of the exact version they’re using. Initial report, thorough diagnosis and patch by John Dennis, further work on the patch by Stephen Thorne, and a few edits and additions by me.
1 parent a3e072b commit ab7c1b3

5 files changed

Lines changed: 84 additions & 33 deletions

File tree

Doc/distutils/sourcedist.rst

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,20 @@ per line, regular files (or symlinks to them) only. If you do supply your own
103103
:file:`MANIFEST`, you must specify everything: the default set of files
104104
described above does not apply in this case.
105105

106-
.. versionadded:: 3.1
106+
.. versionchanged:: 3.1
107+
An existing generated :file:`MANIFEST` will be regenerated without
108+
:command:`sdist` comparing its modification time to the one of
109+
:file:`MANIFEST.in` or :file:`setup.py`.
110+
111+
.. versionchanged:: 3.1.3
107112
:file:`MANIFEST` files start with a comment indicating they are generated.
108113
Files without this comment are not overwritten or removed.
109114

115+
.. versionchanged:: 3.2.2
116+
:command:`sdist` will read a :file:`MANIFEST` file if no :file:`MANIFEST.in`
117+
exists, like it used to do.
118+
119+
110120
The manifest template has one command per line, where each command specifies a
111121
set of files to include or exclude from the source distribution. For an
112122
example, again we turn to the Distutils' own manifest template::
@@ -185,8 +195,12 @@ Manifest-related options
185195

186196
The normal course of operations for the :command:`sdist` command is as follows:
187197

188-
* if the manifest file, :file:`MANIFEST` doesn't exist, read :file:`MANIFEST.in`
189-
and create the manifest
198+
* if the manifest file (:file:`MANIFEST` by default) exists and the first line
199+
does not have a comment indicating it is generated from :file:`MANIFEST.in`,
200+
then it is used as is, unaltered
201+
202+
* if the manifest file doesn't exist or has been previously automatically
203+
generated, read :file:`MANIFEST.in` and create the manifest
190204

191205
* if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest
192206
with just the default file set
@@ -204,8 +218,3 @@ distribution::
204218
python setup.py sdist --manifest-only
205219

206220
:option:`-o` is a shortcut for :option:`--manifest-only`.
207-
208-
.. versionchanged:: 3.1
209-
An existing generated :file:`MANIFEST` will be regenerated without
210-
:command:`sdist` comparing its modification time to the one of
211-
:file:`MANIFEST.in` or :file:`setup.py`.

Lib/distutils/command/sdist.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,20 @@ def get_file_list(self):
174174
reading the manifest, or just using the default file set -- it all
175175
depends on the user's options.
176176
"""
177-
# new behavior:
177+
# new behavior when using a template:
178178
# the file list is recalculated everytime because
179179
# even if MANIFEST.in or setup.py are not changed
180180
# the user might have added some files in the tree that
181181
# need to be included.
182182
#
183-
# This makes --force the default and only behavior.
183+
# This makes --force the default and only behavior with templates.
184184
template_exists = os.path.isfile(self.template)
185+
if not template_exists and self._manifest_is_not_generated():
186+
self.read_manifest()
187+
self.filelist.sort()
188+
self.filelist.remove_duplicates()
189+
return
190+
185191
if not template_exists:
186192
self.warn(("manifest template '%s' does not exist " +
187193
"(using default file list)") %
@@ -336,36 +342,40 @@ def write_manifest(self):
336342
by 'add_defaults()' and 'read_template()') to the manifest file
337343
named by 'self.manifest'.
338344
"""
339-
if os.path.isfile(self.manifest):
340-
fp = open(self.manifest)
341-
try:
342-
first_line = fp.readline()
343-
finally:
344-
fp.close()
345-
346-
if first_line != '# file GENERATED by distutils, do NOT edit\n':
347-
log.info("not writing to manually maintained "
348-
"manifest file '%s'" % self.manifest)
349-
return
345+
if self._manifest_is_not_generated():
346+
log.info("not writing to manually maintained "
347+
"manifest file '%s'" % self.manifest)
348+
return
350349

351350
content = self.filelist.files[:]
352351
content.insert(0, '# file GENERATED by distutils, do NOT edit')
353352
self.execute(file_util.write_file, (self.manifest, content),
354353
"writing manifest file '%s'" % self.manifest)
355354

355+
def _manifest_is_not_generated(self):
356+
# check for special comment used in 3.1.3 and higher
357+
if not os.path.isfile(self.manifest):
358+
return False
359+
360+
fp = open(self.manifest)
361+
try:
362+
first_line = fp.readline()
363+
finally:
364+
fp.close()
365+
return first_line != '# file GENERATED by distutils, do NOT edit\n'
366+
356367
def read_manifest(self):
357368
"""Read the manifest file (named by 'self.manifest') and use it to
358369
fill in 'self.filelist', the list of files to include in the source
359370
distribution.
360371
"""
361372
log.info("reading manifest file '%s'", self.manifest)
362373
manifest = open(self.manifest)
363-
while True:
364-
line = manifest.readline()
365-
if line == '': # end of file
366-
break
367-
if line[-1] == '\n':
368-
line = line[0:-1]
374+
for line in manifest:
375+
# ignore comments and blank lines
376+
line = line.strip()
377+
if line.startswith('#') or not line:
378+
continue
369379
self.filelist.append(line)
370380
manifest.close()
371381

Lib/distutils/tests/test_sdist.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
"""Tests for distutils.command.sdist."""
22
import os
3+
import tarfile
34
import unittest
4-
import shutil
5+
import warnings
56
import zipfile
67
from os.path import join
7-
import sys
8-
import tempfile
9-
import warnings
8+
from textwrap import dedent
109

1110
from test.support import captured_stdout, check_warnings, run_unittest
1211

1312
from distutils.command.sdist import sdist, show_formats
1413
from distutils.core import Distribution
1514
from distutils.tests.test_config import PyPIRCCommandTestCase
16-
from distutils.errors import DistutilsExecError, DistutilsOptionError
15+
from distutils.errors import DistutilsOptionError
1716
from distutils.spawn import find_executable
18-
from distutils.tests import support
1917
from distutils.log import WARN
2018
from distutils.archive_util import ARCHIVE_FORMATS
2119

@@ -346,13 +344,33 @@ def test_manifest_marker(self):
346344
self.assertEqual(manifest[0],
347345
'# file GENERATED by distutils, do NOT edit')
348346

347+
@unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run")
348+
def test_manifest_comments(self):
349+
# make sure comments don't cause exceptions or wrong includes
350+
contents = dedent("""\
351+
# bad.py
352+
#bad.py
353+
good.py
354+
""")
355+
dist, cmd = self.get_cmd()
356+
cmd.ensure_finalized()
357+
self.write_file((self.tmp_dir, cmd.manifest), contents)
358+
self.write_file((self.tmp_dir, 'good.py'), '# pick me!')
359+
self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!")
360+
self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!")
361+
cmd.run()
362+
self.assertEqual(cmd.filelist.files, ['good.py'])
363+
349364
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
350365
def test_manual_manifest(self):
351366
# check that a MANIFEST without a marker is left alone
352367
dist, cmd = self.get_cmd()
353368
cmd.ensure_finalized()
354369
self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
370+
self.write_file((self.tmp_dir, 'README.manual'),
371+
'This project maintains its MANIFEST file itself.')
355372
cmd.run()
373+
self.assertEqual(cmd.filelist.files, ['README.manual'])
356374

357375
f = open(cmd.manifest)
358376
try:
@@ -363,6 +381,15 @@ def test_manual_manifest(self):
363381

364382
self.assertEqual(manifest, ['README.manual'])
365383

384+
archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
385+
archive = tarfile.open(archive_name)
386+
try:
387+
filenames = [tarinfo.name for tarinfo in archive]
388+
finally:
389+
archive.close()
390+
self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO',
391+
'fake-1.0/README.manual'])
392+
366393
def test_suite():
367394
return unittest.makeSuite(SDistTestCase)
368395

Misc/ACKS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ Ned Deily
215215
Vincent Delft
216216
Arnaud Delobelle
217217
Erik Demaine
218+
John Dennis
218219
Roger Dev
219220
Raghuram Devarakonda
220221
Caleb Deveraux
@@ -875,6 +876,7 @@ Mikhail Terekhov
875876
Tobias Thelen
876877
James Thomas
877878
Robin Thomas
879+
Stephen Thorne
878880
Jeremy Thurgood
879881
Eric Tiedemann
880882
July Tikhonov

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ Core and Builtins
4141
Library
4242
-------
4343

44+
- Issues #11104, #8688: Fix the behavior of distutils' sdist command with
45+
manually-maintained MANIFEST files.
46+
4447
- Issue #12464: tempfile.TemporaryDirectory.cleanup() should not follow
4548
symlinks: fix it. Patch by Petri Lehtinen.
4649

0 commit comments

Comments
 (0)