mirror of
https://github.com/python/cpython.git
synced 2025-09-08 18:01:44 +00:00

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.
397 lines
13 KiB
Python
397 lines
13 KiB
Python
"""Tests for distutils.command.sdist."""
|
|
import os
|
|
import tarfile
|
|
import unittest
|
|
import warnings
|
|
import zipfile
|
|
from os.path import join
|
|
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 DistutilsOptionError
|
|
from distutils.spawn import find_executable
|
|
from distutils.log import WARN
|
|
from distutils.archive_util import ARCHIVE_FORMATS
|
|
|
|
SETUP_PY = """
|
|
from distutils.core import setup
|
|
import somecode
|
|
|
|
setup(name='fake')
|
|
"""
|
|
|
|
MANIFEST = """\
|
|
# file GENERATED by distutils, do NOT edit
|
|
README
|
|
inroot.txt
|
|
setup.py
|
|
data%(sep)sdata.dt
|
|
scripts%(sep)sscript.py
|
|
some%(sep)sfile.txt
|
|
some%(sep)sother_file.txt
|
|
somecode%(sep)s__init__.py
|
|
somecode%(sep)sdoc.dat
|
|
somecode%(sep)sdoc.txt
|
|
"""
|
|
|
|
try:
|
|
import zlib
|
|
ZLIB_SUPPORT = True
|
|
except ImportError:
|
|
ZLIB_SUPPORT = False
|
|
|
|
|
|
class SDistTestCase(PyPIRCCommandTestCase):
|
|
|
|
def setUp(self):
|
|
# PyPIRCCommandTestCase creates a temp dir already
|
|
# and put it in self.tmp_dir
|
|
super(SDistTestCase, self).setUp()
|
|
# setting up an environment
|
|
self.old_path = os.getcwd()
|
|
os.mkdir(join(self.tmp_dir, 'somecode'))
|
|
os.mkdir(join(self.tmp_dir, 'dist'))
|
|
# a package, and a README
|
|
self.write_file((self.tmp_dir, 'README'), 'xxx')
|
|
self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
|
|
self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
|
|
os.chdir(self.tmp_dir)
|
|
|
|
def tearDown(self):
|
|
# back to normal
|
|
os.chdir(self.old_path)
|
|
super(SDistTestCase, self).tearDown()
|
|
|
|
def get_cmd(self, metadata=None):
|
|
"""Returns a cmd"""
|
|
if metadata is None:
|
|
metadata = {'name': 'fake', 'version': '1.0',
|
|
'url': 'xxx', 'author': 'xxx',
|
|
'author_email': 'xxx'}
|
|
dist = Distribution(metadata)
|
|
dist.script_name = 'setup.py'
|
|
dist.packages = ['somecode']
|
|
dist.include_package_data = True
|
|
cmd = sdist(dist)
|
|
cmd.dist_dir = 'dist'
|
|
def _warn(*args):
|
|
pass
|
|
cmd.warn = _warn
|
|
return dist, cmd
|
|
|
|
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
|
|
def test_prune_file_list(self):
|
|
# this test creates a package with some vcs dirs in it
|
|
# and launch sdist to make sure they get pruned
|
|
# on all systems
|
|
|
|
# creating VCS directories with some files in them
|
|
os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
|
|
self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
|
|
|
|
os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
|
|
self.write_file((self.tmp_dir, 'somecode', '.hg',
|
|
'ok'), 'xxx')
|
|
|
|
os.mkdir(join(self.tmp_dir, 'somecode', '.git'))
|
|
self.write_file((self.tmp_dir, 'somecode', '.git',
|
|
'ok'), 'xxx')
|
|
|
|
# now building a sdist
|
|
dist, cmd = self.get_cmd()
|
|
|
|
# zip is available universally
|
|
# (tar might not be installed under win32)
|
|
cmd.formats = ['zip']
|
|
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
|
|
# now let's check what we have
|
|
dist_folder = join(self.tmp_dir, 'dist')
|
|
files = os.listdir(dist_folder)
|
|
self.assertEqual(files, ['fake-1.0.zip'])
|
|
|
|
zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
|
|
try:
|
|
content = zip_file.namelist()
|
|
finally:
|
|
zip_file.close()
|
|
|
|
# making sure everything has been pruned correctly
|
|
self.assertEqual(len(content), 4)
|
|
|
|
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
|
|
def test_make_distribution(self):
|
|
|
|
# check if tar and gzip are installed
|
|
if (find_executable('tar') is None or
|
|
find_executable('gzip') is None):
|
|
return
|
|
|
|
# now building a sdist
|
|
dist, cmd = self.get_cmd()
|
|
|
|
# creating a gztar then a tar
|
|
cmd.formats = ['gztar', 'tar']
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
|
|
# making sure we have two files
|
|
dist_folder = join(self.tmp_dir, 'dist')
|
|
result = os.listdir(dist_folder)
|
|
result.sort()
|
|
self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'] )
|
|
|
|
os.remove(join(dist_folder, 'fake-1.0.tar'))
|
|
os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
|
|
|
|
# now trying a tar then a gztar
|
|
cmd.formats = ['tar', 'gztar']
|
|
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
|
|
result = os.listdir(dist_folder)
|
|
result.sort()
|
|
self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
|
|
|
|
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
|
|
def test_add_defaults(self):
|
|
|
|
# http://bugs.python.org/issue2279
|
|
|
|
# add_default should also include
|
|
# data_files and package_data
|
|
dist, cmd = self.get_cmd()
|
|
|
|
# filling data_files by pointing files
|
|
# in package_data
|
|
dist.package_data = {'': ['*.cfg', '*.dat'],
|
|
'somecode': ['*.txt']}
|
|
self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
|
|
self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
|
|
|
|
# adding some data in data_files
|
|
data_dir = join(self.tmp_dir, 'data')
|
|
os.mkdir(data_dir)
|
|
self.write_file((data_dir, 'data.dt'), '#')
|
|
some_dir = join(self.tmp_dir, 'some')
|
|
os.mkdir(some_dir)
|
|
self.write_file((self.tmp_dir, 'inroot.txt'), '#')
|
|
self.write_file((some_dir, 'file.txt'), '#')
|
|
self.write_file((some_dir, 'other_file.txt'), '#')
|
|
|
|
dist.data_files = [('data', ['data/data.dt',
|
|
'inroot.txt',
|
|
'notexisting']),
|
|
'some/file.txt',
|
|
'some/other_file.txt']
|
|
|
|
# adding a script
|
|
script_dir = join(self.tmp_dir, 'scripts')
|
|
os.mkdir(script_dir)
|
|
self.write_file((script_dir, 'script.py'), '#')
|
|
dist.scripts = [join('scripts', 'script.py')]
|
|
|
|
cmd.formats = ['zip']
|
|
cmd.use_defaults = True
|
|
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
|
|
# now let's check what we have
|
|
dist_folder = join(self.tmp_dir, 'dist')
|
|
files = os.listdir(dist_folder)
|
|
self.assertEqual(files, ['fake-1.0.zip'])
|
|
|
|
zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
|
|
try:
|
|
content = zip_file.namelist()
|
|
finally:
|
|
zip_file.close()
|
|
|
|
# making sure everything was added
|
|
self.assertEqual(len(content), 11)
|
|
|
|
# checking the MANIFEST
|
|
f = open(join(self.tmp_dir, 'MANIFEST'))
|
|
try:
|
|
manifest = f.read()
|
|
self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
|
|
finally:
|
|
f.close()
|
|
|
|
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
|
|
def test_metadata_check_option(self):
|
|
# testing the `medata-check` option
|
|
dist, cmd = self.get_cmd(metadata={})
|
|
|
|
# this should raise some warnings !
|
|
# with the `check` subcommand
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
warnings = self.get_logs(WARN)
|
|
self.assertEqual(len(warnings), 2)
|
|
|
|
# trying with a complete set of metadata
|
|
self.clear_logs()
|
|
dist, cmd = self.get_cmd()
|
|
cmd.ensure_finalized()
|
|
cmd.metadata_check = 0
|
|
cmd.run()
|
|
warnings = self.get_logs(WARN)
|
|
self.assertEqual(len(warnings), 0)
|
|
|
|
def test_check_metadata_deprecated(self):
|
|
# makes sure make_metadata is deprecated
|
|
dist, cmd = self.get_cmd()
|
|
with check_warnings() as w:
|
|
warnings.simplefilter("always")
|
|
cmd.check_metadata()
|
|
self.assertEqual(len(w.warnings), 1)
|
|
|
|
def test_show_formats(self):
|
|
with captured_stdout() as stdout:
|
|
show_formats()
|
|
|
|
# the output should be a header line + one line per format
|
|
num_formats = len(ARCHIVE_FORMATS.keys())
|
|
output = [line for line in stdout.getvalue().split('\n')
|
|
if line.strip().startswith('--formats=')]
|
|
self.assertEqual(len(output), num_formats)
|
|
|
|
def test_finalize_options(self):
|
|
|
|
dist, cmd = self.get_cmd()
|
|
cmd.finalize_options()
|
|
|
|
# default options set by finalize
|
|
self.assertEqual(cmd.manifest, 'MANIFEST')
|
|
self.assertEqual(cmd.template, 'MANIFEST.in')
|
|
self.assertEqual(cmd.dist_dir, 'dist')
|
|
|
|
# formats has to be a string splitable on (' ', ',') or
|
|
# a stringlist
|
|
cmd.formats = 1
|
|
self.assertRaises(DistutilsOptionError, cmd.finalize_options)
|
|
cmd.formats = ['zip']
|
|
cmd.finalize_options()
|
|
|
|
# formats has to be known
|
|
cmd.formats = 'supazipa'
|
|
self.assertRaises(DistutilsOptionError, cmd.finalize_options)
|
|
|
|
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
|
|
def test_get_file_list(self):
|
|
# make sure MANIFEST is recalculated
|
|
dist, cmd = self.get_cmd()
|
|
|
|
# filling data_files by pointing files in package_data
|
|
dist.package_data = {'somecode': ['*.txt']}
|
|
self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
|
|
f = open(cmd.manifest)
|
|
try:
|
|
manifest = [line.strip() for line in f.read().split('\n')
|
|
if line.strip() != '']
|
|
finally:
|
|
f.close()
|
|
|
|
self.assertEqual(len(manifest), 5)
|
|
|
|
# adding a file
|
|
self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
|
|
|
|
# make sure build_py is reinitialized, like a fresh run
|
|
build_py = dist.get_command_obj('build_py')
|
|
build_py.finalized = False
|
|
build_py.ensure_finalized()
|
|
|
|
cmd.run()
|
|
|
|
f = open(cmd.manifest)
|
|
try:
|
|
manifest2 = [line.strip() for line in f.read().split('\n')
|
|
if line.strip() != '']
|
|
finally:
|
|
f.close()
|
|
|
|
# do we have the new file in MANIFEST ?
|
|
self.assertEqual(len(manifest2), 6)
|
|
self.assertIn('doc2.txt', manifest2[-1])
|
|
|
|
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
|
|
def test_manifest_marker(self):
|
|
# check that autogenerated MANIFESTs have a marker
|
|
dist, cmd = self.get_cmd()
|
|
cmd.ensure_finalized()
|
|
cmd.run()
|
|
|
|
f = open(cmd.manifest)
|
|
try:
|
|
manifest = [line.strip() for line in f.read().split('\n')
|
|
if line.strip() != '']
|
|
finally:
|
|
f.close()
|
|
|
|
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:
|
|
manifest = [line.strip() for line in f.read().split('\n')
|
|
if line.strip() != '']
|
|
finally:
|
|
f.close()
|
|
|
|
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)
|
|
|
|
if __name__ == "__main__":
|
|
run_unittest(test_suite())
|