Branch merge

This commit is contained in:
Éric Araujo 2011-08-01 14:44:17 +02:00
commit 7dfdac0b5a
8 changed files with 148 additions and 78 deletions

View File

@ -103,10 +103,20 @@ per line, regular files (or symlinks to them) only. If you do supply your own
: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 @@ 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` 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 @@ distribution::
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`.

View File

@ -174,14 +174,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 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 @@ class sdist(Command):
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 @@ 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()

View File

@ -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 @@ class SDistTestCase(PyPIRCCommandTestCase):
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 @@ class SDistTestCase(PyPIRCCommandTestCase):
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)

View File

@ -177,22 +177,26 @@ from __future__ import print_function"""
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):

View File

@ -39,20 +39,40 @@ def record_pieces(file):
return [path, digest, size]
class CommonDistributionTests:
class FakeDistsMixin:
def setUp(self):
super(FakeDistsMixin, self).setUp()
self.addCleanup(enable_cache)
disable_cache()
# make a copy that we can write into for our fake installed
# distributions
tmpdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, tmpdir)
self.fake_dists_path = os.path.join(tmpdir, 'fake_dists')
fake_dists_src = os.path.abspath(
os.path.join(os.path.dirname(__file__), 'fake_dists'))
shutil.copytree(fake_dists_src, self.fake_dists_path)
# XXX ugly workaround: revert copystat calls done by shutil behind our
# back (to avoid getting a read-only copy of a read-only file). we
# could pass a custom copy_function to change the mode of files, but
# shutil gives no control over the mode of directories :(
for root, dirs, files in os.walk(self.fake_dists_path):
os.chmod(root, 0o755)
for f in files:
os.chmod(os.path.join(root, f), 0o644)
for d in dirs:
os.chmod(os.path.join(root, d), 0o755)
class CommonDistributionTests(FakeDistsMixin):
"""Mixin used to test the interface common to both Distribution classes.
Derived classes define cls, sample_dist, dirs and records. These
attributes are used in test methods. See source code for details.
"""
def setUp(self):
super(CommonDistributionTests, self).setUp()
self.addCleanup(enable_cache)
disable_cache()
self.fake_dists_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), 'fake_dists'))
def test_instantiation(self):
# check that useful attributes are here
name, version, distdir = self.sample_dist
@ -110,6 +130,7 @@ class TestDistribution(CommonDistributionTests, unittest.TestCase):
self.records = {}
for distinfo_dir in self.dirs:
record_file = os.path.join(distinfo_dir, 'RECORD')
with open(record_file, 'w') as file:
record_writer = csv.writer(
@ -138,12 +159,6 @@ class TestDistribution(CommonDistributionTests, unittest.TestCase):
record_data[path] = md5_, size
self.records[distinfo_dir] = record_data
def tearDown(self):
for distinfo_dir in self.dirs:
record_file = os.path.join(distinfo_dir, 'RECORD')
open(record_file, 'wb').close()
super(TestDistribution, self).tearDown()
def test_instantiation(self):
super(TestDistribution, self).test_instantiation()
self.assertIsInstance(self.dist.requested, bool)
@ -252,20 +267,13 @@ class TestEggInfoDistribution(CommonDistributionTests,
class TestDatabase(support.LoggingCatcher,
FakeDistsMixin,
unittest.TestCase):
def setUp(self):
super(TestDatabase, self).setUp()
disable_cache()
# Setup the path environment with our fake distributions
current_path = os.path.abspath(os.path.dirname(__file__))
self.fake_dists_path = os.path.join(current_path, 'fake_dists')
sys.path.insert(0, self.fake_dists_path)
def tearDown(self):
sys.path.remove(self.fake_dists_path)
enable_cache()
super(TestDatabase, self).tearDown()
self.addCleanup(sys.path.remove, self.fake_dists_path)
def test_distinfo_dirname(self):
# Given a name and a version, we expect the distinfo_dirname function

View File

@ -228,6 +228,7 @@ Vincent Delft
Arnaud Delobelle
Konrad Delong
Erik Demaine
John Dennis
Roger Dev
Raghuram Devarakonda
Caleb Deveraux
@ -931,6 +932,7 @@ Mikhail Terekhov
Tobias Thelen
James Thomas
Robin Thomas
Stephen Thorne
Jeremy Thurgood
Eric Tiedemann
July Tikhonov

View File

@ -249,6 +249,9 @@ Core and Builtins
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,12 @@ Extension Modules
Tests
-----
- Issue #12331: The test suite for the packaging module can now run from an
installed Python.
- 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

View File

@ -4,11 +4,15 @@ import sys
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 "")
@ -35,18 +39,13 @@ def status(message, modal=False, info=None):
@status("Getting the list of files that have been added/changed",
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'):
cmd = 'hg status --added --modified --no-status'
else:
"""Get the list of changed or added files from Mercurial."""
if not os.path.isdir(os.path.join(SRCDIR, '.hg')):
sys.exit('need a checkout to get modified files')
st = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
try:
st.wait()
cmd = 'hg status --added --modified --no-status'
with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
return [x.decode().rstrip() for x in st.stdout]
finally:
st.stdout.close()
def report_modified_files(file_paths):
@ -65,7 +64,7 @@ def normalize_whitespace(file_paths):
"""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 +73,11 @@ def normalize_c_whitespace(file_paths):
"""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 +88,14 @@ ws_re = re.compile(br'\s+(\r?\n)$')
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: