test_sdist.py 17 KB

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