blob: 48335e770556d3e45072ec241230f1e4b2fc0445 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""Tests for packaging.dist."""
2import os
3import io
4import sys
5import logging
6import textwrap
Éric Araujob6be20c2011-06-16 23:34:55 +02007import sysconfig
Tarek Ziade1231a4e2011-05-19 13:07:25 +02008import packaging.dist
9
10from packaging.dist import Distribution
11from packaging.command import set_command
12from packaging.command.cmd import Command
Éric Araujod2c7e3f2011-06-17 13:24:33 +020013from packaging.metadata import Metadata
Tarek Ziade1231a4e2011-05-19 13:07:25 +020014from packaging.errors import PackagingModuleError, PackagingOptionError
15from packaging.tests import TESTFN, captured_stdout
16from packaging.tests import support, unittest
17from packaging.tests.support import create_distribution
Victor Stinner9bcfacd2011-05-24 14:01:39 +020018from test.support import unload
Tarek Ziade1231a4e2011-05-19 13:07:25 +020019
20
21class test_dist(Command):
22 """Sample packaging extension command."""
23
24 user_options = [
25 ("sample-option=", "S", "help text"),
26 ]
27
28 def initialize_options(self):
29 self.sample_option = None
30
31 def finalize_options(self):
32 pass
33
34
35class DistributionTestCase(support.TempdirManager,
36 support.LoggingCatcher,
37 support.EnvironRestorer,
38 unittest.TestCase):
39
Éric Araujo2b612222011-06-10 04:29:43 +020040 restore_environ = ['HOME', 'PLAT']
Tarek Ziade1231a4e2011-05-19 13:07:25 +020041
42 def setUp(self):
43 super(DistributionTestCase, self).setUp()
44 self.argv = sys.argv, sys.argv[:]
45 del sys.argv[1:]
46
47 def tearDown(self):
48 sys.argv = self.argv[0]
49 sys.argv[:] = self.argv[1]
50 super(DistributionTestCase, self).tearDown()
51
52 def test_debug_mode(self):
53 self.addCleanup(os.unlink, TESTFN)
54 with open(TESTFN, "w") as f:
55 f.write("[global]\n")
56 f.write("command_packages = foo.bar, splat")
57
58 files = [TESTFN]
59 sys.argv.append("build")
60 __, stdout = captured_stdout(create_distribution, files)
61 self.assertEqual(stdout, '')
62 packaging.dist.DEBUG = True
63 try:
64 __, stdout = captured_stdout(create_distribution, files)
65 self.assertEqual(stdout, '')
66 finally:
67 packaging.dist.DEBUG = False
68
69 def test_write_pkg_file(self):
70 # Check Metadata handling of Unicode fields
71 tmp_dir = self.mkdtemp()
72 my_file = os.path.join(tmp_dir, 'f')
73 cls = Distribution
74
75 dist = cls(attrs={'author': 'Mister Café',
76 'name': 'my.package',
77 'maintainer': 'Café Junior',
78 'summary': 'Café torréfié',
79 'description': 'Héhéhé'})
80
81 # let's make sure the file can be written
82 # with Unicode fields. they are encoded with
83 # PKG_INFO_ENCODING
Victor Stinnerc3364522011-05-19 18:49:56 +020084 with open(my_file, 'w', encoding='utf-8') as fp:
Tarek Ziade1231a4e2011-05-19 13:07:25 +020085 dist.metadata.write_file(fp)
86
87 # regular ascii is of course always usable
88 dist = cls(attrs={'author': 'Mister Cafe',
89 'name': 'my.package',
90 'maintainer': 'Cafe Junior',
91 'summary': 'Cafe torrefie',
92 'description': 'Hehehe'})
93
94 with open(my_file, 'w') as fp:
95 dist.metadata.write_file(fp)
96
97 def test_bad_attr(self):
98 Distribution(attrs={'author': 'xxx',
99 'name': 'xxx',
100 'version': '1.2',
101 'url': 'xxxx',
102 'badoptname': 'xxx'})
103 logs = self.get_logs(logging.WARNING)
104 self.assertEqual(1, len(logs))
105 self.assertIn('unknown argument', logs[0])
106
107 def test_bad_version(self):
108 Distribution(attrs={'author': 'xxx',
109 'name': 'xxx',
110 'version': 'xxx',
111 'url': 'xxxx'})
112 logs = self.get_logs(logging.WARNING)
113 self.assertEqual(1, len(logs))
114 self.assertIn('not a valid version', logs[0])
115
116 def test_empty_options(self):
117 # an empty options dictionary should not stay in the
118 # list of attributes
119 Distribution(attrs={'author': 'xxx',
120 'name': 'xxx',
121 'version': '1.2',
122 'url': 'xxxx',
123 'options': {}})
124
125 self.assertEqual([], self.get_logs(logging.WARNING))
126
127 def test_non_empty_options(self):
128 # TODO: how to actually use options is not documented except
129 # for a few cryptic comments in dist.py. If this is to stay
130 # in the public API, it deserves some better documentation.
131
132 # Here is an example of how it's used out there:
133 # http://svn.pythonmac.org/py2app/py2app/trunk/doc/
134 # index.html#specifying-customizations
135 dist = Distribution(attrs={'author': 'xxx',
136 'name': 'xxx',
137 'version': 'xxx',
138 'url': 'xxxx',
139 'options': {'sdist': {'owner': 'root'}}})
140
141 self.assertIn('owner', dist.get_option_dict('sdist'))
142
143 def test_finalize_options(self):
144
145 attrs = {'keywords': 'one,two',
146 'platform': 'one,two'}
147
148 dist = Distribution(attrs=attrs)
149 dist.finalize_options()
150
151 # finalize_option splits platforms and keywords
152 self.assertEqual(dist.metadata['platform'], ['one', 'two'])
153 self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
154
155 def test_find_config_files_disable(self):
156 # Bug #1180: Allow users to disable their own config file.
157 temp_home = self.mkdtemp()
158 if os.name == 'posix':
159 user_filename = os.path.join(temp_home, ".pydistutils.cfg")
160 else:
161 user_filename = os.path.join(temp_home, "pydistutils.cfg")
162
163 with open(user_filename, 'w') as f:
164 f.write('[distutils2]\n')
165
166 def _expander(path):
167 return temp_home
168
169 old_expander = os.path.expanduser
170 os.path.expanduser = _expander
171 try:
172 d = packaging.dist.Distribution()
173 all_files = d.find_config_files()
174
175 d = packaging.dist.Distribution(attrs={'script_args':
176 ['--no-user-cfg']})
177 files = d.find_config_files()
178 finally:
179 os.path.expanduser = old_expander
180
181 # make sure --no-user-cfg disables the user cfg file
182 self.assertEqual((len(all_files) - 1), len(files))
183
184 def test_special_hooks_parsing(self):
185 temp_home = self.mkdtemp()
186 config_files = [os.path.join(temp_home, "config1.cfg"),
187 os.path.join(temp_home, "config2.cfg")]
188
189 # Store two aliased hooks in config files
190 self.write_file((temp_home, "config1.cfg"),
191 '[test_dist]\npre-hook.a = type')
192 self.write_file((temp_home, "config2.cfg"),
193 '[test_dist]\npre-hook.b = type')
194
195 set_command('packaging.tests.test_dist.test_dist')
196 dist = create_distribution(config_files)
197 cmd = dist.get_command_obj("test_dist")
198 self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'})
199
200 def test_hooks_get_run(self):
201 temp_home = self.mkdtemp()
Tarek Ziadecd0f7bf2011-05-19 14:46:10 +0200202 module_name = os.path.split(temp_home)[-1]
203 pyname = '%s.py' % module_name
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200204 config_file = os.path.join(temp_home, "config1.cfg")
Tarek Ziadecd0f7bf2011-05-19 14:46:10 +0200205 hooks_module = os.path.join(temp_home, pyname)
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200206
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200207 self.write_file(config_file, textwrap.dedent('''\
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200208 [test_dist]
Tarek Ziadecd0f7bf2011-05-19 14:46:10 +0200209 pre-hook.test = %(modname)s.log_pre_call
210 post-hook.test = %(modname)s.log_post_call'''
211 % {'modname': module_name}))
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200212
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200213 self.write_file(hooks_module, textwrap.dedent('''\
214 record = []
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200215
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200216 def log_pre_call(cmd):
217 record.append('pre-%s' % cmd.get_command_name())
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200218
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200219 def log_post_call(cmd):
220 record.append('post-%s' % cmd.get_command_name())
221 '''))
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200222
223 set_command('packaging.tests.test_dist.test_dist')
224 d = create_distribution([config_file])
225 cmd = d.get_command_obj("test_dist")
226
227 # prepare the call recorders
228 sys.path.append(temp_home)
229 self.addCleanup(sys.path.remove, temp_home)
Victor Stinner9bcfacd2011-05-24 14:01:39 +0200230 self.addCleanup(unload, module_name)
Tarek Ziadecd0f7bf2011-05-19 14:46:10 +0200231 record = __import__(module_name).record
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200232
233 cmd.run = lambda: record.append('run')
234 cmd.finalize_options = lambda: record.append('finalize')
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200235 d.run_command('test_dist')
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200236
237 self.assertEqual(record, ['finalize',
238 'pre-test_dist',
239 'run',
240 'post-test_dist'])
241
242 def test_hooks_importable(self):
243 temp_home = self.mkdtemp()
244 config_file = os.path.join(temp_home, "config1.cfg")
245
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200246 self.write_file(config_file, textwrap.dedent('''\
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200247 [test_dist]
248 pre-hook.test = nonexistent.dotted.name'''))
249
250 set_command('packaging.tests.test_dist.test_dist')
251 d = create_distribution([config_file])
252 cmd = d.get_command_obj("test_dist")
253 cmd.ensure_finalized()
254
255 self.assertRaises(PackagingModuleError, d.run_command, 'test_dist')
256
257 def test_hooks_callable(self):
258 temp_home = self.mkdtemp()
259 config_file = os.path.join(temp_home, "config1.cfg")
260
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200261 self.write_file(config_file, textwrap.dedent('''\
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200262 [test_dist]
263 pre-hook.test = packaging.tests.test_dist.__doc__'''))
264
265 set_command('packaging.tests.test_dist.test_dist')
266 d = create_distribution([config_file])
267 cmd = d.get_command_obj("test_dist")
268 cmd.ensure_finalized()
269
270 self.assertRaises(PackagingOptionError, d.run_command, 'test_dist')
271
272
273class MetadataTestCase(support.TempdirManager,
274 support.LoggingCatcher,
275 unittest.TestCase):
276
277 def setUp(self):
278 super(MetadataTestCase, self).setUp()
279 self.argv = sys.argv, sys.argv[:]
280
281 def tearDown(self):
282 sys.argv = self.argv[0]
283 sys.argv[:] = self.argv[1]
284 super(MetadataTestCase, self).tearDown()
285
286 def test_simple_metadata(self):
287 attrs = {"name": "package",
288 "version": "1.0"}
289 dist = Distribution(attrs)
290 meta = self.format_metadata(dist)
291 self.assertIn("Metadata-Version: 1.0", meta)
292 self.assertNotIn("provides:", meta.lower())
293 self.assertNotIn("requires:", meta.lower())
294 self.assertNotIn("obsoletes:", meta.lower())
295
296 def test_provides_dist(self):
297 attrs = {"name": "package",
298 "version": "1.0",
299 "provides_dist": ["package", "package.sub"]}
300 dist = Distribution(attrs)
301 self.assertEqual(dist.metadata['Provides-Dist'],
302 ["package", "package.sub"])
303 meta = self.format_metadata(dist)
304 self.assertIn("Metadata-Version: 1.2", meta)
305 self.assertNotIn("requires:", meta.lower())
306 self.assertNotIn("obsoletes:", meta.lower())
307
308 def _test_provides_illegal(self):
309 # XXX to do: check the versions
310 self.assertRaises(ValueError, Distribution,
311 {"name": "package",
312 "version": "1.0",
313 "provides_dist": ["my.pkg (splat)"]})
314
315 def test_requires_dist(self):
316 attrs = {"name": "package",
317 "version": "1.0",
318 "requires_dist": ["other", "another (==1.0)"]}
319 dist = Distribution(attrs)
320 self.assertEqual(dist.metadata['Requires-Dist'],
321 ["other", "another (==1.0)"])
322 meta = self.format_metadata(dist)
323 self.assertIn("Metadata-Version: 1.2", meta)
324 self.assertNotIn("provides:", meta.lower())
325 self.assertIn("Requires-Dist: other", meta)
326 self.assertIn("Requires-Dist: another (==1.0)", meta)
327 self.assertNotIn("obsoletes:", meta.lower())
328
329 def _test_requires_illegal(self):
330 # XXX
331 self.assertRaises(ValueError, Distribution,
332 {"name": "package",
333 "version": "1.0",
334 "requires": ["my.pkg (splat)"]})
335
336 def test_obsoletes_dist(self):
337 attrs = {"name": "package",
338 "version": "1.0",
339 "obsoletes_dist": ["other", "another (<1.0)"]}
340 dist = Distribution(attrs)
341 self.assertEqual(dist.metadata['Obsoletes-Dist'],
342 ["other", "another (<1.0)"])
343 meta = self.format_metadata(dist)
344 self.assertIn("Metadata-Version: 1.2", meta)
345 self.assertNotIn("provides:", meta.lower())
346 self.assertNotIn("requires:", meta.lower())
347 self.assertIn("Obsoletes-Dist: other", meta)
348 self.assertIn("Obsoletes-Dist: another (<1.0)", meta)
349
350 def _test_obsoletes_illegal(self):
351 # XXX
352 self.assertRaises(ValueError, Distribution,
353 {"name": "package",
354 "version": "1.0",
355 "obsoletes": ["my.pkg (splat)"]})
356
357 def format_metadata(self, dist):
358 sio = io.StringIO()
359 dist.metadata.write_file(sio)
360 return sio.getvalue()
361
362 def test_custom_pydistutils(self):
363 # fixes #2166
364 # make sure pydistutils.cfg is found
365 if os.name == 'posix':
366 user_filename = ".pydistutils.cfg"
367 else:
368 user_filename = "pydistutils.cfg"
369
370 temp_dir = self.mkdtemp()
371 user_filename = os.path.join(temp_dir, user_filename)
372 with open(user_filename, 'w') as f:
373 f.write('.')
374
375 dist = Distribution()
376
377 # linux-style
378 if sys.platform in ('linux', 'darwin'):
379 os.environ['HOME'] = temp_dir
380 files = dist.find_config_files()
381 self.assertIn(user_filename, files)
382
383 # win32-style
384 if sys.platform == 'win32':
385 # home drive should be found
386 os.environ['HOME'] = temp_dir
387 files = dist.find_config_files()
388 self.assertIn(user_filename, files)
389
390 def test_show_help(self):
391 # smoke test, just makes sure some help is displayed
392 dist = Distribution()
393 sys.argv = []
394 dist.help = True
Éric Araujob6be20c2011-06-16 23:34:55 +0200395 dist.script_name = os.path.join(sysconfig.get_path('scripts'),
396 'pysetup')
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200397 __, stdout = captured_stdout(dist.parse_command_line)
398 output = [line for line in stdout.split('\n')
399 if line.strip() != '']
400 self.assertGreater(len(output), 0)
401
402 def test_description(self):
403 desc = textwrap.dedent("""\
404 example::
405 We start here
406 and continue here
407 and end here.""")
408 attrs = {"name": "package",
409 "version": "1.0",
410 "description": desc}
411
412 dist = packaging.dist.Distribution(attrs)
413 meta = self.format_metadata(dist)
414 meta = meta.replace('\n' + 7 * ' ' + '|', '\n')
415 self.assertIn(desc, meta)
416
417 def test_read_metadata(self):
418 attrs = {"name": "package",
419 "version": "1.0",
420 "description": "desc",
421 "summary": "xxx",
422 "download_url": "http://example.com",
423 "keywords": ['one', 'two'],
424 "requires_dist": ['foo']}
425
426 dist = Distribution(attrs)
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200427 PKG_INFO = io.StringIO()
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200428 dist.metadata.write_file(PKG_INFO)
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200429 PKG_INFO.seek(0)
430
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200431 metadata = Metadata()
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200432 metadata.read_file(PKG_INFO)
Éric Araujod2c7e3f2011-06-17 13:24:33 +0200433
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200434 self.assertEqual(metadata['name'], "package")
435 self.assertEqual(metadata['version'], "1.0")
436 self.assertEqual(metadata['summary'], "xxx")
437 self.assertEqual(metadata['download_url'], 'http://example.com')
438 self.assertEqual(metadata['keywords'], ['one', 'two'])
439 self.assertEqual(metadata['platform'], [])
440 self.assertEqual(metadata['obsoletes'], [])
441 self.assertEqual(metadata['requires-dist'], ['foo'])
442
443
444def test_suite():
445 suite = unittest.TestSuite()
446 suite.addTest(unittest.makeSuite(DistributionTestCase))
447 suite.addTest(unittest.makeSuite(MetadataTestCase))
448 return suite
449
450if __name__ == "__main__":
451 unittest.main(defaultTest="test_suite")