Home | History | Annotate | Download | only in tests
      1 """Tests for distutils.command.sdist."""
      2 import os
      3 import tarfile
      4 import unittest
      5 import warnings
      6 import zipfile
      7 from os.path import join
      8 from textwrap import dedent
      9 from test.test_support import captured_stdout, check_warnings, run_unittest
     10 
     11 # zlib is not used here, but if it's not available
     12 # the tests that use zipfile may fail
     13 try:
     14     import zlib
     15 except ImportError:
     16     zlib = None
     17 
     18 try:
     19     import grp
     20     import pwd
     21     UID_GID_SUPPORT = True
     22 except ImportError:
     23     UID_GID_SUPPORT = False
     24 
     25 
     26 from distutils.command.sdist import sdist, show_formats
     27 from distutils.core import Distribution
     28 from distutils.tests.test_config import PyPIRCCommandTestCase
     29 from distutils.errors import DistutilsOptionError
     30 from distutils.spawn import find_executable
     31 from distutils.log import WARN
     32 from distutils.filelist import FileList
     33 from distutils.archive_util import ARCHIVE_FORMATS
     34 
     35 SETUP_PY = """
     36 from distutils.core import setup
     37 import somecode
     38 
     39 setup(name='fake')
     40 """
     41 
     42 MANIFEST = """\
     43 # file GENERATED by distutils, do NOT edit
     44 README
     45 buildout.cfg
     46 inroot.txt
     47 setup.py
     48 data%(sep)sdata.dt
     49 scripts%(sep)sscript.py
     50 some%(sep)sfile.txt
     51 some%(sep)sother_file.txt
     52 somecode%(sep)s__init__.py
     53 somecode%(sep)sdoc.dat
     54 somecode%(sep)sdoc.txt
     55 """
     56 
     57 class SDistTestCase(PyPIRCCommandTestCase):
     58 
     59     def setUp(self):
     60         # PyPIRCCommandTestCase creates a temp dir already
     61         # and put it in self.tmp_dir
     62         super(SDistTestCase, self).setUp()
     63         # setting up an environment
     64         self.old_path = os.getcwd()
     65         os.mkdir(join(self.tmp_dir, 'somecode'))
     66         os.mkdir(join(self.tmp_dir, 'dist'))
     67         # a package, and a README
     68         self.write_file((self.tmp_dir, 'README'), 'xxx')
     69         self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
     70         self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
     71         os.chdir(self.tmp_dir)
     72 
     73     def tearDown(self):
     74         # back to normal
     75         os.chdir(self.old_path)
     76         super(SDistTestCase, self).tearDown()
     77 
     78     def get_cmd(self, metadata=None):
     79         """Returns a cmd"""
     80         if metadata is None:
     81             metadata = {'name': 'fake', 'version': '1.0',
     82                         'url': 'xxx', 'author': 'xxx',
     83                         'author_email': 'xxx'}
     84         dist = Distribution(metadata)
     85         dist.script_name = 'setup.py'
     86         dist.packages = ['somecode']
     87         dist.include_package_data = True
     88         cmd = sdist(dist)
     89         cmd.dist_dir = 'dist'
     90         return dist, cmd
     91 
     92     @unittest.skipUnless(zlib, "requires zlib")
     93     def test_prune_file_list(self):
     94         # this test creates a project with some VCS dirs and an NFS rename
     95         # file, then launches sdist to check they get pruned on all systems
     96 
     97         # creating VCS directories with some files in them
     98         os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
     99         self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
    100 
    101         os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
    102         self.write_file((self.tmp_dir, 'somecode', '.hg',
    103                          'ok'), 'xxx')
    104 
    105         os.mkdir(join(self.tmp_dir, 'somecode', '.git'))
    106         self.write_file((self.tmp_dir, 'somecode', '.git',
    107                          'ok'), 'xxx')
    108 
    109         self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx')
    110 
    111         # now building a sdist
    112         dist, cmd = self.get_cmd()
    113 
    114         # zip is available universally
    115         # (tar might not be installed under win32)
    116         cmd.formats = ['zip']
    117 
    118         cmd.ensure_finalized()
    119         cmd.run()
    120 
    121         # now let's check what we have
    122         dist_folder = join(self.tmp_dir, 'dist')
    123         files = os.listdir(dist_folder)
    124         self.assertEqual(files, ['fake-1.0.zip'])
    125 
    126         zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
    127         try:
    128             content = zip_file.namelist()
    129         finally:
    130             zip_file.close()
    131 
    132         # making sure everything has been pruned correctly
    133         self.assertEqual(len(content), 4)
    134 
    135     @unittest.skipUnless(zlib, "requires zlib")
    136     def test_make_distribution(self):
    137         # now building a sdist
    138         dist, cmd = self.get_cmd()
    139 
    140         # creating a gztar then a tar
    141         cmd.formats = ['gztar', 'tar']
    142         cmd.ensure_finalized()
    143         cmd.run()
    144 
    145         # making sure we have two files
    146         dist_folder = join(self.tmp_dir, 'dist')
    147         result = os.listdir(dist_folder)
    148         result.sort()
    149         self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
    150 
    151         os.remove(join(dist_folder, 'fake-1.0.tar'))
    152         os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
    153 
    154         # now trying a tar then a gztar
    155         cmd.formats = ['tar', 'gztar']
    156 
    157         cmd.ensure_finalized()
    158         cmd.run()
    159 
    160         result = os.listdir(dist_folder)
    161         result.sort()
    162         self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
    163 
    164     @unittest.skipUnless(zlib, "requires zlib")
    165     def test_unicode_metadata_tgz(self):
    166         """
    167         Unicode name or version should not break building to tar.gz format.
    168         Reference issue #11638.
    169         """
    170 
    171         # create the sdist command with unicode parameters
    172         dist, cmd = self.get_cmd({'name': u'fake', 'version': u'1.0'})
    173 
    174         # create the sdist as gztar and run the command
    175         cmd.formats = ['gztar']
    176         cmd.ensure_finalized()
    177         cmd.run()
    178 
    179         # The command should have created the .tar.gz file
    180         dist_folder = join(self.tmp_dir, 'dist')
    181         result = os.listdir(dist_folder)
    182         self.assertEqual(result, ['fake-1.0.tar.gz'])
    183 
    184         os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
    185 
    186     @unittest.skipUnless(zlib, "requires zlib")
    187     def test_add_defaults(self):
    188 
    189         # http://bugs.python.org/issue2279
    190 
    191         # add_default should also include
    192         # data_files and package_data
    193         dist, cmd = self.get_cmd()
    194 
    195         # filling data_files by pointing files
    196         # in package_data
    197         dist.package_data = {'': ['*.cfg', '*.dat'],
    198                              'somecode': ['*.txt']}
    199         self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
    200         self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
    201 
    202         # adding some data in data_files
    203         data_dir = join(self.tmp_dir, 'data')
    204         os.mkdir(data_dir)
    205         self.write_file((data_dir, 'data.dt'), '#')
    206         some_dir = join(self.tmp_dir, 'some')
    207         os.mkdir(some_dir)
    208         # make sure VCS directories are pruned (#14004)
    209         hg_dir = join(self.tmp_dir, '.hg')
    210         os.mkdir(hg_dir)
    211         self.write_file((hg_dir, 'last-message.txt'), '#')
    212         # a buggy regex used to prevent this from working on windows (#6884)
    213         self.write_file((self.tmp_dir, 'buildout.cfg'), '#')
    214         self.write_file((self.tmp_dir, 'inroot.txt'), '#')
    215         self.write_file((some_dir, 'file.txt'), '#')
    216         self.write_file((some_dir, 'other_file.txt'), '#')
    217 
    218         dist.data_files = [('data', ['data/data.dt',
    219                                      'buildout.cfg',
    220                                      'inroot.txt',
    221                                      'notexisting']),
    222                            'some/file.txt',
    223                            'some/other_file.txt']
    224 
    225         # adding a script
    226         script_dir = join(self.tmp_dir, 'scripts')
    227         os.mkdir(script_dir)
    228         self.write_file((script_dir, 'script.py'), '#')
    229         dist.scripts = [join('scripts', 'script.py')]
    230 
    231         cmd.formats = ['zip']
    232         cmd.use_defaults = True
    233 
    234         cmd.ensure_finalized()
    235         cmd.run()
    236 
    237         # now let's check what we have
    238         dist_folder = join(self.tmp_dir, 'dist')
    239         files = os.listdir(dist_folder)
    240         self.assertEqual(files, ['fake-1.0.zip'])
    241 
    242         zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
    243         try:
    244             content = zip_file.namelist()
    245         finally:
    246             zip_file.close()
    247 
    248         # making sure everything was added
    249         self.assertEqual(len(content), 12)
    250 
    251         # checking the MANIFEST
    252         f = open(join(self.tmp_dir, 'MANIFEST'))
    253         try:
    254             manifest = f.read()
    255         finally:
    256             f.close()
    257         self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
    258 
    259     @unittest.skipUnless(zlib, "requires zlib")
    260     def test_metadata_check_option(self):
    261         # testing the `medata-check` option
    262         dist, cmd = self.get_cmd(metadata={})
    263 
    264         # this should raise some warnings !
    265         # with the `check` subcommand
    266         cmd.ensure_finalized()
    267         cmd.run()
    268         warnings = [msg for msg in self.get_logs(WARN) if
    269                     msg.startswith('warning: check:')]
    270         self.assertEqual(len(warnings), 2)
    271 
    272         # trying with a complete set of metadata
    273         self.clear_logs()
    274         dist, cmd = self.get_cmd()
    275         cmd.ensure_finalized()
    276         cmd.metadata_check = 0
    277         cmd.run()
    278         warnings = [msg for msg in self.get_logs(WARN) if
    279                     msg.startswith('warning: check:')]
    280         self.assertEqual(len(warnings), 0)
    281 
    282     def test_check_metadata_deprecated(self):
    283         # makes sure make_metadata is deprecated
    284         dist, cmd = self.get_cmd()
    285         with check_warnings() as w:
    286             warnings.simplefilter("always")
    287             cmd.check_metadata()
    288             self.assertEqual(len(w.warnings), 1)
    289 
    290     def test_show_formats(self):
    291         with captured_stdout() as stdout:
    292             show_formats()
    293 
    294         # the output should be a header line + one line per format
    295         num_formats = len(ARCHIVE_FORMATS.keys())
    296         output = [line for line in stdout.getvalue().split('\n')
    297                   if line.strip().startswith('--formats=')]
    298         self.assertEqual(len(output), num_formats)
    299 
    300     def test_finalize_options(self):
    301         dist, cmd = self.get_cmd()
    302         cmd.finalize_options()
    303 
    304         # default options set by finalize
    305         self.assertEqual(cmd.manifest, 'MANIFEST')
    306         self.assertEqual(cmd.template, 'MANIFEST.in')
    307         self.assertEqual(cmd.dist_dir, 'dist')
    308 
    309         # formats has to be a string splitable on (' ', ',') or
    310         # a stringlist
    311         cmd.formats = 1
    312         self.assertRaises(DistutilsOptionError, cmd.finalize_options)
    313         cmd.formats = ['zip']
    314         cmd.finalize_options()
    315 
    316         # formats has to be known
    317         cmd.formats = 'supazipa'
    318         self.assertRaises(DistutilsOptionError, cmd.finalize_options)
    319 
    320     @unittest.skipUnless(zlib, "requires zlib")
    321     @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
    322     @unittest.skipIf(find_executable('tar') is None,
    323                      "The tar command is not found")
    324     @unittest.skipIf(find_executable('gzip') is None,
    325                      "The gzip command is not found")
    326     def test_make_distribution_owner_group(self):
    327         # now building a sdist
    328         dist, cmd = self.get_cmd()
    329 
    330         # creating a gztar and specifying the owner+group
    331         cmd.formats = ['gztar']
    332         cmd.owner = pwd.getpwuid(0)[0]
    333         cmd.group = grp.getgrgid(0)[0]
    334         cmd.ensure_finalized()
    335         cmd.run()
    336 
    337         # making sure we have the good rights
    338         archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
    339         archive = tarfile.open(archive_name)
    340         try:
    341             for member in archive.getmembers():
    342                 self.assertEqual(member.uid, 0)
    343                 self.assertEqual(member.gid, 0)
    344         finally:
    345             archive.close()
    346 
    347         # building a sdist again
    348         dist, cmd = self.get_cmd()
    349 
    350         # creating a gztar
    351         cmd.formats = ['gztar']
    352         cmd.ensure_finalized()
    353         cmd.run()
    354 
    355         # making sure we have the good rights
    356         archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
    357         archive = tarfile.open(archive_name)
    358 
    359         # note that we are not testing the group ownership here
    360         # because, depending on the platforms and the container
    361         # rights (see #7408)
    362         try:
    363             for member in archive.getmembers():
    364                 self.assertEqual(member.uid, os.getuid())
    365         finally:
    366             archive.close()
    367 
    368     # the following tests make sure there is a nice error message instead
    369     # of a traceback when parsing an invalid manifest template
    370 
    371     def _check_template(self, content):
    372         dist, cmd = self.get_cmd()
    373         os.chdir(self.tmp_dir)
    374         self.write_file('MANIFEST.in', content)
    375         cmd.ensure_finalized()
    376         cmd.filelist = FileList()
    377         cmd.read_template()
    378         warnings = self.get_logs(WARN)
    379         self.assertEqual(len(warnings), 1)
    380 
    381     def test_invalid_template_unknown_command(self):
    382         self._check_template('taunt knights *')
    383 
    384     def test_invalid_template_wrong_arguments(self):
    385         # this manifest command takes one argument
    386         self._check_template('prune')
    387 
    388     @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only')
    389     def test_invalid_template_wrong_path(self):
    390         # on Windows, trailing slashes are not allowed
    391         # this used to crash instead of raising a warning: #8286
    392         self._check_template('include examples/')
    393 
    394     @unittest.skipUnless(zlib, "requires zlib")
    395     def test_get_file_list(self):
    396         # make sure MANIFEST is recalculated
    397         dist, cmd = self.get_cmd()
    398 
    399         # filling data_files by pointing files in package_data
    400         dist.package_data = {'somecode': ['*.txt']}
    401         self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
    402         cmd.formats = ['gztar']
    403         cmd.ensure_finalized()
    404         cmd.run()
    405 
    406         f = open(cmd.manifest)
    407         try:
    408             manifest = [line.strip() for line in f.read().split('\n')
    409                         if line.strip() != '']
    410         finally:
    411             f.close()
    412 
    413         self.assertEqual(len(manifest), 5)
    414 
    415         # adding a file
    416         self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
    417 
    418         # make sure build_py is reinitialized, like a fresh run
    419         build_py = dist.get_command_obj('build_py')
    420         build_py.finalized = False
    421         build_py.ensure_finalized()
    422 
    423         cmd.run()
    424 
    425         f = open(cmd.manifest)
    426         try:
    427             manifest2 = [line.strip() for line in f.read().split('\n')
    428                          if line.strip() != '']
    429         finally:
    430             f.close()
    431 
    432         # do we have the new file in MANIFEST ?
    433         self.assertEqual(len(manifest2), 6)
    434         self.assertIn('doc2.txt', manifest2[-1])
    435 
    436     @unittest.skipUnless(zlib, "requires zlib")
    437     def test_manifest_marker(self):
    438         # check that autogenerated MANIFESTs have a marker
    439         dist, cmd = self.get_cmd()
    440         cmd.ensure_finalized()
    441         cmd.run()
    442 
    443         f = open(cmd.manifest)
    444         try:
    445             manifest = [line.strip() for line in f.read().split('\n')
    446                         if line.strip() != '']
    447         finally:
    448             f.close()
    449 
    450         self.assertEqual(manifest[0],
    451                          '# file GENERATED by distutils, do NOT edit')
    452 
    453     @unittest.skipUnless(zlib, 'requires zlib')
    454     def test_manifest_comments(self):
    455         # make sure comments don't cause exceptions or wrong includes
    456         contents = dedent("""\
    457             # bad.py
    458             #bad.py
    459             good.py
    460             """)
    461         dist, cmd = self.get_cmd()
    462         cmd.ensure_finalized()
    463         self.write_file((self.tmp_dir, cmd.manifest), contents)
    464         self.write_file((self.tmp_dir, 'good.py'), '# pick me!')
    465         self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!")
    466         self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!")
    467         cmd.run()
    468         self.assertEqual(cmd.filelist.files, ['good.py'])
    469 
    470     @unittest.skipUnless(zlib, "requires zlib")
    471     def test_manual_manifest(self):
    472         # check that a MANIFEST without a marker is left alone
    473         dist, cmd = self.get_cmd()
    474         cmd.formats = ['gztar']
    475         cmd.ensure_finalized()
    476         self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
    477         self.write_file((self.tmp_dir, 'README.manual'),
    478                          'This project maintains its MANIFEST file itself.')
    479         cmd.run()
    480         self.assertEqual(cmd.filelist.files, ['README.manual'])
    481 
    482         f = open(cmd.manifest)
    483         try:
    484             manifest = [line.strip() for line in f.read().split('\n')
    485                         if line.strip() != '']
    486         finally:
    487             f.close()
    488 
    489         self.assertEqual(manifest, ['README.manual'])
    490 
    491         archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
    492         archive = tarfile.open(archive_name)
    493         try:
    494             filenames = [tarinfo.name for tarinfo in archive]
    495         finally:
    496             archive.close()
    497         self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO',
    498                                              'fake-1.0/README.manual'])
    499 
    500 def test_suite():
    501     return unittest.makeSuite(SDistTestCase)
    502 
    503 if __name__ == "__main__":
    504     run_unittest(test_suite())
    505