[Python-checkins] cpython (merge 3.2 -> default): Merge fixes for #9860, #11104/#8688 and #12331 from 3.2

eric.araujo python-checkins at python.org
Mon Aug 1 14:45:25 CEST 2011


http://hg.python.org/cpython/rev/5993f91598ce
changeset:   71666:5993f91598ce
parent:      71632:8d1a046441ea
parent:      71665:2b5a0c4e052b
user:        Éric Araujo <merwok at netwok.org>
date:        Sun Jul 31 18:33:00 2011 +0200
summary:
  Merge fixes for #9860, #11104/#8688 and #12331 from 3.2

files:
  Doc/distutils/sourcedist.rst       |  25 ++++++---
  Lib/distutils/command/sdist.py     |  48 ++++++++++-------
  Lib/distutils/tests/test_sdist.py  |  39 ++++++++++++--
  Lib/lib2to3/tests/test_refactor.py |  18 ++++--
  Misc/ACKS                          |   2 +
  Misc/NEWS                          |   6 ++
  Tools/scripts/patchcheck.py        |  20 ++++--
  7 files changed, 111 insertions(+), 47 deletions(-)


diff --git a/Doc/distutils/sourcedist.rst b/Doc/distutils/sourcedist.rst
--- a/Doc/distutils/sourcedist.rst
+++ b/Doc/distutils/sourcedist.rst
@@ -103,10 +103,20 @@
 :file:`MANIFEST`, you must specify everything: the default set of files
 described above does not apply in this case.
 
-.. versionadded:: 3.1
+.. 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`.
+
+.. versionchanged:: 3.1.3
    :file:`MANIFEST` files start with a comment indicating they are generated.
    Files without this comment are not overwritten or removed.
 
+.. versionchanged:: 3.2.2
+   :command:`sdist` will read a :file:`MANIFEST` file if no :file:`MANIFEST.in`
+   exists, like it used to do.
+
+
 The manifest template has one command per line, where each command specifies a
 set of files to include or exclude from the source distribution.  For an
 example, again we turn to the Distutils' own manifest template::
@@ -185,8 +195,12 @@
 
 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` by default) 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 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
@@ -204,8 +218,3 @@
    python setup.py sdist --manifest-only
 
 :option:`-o` is a shortcut for :option:`--manifest-only`.
