test_sdist.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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 PyPIRCCommandTestCase
  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(PyPIRCCommandTestCase):
  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. self.assertEqual(len(content), 4)
  111. @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
  112. @unittest.skipIf(find_executable('tar') is None,
  113. "The tar command is not found")
  114. @unittest.skipIf(find_executable('gzip') is None,
  115. "The gzip command is not found")
  116. def test_make_distribution(self):
  117. # now building a sdist
  118. dist, cmd = self.get_cmd()
  119. # creating a gztar then a tar
  120. cmd.formats = ['gztar', 'tar']
  121. cmd.ensure_finalized()
  122. cmd.run()
  123. # making sure we have two files
  124. dist_folder = join(self.tmp_dir, 'dist')
  125. result = os.listdir(dist_folder)
  126. result.sort()
  127. self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
  128. os.remove(join(dist_folder, 'fake-1.0.tar'))
  129. os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
  130. # now trying a tar then a gztar
  131. cmd.formats = ['tar', 'gztar']
  132. cmd.ensure_finalized()
  133. cmd.run()
  134. result = os.listdir(dist_folder)
  135. result.sort()
  136. self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
  137. @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
  138. def test_add_defaults(self):
  139. # http://bugs.python.org/issue2279
  140. # add_default should also include
  141. # data_files and package_data
  142. dist, cmd = self.get_cmd()
  143. # filling data_files by pointing files
  144. # in package_data
  145. dist.package_data = {'': ['*.cfg', '*.dat'],
  146. 'somecode': ['*.txt']}
  147. self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
  148. self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
  149. # adding some data in data_files
  150. data_dir = join(self.tmp_dir, 'data')
  151. os.mkdir(data_dir)
  152. self.write_file((data_dir, 'data.dt'), '#')
  153. some_dir = join(self.tmp_dir, 'some')
  154. os.mkdir(some_dir)
  155. # make sure VCS directories are pruned (#14004)
  156. hg_dir = join(self.tmp_dir, '.hg')
  157. os.mkdir(hg_dir)
  158. self.write_file((hg_dir, 'last-message.txt'), '#')
  159. # a buggy regex used to prevent this from working on windows (#6884)
  160. self.write_file((self.tmp_dir, 'buildout.cfg'), '#')
  161. self.write_file((self.tmp_dir, 'inroot.txt'), '#')
  162. self.write_file((some_dir, 'file.txt'), '#')
  163. self.write_file((some_dir, 'other_file.txt'), '#')
  164. dist.data_files = [('data', ['data/data.dt',
  165. 'buildout.cfg',
  166. 'inroot.txt',
  167. 'notexisting']),
  168. 'some/file.txt',
  169. 'some/other_file.txt']
  170. # adding a script
  171. script_dir = join(self.tmp_dir, 'scripts')
  172. os.mkdir(script_dir)
  173. self.write_file((script_dir, 'script.py'), '#')
  174. dist.scripts = [join('scripts', 'script.py')]
  175. cmd.formats = ['zip']
  176. cmd.use_defaults = True
  177. cmd.ensure_finalized()
  178. cmd.run()
  179. # now let's check what we have
  180. dist_folder = join(self.tmp_dir, 'dist')
  181. files = os.listdir(dist_folder)
  182. self.assertEqual(files, ['fake-1.0.zip'])
  183. zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
  184. try:
  185. content = zip_file.namelist()
  186. finally:
  187. zip_file.close()
  188. # making sure everything was added
  189. self.assertEqual(len(content), 12)
  190. # checking the MANIFEST
  191. f = open(join(self.tmp_dir, 'MANIFEST'))
  192. try:
  193. manifest = f.read()
  194. finally:
  195. f.close()
  196. self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
  197. @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
  198. def test_metadata_check_option(self):
  199. # testing the `medata-check` option
  200. dist, cmd = self.get_cmd(metadata={})
  201. # this should raise some warnings !
  202. # with the `check` subcommand
  203. cmd.ensure_finalized()
  204. cmd.run()
  205. warnings = [msg for msg in self.get_logs(WARN) if
  206. msg.startswith('warning: check:')]
  207. self.assertEqual(len(warnings), 2)
  208. # trying with a complete set of metadata
  209. self.clear_logs()
  210. dist, cmd = self.get_cmd()
  211. cmd.ensure_finalized()
  212. cmd.metadata_check = 0
  213. cmd.run()
  214. warnings = [msg for msg in self.get_logs(WARN) if
  215. msg.startswith('warning: check:')]
  216. self.assertEqual(len(warnings), 0)
  217. def test_check_metadata_deprecated(self):
  218. # makes sure make_metadata is deprecated
  219. dist, cmd = self.get_cmd()
  220. with check_warnings() as w:
  221. warnings.simplefilter("always")
  222. cmd.check_metadata()
  223. self.assertEqual(len(w.warnings), 1)
  224. def test_show_formats(self):
  225. with captured_stdout() as stdout:
  226. show_formats()
  227. # the output should be a header line + one line per format
  228. num_formats = len(ARCHIVE_FORMATS.keys())
  229. output = [line for line in stdout.getvalue().split('\n')
  230. if line.strip().startswith('--formats=')]
  231. self.assertEqual(len(output), num_formats)
  232. def test_finalize_options(self):
  233. dist, cmd = self.get_cmd()
  234. cmd.finalize_options()
  235. # default options set by finalize
  236. self.assertEqual(cmd.manifest, 'MANIFEST')
  237. self.assertEqual(cmd.template, 'MANIFEST.in')
  238. self.assertEqual(cmd.dist_dir, 'dist')
  239. # formats has to be a string splitable on (' ', ',') or
  240. # a stringlist
  241. cmd.formats = 1
  242. self.assertRaises(DistutilsOptionError, cmd.finalize_options)
  243. cmd.formats = ['zip']
  244. cmd.finalize_options()
  245. # formats has to be known
  246. cmd.formats = 'supazipa'
  247. self.assertRaises(DistutilsOptionError, cmd.finalize_options)
  248. # the following tests make sure there is a nice error message instead
  249. # of a traceback when parsing an invalid manifest template
  250. def _check_template(self, content):
  251. dist, cmd = self.get_cmd()
  252. os.chdir(self.tmp_dir)
  253. self.write_file('MANIFEST.in', content)
  254. cmd.ensure_finalized()
  255. cmd.filelist = FileList()
  256. cmd.read_template()
  257. warnings = self.get_logs(WARN)
  258. self.assertEqual(len(warnings), 1)
  259. def test_invalid_template_unknown_command(self):
  260. self._check_template('taunt knights *')
  261. def test_invalid_template_wrong_arguments(self):
  262. # this manifest command takes one argument
  263. self._check_template('prune')
  264. @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only')
  265. def test_invalid_template_wrong_path(self):
  266. # on Windows, trailing slashes are not allowed
  267. # this used to crash instead of raising a warning: #8286
  268. self._check_template('include examples/')
  269. @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
  270. def test_get_file_list(self):
  271. # make sure MANIFEST is recalculated
  272. dist, cmd = self.get_cmd()
  273. # filling data_files by pointing files in package_data
  274. dist.package_data = {'somecode': ['*.txt']}
  275. self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
  276. cmd.formats = ['gztar']
  277. cmd.ensure_finalized()
  278. cmd.run()
  279. f = open(cmd.manifest)
  280. try:
  281. manifest = [line.strip() for line in f.read().split('\n')
  282. if line.strip() != '']
  283. finally:
  284. f.close()
  285. self.assertEqual(len(manifest), 5)
  286. # adding a file
  287. self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
  288. # make sure build_py is reinitialized, like a fresh run
  289. build_py = dist.get_command_obj('build_py')
  290. build_py.finalized = False
  291. build_py.ensure_finalized()
  292. cmd.run()
  293. f = open(cmd.manifest)
  294. try:
  295. manifest2 = [line.strip() for line in f.read().split('\n')
  296. if line.strip() != '']
  297. finally:
  298. f.close()
  299. # do we have the new file in MANIFEST ?
  300. self.assertEqual(len(manifest2), 6)
  301. self.assertIn('doc2.txt', manifest2[-1])
  302. @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
  303. def test_manifest_marker(self):
  304. # check that autogenerated MANIFESTs have a marker
  305. dist, cmd = self.get_cmd()
  306. cmd.ensure_finalized()
  307. cmd.run()
  308. f = open(cmd.manifest)
  309. try:
  310. manifest = [line.strip() for line in f.read().split('\n')
  311. if line.strip() != '']
  312. finally:
  313. f.close()
  314. self.assertEqual(manifest[0],
  315. '# file GENERATED by distutils, do NOT edit')
  316. @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run")
  317. def test_manifest_comments(self):
  318. # make sure comments don't cause exceptions or wrong includes
  319. contents = dedent("""\
  320. # bad.py
  321. #bad.py
  322. good.py
  323. """)
  324. dist, cmd = self.get_cmd()
  325. cmd.ensure_finalized()
  326. self.write_file((self.tmp_dir, cmd.manifest), contents)
  327. self.write_file((self.tmp_dir, 'good.py'), '# pick me!')
  328. self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!")
  329. self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!")
  330. cmd.run()
  331. self.assertEqual(cmd.filelist.files, ['good.py'])
  332. @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
  333. def test_manual_manifest(self):
  334. # check that a MANIFEST without a marker is left alone
  335. dist, cmd = self.get_cmd()
  336. cmd.formats = ['gztar']
  337. cmd.ensure_finalized()
  338. self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
  339. self.write_file((self.tmp_dir, 'README.manual'),
  340. 'This project maintains its MANIFEST file itself.')
  341. cmd.run()
  342. self.assertEqual(cmd.filelist.files, ['README.manual'])
  343. f = open(cmd.manifest)
  344. try:
  345. manifest = [line.strip() for line in f.read().split('\n')
  346. if line.strip() != '']
  347. finally:
  348. f.close()
  349. self.assertEqual(manifest, ['README.manual'])
  350. archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
  351. archive = tarfile.open(archive_name)
  352. try:
  353. filenames = [tarinfo.name for tarinfo in archive]
  354. finally:
  355. archive.close()
  356. self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO',
  357. 'fake-1.0/README.manual'])
  358. @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib")
  359. @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
  360. @unittest.skipIf(find_executable('tar') is None,
  361. "The tar command is not found")
  362. @unittest.skipIf(find_executable('gzip') is None,
  363. "The gzip command is not found")
  364. def test_make_distribution_owner_group(self):
  365. # now building a sdist
  366. dist, cmd = self.get_cmd()
  367. # creating a gztar and specifying the owner+group
  368. cmd.formats = ['gztar']
  369. cmd.owner = pwd.getpwuid(0)[0]
  370. cmd.group = grp.getgrgid(0)[0]
  371. cmd.ensure_finalized()
  372. cmd.run()
  373. # making sure we have the good rights
  374. archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
  375. archive = tarfile.open(archive_name)
  376. try:
  377. for member in archive.getmembers():
  378. self.assertEqual(member.uid, 0)
  379. self.assertEqual(member.gid, 0)
  380. finally:
  381. archive.close()
  382. # building a sdist again
  383. dist, cmd = self.get_cmd()
  384. # creating a gztar
  385. cmd.formats = ['gztar']
  386. cmd.ensure_finalized()
  387. cmd.run()
  388. # making sure we have the good rights
  389. archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
  390. archive = tarfile.open(archive_name)
  391. # note that we are not testing the group ownership here
  392. # because, depending on the platforms and the container
  393. # rights (see #7408)
  394. try:
  395. for member in archive.getmembers():
  396. self.assertEqual(member.uid, os.getuid())
  397. finally:
  398. archive.close()
  399. def test_suite():
  400. return unittest.makeSuite(SDistTestCase)
  401. if __name__ == "__main__":
  402. run_unittest(test_suite())