Skip to content

Commit ad548b8

Browse files
committed
Merge fixes for python#9860, python#11104/python#8688 and python#12331 from 3.2
2 parents 052c83c + 548c054 commit ad548b8

7 files changed

Lines changed: 111 additions & 47 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

Lib/lib2to3/tests/test_refactor.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,22 +177,26 @@ def print_output(self, old_text, new_text, filename, equal):
177177
self.assertEqual(results, expected)
178178

179179
def check_file_refactoring(self, test_file, fixers=_2TO3_FIXERS):
180+
tmpdir = tempfile.mkdtemp(prefix="2to3-test_refactor")
181+
self.addCleanup(shutil.rmtree, tmpdir)
182+
# make a copy of the tested file that we can write to
183+
shutil.copy(test_file, tmpdir)
184+
test_file = os.path.join(tmpdir, os.path.basename(test_file))
185+
os.chmod(test_file, 0o644)
186+
180187
def read_file():
181188
with open(test_file, "rb") as fp:
182189
return fp.read()
190+
183191
old_contents = read_file()
184192
rt = self.rt(fixers=fixers)
185193

186194
rt.refactor_file(test_file)
187195
self.assertEqual(old_contents, read_file())
188196

189-
try:
190-
rt.refactor_file(test_file, True)
191-
new_contents = read_file()
192-
self.assertNotEqual(old_contents, new_contents)
193-
finally:
194-
with open(test_file, "wb") as fp:
195-
fp.write(old_contents)
197+
rt.refactor_file(test_file, True)
198+
new_contents = read_file()
199+
self.assertNotEqual(old_contents, new_contents)
196200
return new_contents
197201

198202
def test_refactor_file(self):

Misc/ACKS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ Vincent Delft
228228
Arnaud Delobelle
229229
Konrad Delong
230230
Erik Demaine
231+
John Dennis
231232
Roger Dev
232233
Raghuram Devarakonda
233234
Caleb Deveraux
@@ -931,6 +932,7 @@ Mikhail Terekhov
931932
Tobias Thelen
932933
James Thomas
933934
Robin Thomas
935+
Stephen Thorne
934936
Jeremy Thurgood
935937
Eric Tiedemann
936938
July Tikhonov

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ Core and Builtins
249249
Library
250250
-------
251251

252+
- Issues #11104, #8688: Fix the behavior of distutils' sdist command with
253+
manually-maintained MANIFEST files.
254+
252255
- Issue #11281: smtplib.STMP gets source_address parameter, which adds the
253256
ability to bind to specific source address on a machine with multiple
254257
interfaces. Patch by Paulo Scardine.
@@ -1144,6 +1147,9 @@ Extension Modules
11441147
Tests
11451148
-----
11461149

1150+
- Issue #12331: The test suite for lib2to3 can now run from an installed
1151+
Python.
1152+
11471153
- Issue #12626: In regrtest, allow to filter tests using a glob filter
11481154
with the ``-m`` (or ``--match``) option. This works with all test cases
11491155
using the unittest module. This is useful with long test suites

Tools/scripts/patchcheck.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import shutil
55
import os.path
66
import subprocess
7+
import sysconfig
78

89
import reindent
910
import untabify
1011

1112

13+
SRCDIR = sysconfig.get_config_var('srcdir')
14+
15+
1216
def n_files_str(count):
1317
"""Return 'N file(s)' with the proper plurality on 'file'."""
1418
return "{} file{}".format(count, "s" if count != 1 else "")
@@ -36,7 +40,7 @@ def call_fxn(*args, **kwargs):
3640
info=lambda x: n_files_str(len(x)))
3741
def changed_files():
3842
"""Get the list of changed or added files from the VCS."""
39-
if os.path.isdir('.hg'):
43+
if os.path.isdir(os.path.join(SRCDIR, '.hg')):
4044
cmd = 'hg status --added --modified --no-status'
4145
else:
4246
sys.exit('need a checkout to get modified files')
@@ -65,7 +69,7 @@ def normalize_whitespace(file_paths):
6569
"""Make sure that the whitespace for .py files have been normalized."""
6670
reindent.makebackup = False # No need to create backups.
6771
fixed = [path for path in file_paths if path.endswith('.py') and
68-
reindent.check(path)]
72+
reindent.check(os.path.join(SRCDIR, path))]
6973
return fixed
7074

7175

@@ -74,10 +78,11 @@ def normalize_c_whitespace(file_paths):
7478
"""Report if any C files """
7579
fixed = []
7680
for path in file_paths:
77-
with open(path, 'r') as f:
81+
abspath = os.path.join(SRCDIR, path)
82+
with open(abspath, 'r') as f:
7883
if '\t' not in f.read():
7984
continue
80-
untabify.process(path, 8, verbose=False)
85+
untabify.process(abspath, 8, verbose=False)
8186
fixed.append(path)
8287
return fixed
8388

@@ -88,13 +93,14 @@ def normalize_c_whitespace(file_paths):
8893
def normalize_docs_whitespace(file_paths):
8994
fixed = []
9095
for path in file_paths:
96+
abspath = os.path.join(SRCDIR, path)
9197
try:
92-
with open(path, 'rb') as f:
98+
with open(abspath, 'rb') as f:
9399
lines = f.readlines()
94100
new_lines = [ws_re.sub(br'\1', line) for line in lines]
95101
if new_lines != lines:
96-
shutil.copyfile(path, path + '.bak')
97-
with open(path, 'wb') as f:
102+
shutil.copyfile(abspath, abspath + '.bak')
103+
with open(abspath, 'wb') as f:
98104
f.writelines(new_lines)
99105
fixed.append(path)
100106
except Exception as err:

0 commit comments

Comments
 (0)