-
-.. 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`.
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
@@ -174,14 +174,20 @@
         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 self._manifest_is_not_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)") %
@@ -336,23 +342,28 @@
         by 'add_defaults()' and 'read_template()') to the manifest file
         named by 'self.manifest'.
         """
-        if os.path.isfile(self.manifest):
-            fp = open(self.manifest)
-            try:
-                first_line = fp.readline()
-            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
+        if self._manifest_is_not_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')
         self.execute(file_util.write_file, (self.manifest, content),
                      "writing manifest file '%s'" % self.manifest)
 
+    def _manifest_is_not_generated(self):
+        # check for special comment used in 3.1.3 and higher
+        if not os.path.isfile(self.manifest):
+            return False
+
+        fp = open(self.manifest)
+        try:
+            first_line = fp.readline()
+        finally:
+            fp.close()
+        return first_line != '# file GENERATED by distutils, do NOT edit\n'
+
     def read_manifest(self):
         """Read the manifest file (named by 'self.manifest') and use it to
         fill in 'self.filelist', the list of files to include in the source
@@ -360,12 +371,11 @@
         """
         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
@@ -1,21 +1,19 @@
 """Tests for distutils.command.sdist."""
 import os
+import tarfile
 import unittest
-import shutil
+import warnings
 import zipfile
 from os.path import join
-import sys
-import tempfile
-import warnings
+from textwrap import dedent
 
 from test.support import captured_stdout, check_warnings, run_unittest
 
 from distutils.command.sdist import sdist, show_formats
 from distutils.core import Distribution
 from distutils.tests.test_config import PyPIRCCommandTestCase
-from distutils.errors import DistutilsExecError, DistutilsOptionError
+from distutils.errors import DistutilsOptionError
 from distutils.spawn import find_executable
-from distutils.tests import support
 from distutils.log import WARN
 from distutils.archive_util import ARCHIVE_FORMATS
 
@@ -346,13 +344,33 @@
         self.assertEqual(manifest[0],
                          '# file GENERATED by distutils, do NOT edit')
 
+    @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run")
+    def test_manifest_comments(self):
+        # make sure comments don't cause exceptions or wrong includes
+        contents = dedent("""\
+            # bad.py
+            #bad.py
+            good.py
+            """)
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        self.write_file((self.tmp_dir, cmd.manifest), contents)
+        self.write_file((self.tmp_dir, 'good.py'), '# pick me!')
+        self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!")
+        self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!")
+        cmd.run()
+        self.assertEqual(cmd.filelist.files, ['good.py'])
+
     @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
     def test_manual_manifest(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), 'README.manual')
+        self.write_file((self.tmp_dir, 'README.manual'),
+                         'This project maintains its MANIFEST file itself.')
         cmd.run()
+        self.assertEqual(cmd.filelist.files, ['README.manual'])
 
         f = open(cmd.manifest)
         try:
@@ -363,6 +381,15 @@
 
         self.assertEqual(manifest, ['README.manual'])
 
+        archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+        archive = tarfile.open(archive_name)
+        try:
+            filenames = [tarinfo.name for tarinfo in archive]
+        finally:
+            archive.close()
+        self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO',
+                                             'fake-1.0/README.manual'])
+
 def test_suite():
     return unittest.makeSuite(SDistTestCase)
 
diff --git a/Lib/lib2to3/tests/test_refactor.py b/Lib/lib2to3/tests/test_refactor.py
--- a/Lib/lib2to3/tests/test_refactor.py
+++ b/Lib/lib2to3/tests/test_refactor.py
@@ -177,22 +177,26 @@
         self.assertEqual(results, expected)
 
     def check_file_refactoring(self, test_file, fixers=_2TO3_FIXERS):
+        tmpdir = tempfile.mkdtemp(prefix="2to3-test_refactor")
+        self.addCleanup(shutil.rmtree, tmpdir)
+        # make a copy of the tested file that we can write to
+        shutil.copy(test_file, tmpdir)
+        test_file = os.path.join(tmpdir, os.path.basename(test_file))
+        os.chmod(test_file, 0o644)
+
         def read_file():
             with open(test_file, "rb") as fp:
                 return fp.read()
+
         old_contents = read_file()
         rt = self.rt(fixers=fixers)
 
         rt.refactor_file(test_file)
         self.assertEqual(old_contents, read_file())
 
-        try:
-            rt.refactor_file(test_file, True)
-            new_contents = read_file()
-            self.assertNotEqual(old_contents, new_contents)
-        finally:
-            with open(test_file, "wb") as fp:
-                fp.write(old_contents)
+        rt.refactor_file(test_file, True)
+        new_contents = read_file()
+        self.assertNotEqual(old_contents, new_contents)
         return new_contents
 
     def test_refactor_file(self):
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -228,6 +228,7 @@
 Arnaud Delobelle
 Konrad Delong
 Erik Demaine
+John Dennis
 Roger Dev
 Raghuram Devarakonda
 Caleb Deveraux
@@ -931,6 +932,7 @@
 Tobias Thelen
 James Thomas
 Robin Thomas
+Stephen Thorne
 Jeremy Thurgood
 Eric Tiedemann
 July Tikhonov
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -249,6 +249,9 @@
 Library
 -------
 
+- Issues #11104, #8688: Fix the behavior of distutils' sdist command with
+  manually-maintained MANIFEST files.
+
 - Issue #11281: smtplib.STMP gets source_address parameter, which adds the
   ability to bind to specific source address on a machine with multiple
   interfaces. Patch by Paulo Scardine.
@@ -1144,6 +1147,9 @@
 Tests
 -----
 
+- Issue #12331: The test suite for lib2to3 can now run from an installed
+  Python.
+
 - Issue #12626: In regrtest, allow to filter tests using a glob filter
   with the ``-m`` (or ``--match``) option.  This works with all test cases
   using the unittest module.  This is useful with long test suites
diff --git a/Tools/scripts/patchcheck.py b/Tools/scripts/patchcheck.py
--- a/Tools/scripts/patchcheck.py
+++ b/Tools/scripts/patchcheck.py
@@ -4,11 +4,15 @@
 import shutil
 import os.path
 import subprocess
+import sysconfig
 
 import reindent
 import untabify
 
 
+SRCDIR = sysconfig.get_config_var('srcdir')
+
+
 def n_files_str(count):
     """Return 'N file(s)' with the proper plurality on 'file'."""
     return "{} file{}".format(count, "s" if count != 1 else "")
@@ -36,7 +40,7 @@
         info=lambda x: n_files_str(len(x)))
 def changed_files():
     """Get the list of changed or added files from the VCS."""
-    if os.path.isdir('.hg'):
+    if os.path.isdir(os.path.join(SRCDIR, '.hg')):
         cmd = 'hg status --added --modified --no-status'
     else:
         sys.exit('need a checkout to get modified files')
@@ -65,7 +69,7 @@
     """Make sure that the whitespace for .py files have been normalized."""
     reindent.makebackup = False  # No need to create backups.
     fixed = [path for path in file_paths if path.endswith('.py') and
-             reindent.check(path)]
+             reindent.check(os.path.join(SRCDIR, path))]
     return fixed
 
 
@@ -74,10 +78,11 @@
     """Report if any C files """
     fixed = []
     for path in file_paths:
-        with open(path, 'r') as f:
+        abspath = os.path.join(SRCDIR, path)
+        with open(abspath, 'r') as f:
             if '\t' not in f.read():
                 continue
-        untabify.process(path, 8, verbose=False)
+        untabify.process(abspath, 8, verbose=False)
         fixed.append(path)
     return fixed
 
@@ -88,13 +93,14 @@
 def normalize_docs_whitespace(file_paths):
     fixed = []
     for path in file_paths:
+        abspath = os.path.join(SRCDIR, path)
         try:
-            with open(path, 'rb') as f:
+            with open(abspath, 'rb') as f:
                 lines = f.readlines()
             new_lines = [ws_re.sub(br'\1', line) for line in lines]
             if new_lines != lines:
-                shutil.copyfile(path, path + '.bak')
-                with open(path, 'wb') as f:
+                shutil.copyfile(abspath, abspath + '.bak')
+                with open(abspath, 'wb') as f:
                     f.writelines(new_lines)
                 fixed.append(path)
         except Exception as err:

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list