test_sdist.py 17 KB

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