test_sdist.py 17 KB

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