»Core Development>Code coverage>Lib/packaging/tests/test_config.py

Python code coverage for Lib/packaging/tests/test_config.py

#countcontent
1n/a"""Tests for packaging.config."""
2n/aimport os
3n/aimport sys
4n/a
5n/afrom packaging import command
6n/afrom packaging.dist import Distribution
7n/afrom packaging.errors import PackagingFileError, PackagingOptionError
8n/afrom packaging.compiler import new_compiler, _COMPILERS
9n/afrom packaging.command.sdist import sdist
10n/a
11n/afrom packaging.tests import unittest, support
12n/afrom packaging.tests.support import requires_zlib
13n/a
14n/a
15n/aSETUP_CFG = """
16n/a[metadata]
17n/aname = RestingParrot
18n/aversion = 0.6.4
19n/aauthor = Carl Meyer
20n/aauthor_email = carl@oddbird.net
21n/amaintainer = Éric Araujo
22n/amaintainer_email = merwok@netwok.org
23n/asummary = A sample project demonstrating packaging
24n/adescription-file = %(description-file)s
25n/akeywords = packaging, sample project
26n/a
27n/aclassifier =
28n/a Development Status :: 4 - Beta
29n/a Environment :: Console (Text Based)
30n/a Environment :: X11 Applications :: GTK; python_version < '3'
31n/a License :: OSI Approved :: MIT License
32n/a Programming Language :: Python
33n/a Programming Language :: Python :: 2
34n/a Programming Language :: Python :: 3
35n/a
36n/arequires_python = >=2.4, <3.2
37n/a
38n/arequires_dist =
39n/a PetShoppe
40n/a MichaelPalin (> 1.1)
41n/a pywin32; sys.platform == 'win32'
42n/a pysqlite2; python_version < '2.5'
43n/a inotify (0.0.1); sys.platform == 'linux2'
44n/a
45n/arequires_external = libxml2
46n/a
47n/aprovides_dist = packaging-sample-project (0.2)
48n/a unittest2-sample-project
49n/a
50n/aproject_url =
51n/a Main repository, http://bitbucket.org/carljm/sample-distutils2-project
52n/a Fork in progress, http://bitbucket.org/Merwok/sample-distutils2-project
53n/a
54n/a[files]
55n/apackages_root = src
56n/a
57n/apackages = one
58n/a two
59n/a three
60n/a
61n/amodules = haven
62n/a
63n/ascripts =
64n/a script1.py
65n/a scripts/find-coconuts
66n/a bin/taunt
67n/a
68n/apackage_data =
69n/a cheese = data/templates/* doc/*
70n/a doc/images/*.png
71n/a
72n/a
73n/aextra_files = %(extra-files)s
74n/a
75n/a# Replaces MANIFEST.in
76n/a# FIXME no, it's extra_files
77n/a# (but sdist_extra is a better name, should use it)
78n/asdist_extra =
79n/a include THANKS HACKING
80n/a recursive-include examples *.txt *.py
81n/a prune examples/sample?/build
82n/a
83n/aresources=
84n/a bm/ {b1,b2}.gif = {icon}
85n/a Cf*/ *.CFG = {config}/baBar/
86n/a init_script = {script}/JunGle/
87n/a
88n/a[global]
89n/acommands =
90n/a packaging.tests.test_config.FooBarBazTest
91n/a
92n/acompilers =
93n/a packaging.tests.test_config.DCompiler
94n/a
95n/asetup_hooks = %(setup-hooks)s
96n/a
97n/a
98n/a
99n/a[install_dist]
100n/asub_commands = foo
101n/a"""
102n/a
103n/aSETUP_CFG_PKGDATA_BUGGY_1 = """
104n/a[files]
105n/apackage_data = foo.*
106n/a"""
107n/a
108n/aSETUP_CFG_PKGDATA_BUGGY_2 = """
109n/a[files]
110n/apackage_data =
111n/a foo.*
112n/a"""
113n/a
114n/a# Can not be merged with SETUP_CFG else install_dist
115n/a# command will fail when trying to compile C sources
116n/a# TODO use a DummyCommand to mock build_ext
117n/aEXT_SETUP_CFG = """
118n/a[files]
119n/apackages = one
120n/a two
121n/a parent.undeclared
122n/a
123n/a[extension:one.speed_coconuts]
124n/asources = c_src/speed_coconuts.c
125n/aextra_link_args = "`gcc -print-file-name=libgcc.a`" -shared
126n/adefine_macros = HAVE_CAIRO HAVE_GTK2
127n/alibraries = gecodeint gecodekernel -- sys.platform != 'win32'
128n/a GecodeInt GecodeKernel -- sys.platform == 'win32'
129n/a
130n/a[extension: two.fast_taunt]
131n/asources = cxx_src/utils_taunt.cxx
132n/a cxx_src/python_module.cxx
133n/ainclude_dirs = /usr/include/gecode
134n/a /usr/include/blitz
135n/aextra_compile_args = -fPIC -O2
136n/a -DGECODE_VERSION=$(./gecode_version) -- sys.platform != 'win32'
137n/a /DGECODE_VERSION=win32 -- sys.platform == 'win32'
138n/alanguage = cxx
139n/a
140n/a# corner case: if the parent package of an extension is declared but
141n/a# not its grandparent, it's legal
142n/a[extension: parent.undeclared._speed]
143n/asources = parent/undeclared/_speed.c
144n/a"""
145n/a
146n/aEXT_SETUP_CFG_BUGGY_1 = """
147n/a[extension: realname]
148n/aname = crash_here
149n/a"""
150n/a
151n/aEXT_SETUP_CFG_BUGGY_2 = """
152n/a[files]
153n/apackages = ham
154n/a
155n/a[extension: spam.eggs]
156n/a"""
157n/a
158n/aEXT_SETUP_CFG_BUGGY_3 = """
159n/a[files]
160n/apackages = ok
161n/a ok.works
162n/a
163n/a[extension: ok.works.breaks._ext]
164n/a"""
165n/a
166n/aHOOKS_MODULE = """
167n/aimport logging
168n/a
169n/alogger = logging.getLogger('packaging')
170n/a
171n/adef logging_hook(config):
172n/a logger.warning('logging_hook called')
173n/a"""
174n/a
175n/a
176n/aclass DCompiler:
177n/a name = 'd'
178n/a description = 'D Compiler'
179n/a
180n/a def __init__(self, *args):
181n/a pass
182n/a
183n/a
184n/adef version_hook(config):
185n/a config['metadata']['version'] += '.dev1'
186n/a
187n/a
188n/adef first_hook(config):
189n/a config['files']['modules'] += '\n first'
190n/a
191n/a
192n/adef third_hook(config):
193n/a config['files']['modules'] += '\n third'
194n/a
195n/a
196n/aclass FooBarBazTest:
197n/a
198n/a def __init__(self, dist):
199n/a self.distribution = dist
200n/a self._record = []
201n/a
202n/a @classmethod
203n/a def get_command_name(cls):
204n/a return 'foo'
205n/a
206n/a def run(self):
207n/a self._record.append('foo has run')
208n/a
209n/a def nothing(self):
210n/a pass
211n/a
212n/a def get_source_files(self):
213n/a return []
214n/a
215n/a ensure_finalized = finalize_options = initialize_options = nothing
216n/a
217n/a
218n/aclass ConfigTestCase(support.TempdirManager,
219n/a support.EnvironRestorer,
220n/a support.LoggingCatcher,
221n/a unittest.TestCase):
222n/a
223n/a restore_environ = ['PLAT']
224n/a
225n/a def setUp(self):
226n/a super(ConfigTestCase, self).setUp()
227n/a tempdir = self.mkdtemp()
228n/a self.working_dir = os.getcwd()
229n/a os.chdir(tempdir)
230n/a self.tempdir = tempdir
231n/a
232n/a def write_setup(self, kwargs=None):
233n/a opts = {'description-file': 'README', 'extra-files': '',
234n/a 'setup-hooks': 'packaging.tests.test_config.version_hook'}
235n/a if kwargs:
236n/a opts.update(kwargs)
237n/a self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8')
238n/a
239n/a def get_dist(self):
240n/a dist = Distribution()
241n/a dist.parse_config_files()
242n/a return dist
243n/a
244n/a def test_config(self):
245n/a self.write_setup()
246n/a self.write_file('README', 'yeah')
247n/a os.mkdir('bm')
248n/a self.write_file(('bm', 'b1.gif'), '')
249n/a self.write_file(('bm', 'b2.gif'), '')
250n/a os.mkdir('Cfg')
251n/a self.write_file(('Cfg', 'data.CFG'), '')
252n/a self.write_file('init_script', '')
253n/a
254n/a # try to load the metadata now
255n/a dist = self.get_dist()
256n/a
257n/a # check what was done
258n/a self.assertEqual(dist.metadata['Author'], 'Carl Meyer')
259n/a self.assertEqual(dist.metadata['Author-Email'], 'carl@oddbird.net')
260n/a
261n/a # the hook adds .dev1
262n/a self.assertEqual(dist.metadata['Version'], '0.6.4.dev1')
263n/a
264n/a wanted = [
265n/a 'Development Status :: 4 - Beta',
266n/a 'Environment :: Console (Text Based)',
267n/a "Environment :: X11 Applications :: GTK; python_version < '3'",
268n/a 'License :: OSI Approved :: MIT License',
269n/a 'Programming Language :: Python',
270n/a 'Programming Language :: Python :: 2',
271n/a 'Programming Language :: Python :: 3']
272n/a self.assertEqual(dist.metadata['Classifier'], wanted)
273n/a
274n/a wanted = ['packaging', 'sample project']
275n/a self.assertEqual(dist.metadata['Keywords'], wanted)
276n/a
277n/a self.assertEqual(dist.metadata['Requires-Python'], '>=2.4, <3.2')
278n/a
279n/a wanted = ['PetShoppe',
280n/a 'MichaelPalin (> 1.1)',
281n/a "pywin32; sys.platform == 'win32'",
282n/a "pysqlite2; python_version < '2.5'",
283n/a "inotify (0.0.1); sys.platform == 'linux2'"]
284n/a
285n/a self.assertEqual(dist.metadata['Requires-Dist'], wanted)
286n/a urls = [('Main repository',
287n/a 'http://bitbucket.org/carljm/sample-distutils2-project'),
288n/a ('Fork in progress',
289n/a 'http://bitbucket.org/Merwok/sample-distutils2-project')]
290n/a self.assertEqual(dist.metadata['Project-Url'], urls)
291n/a
292n/a self.assertEqual(dist.packages, ['one', 'two', 'three'])
293n/a self.assertEqual(dist.py_modules, ['haven'])
294n/a self.assertEqual(dist.package_data,
295n/a {'cheese': ['data/templates/*', 'doc/*',
296n/a 'doc/images/*.png']})
297n/a self.assertEqual(dist.data_files,
298n/a {'bm/b1.gif': '{icon}/b1.gif',
299n/a 'bm/b2.gif': '{icon}/b2.gif',
300n/a 'Cfg/data.CFG': '{config}/baBar/data.CFG',
301n/a 'init_script': '{script}/JunGle/init_script'})
302n/a
303n/a self.assertEqual(dist.package_dir, 'src')
304n/a
305n/a # Make sure we get the foo command loaded. We use a string comparison
306n/a # instead of assertIsInstance because the class is not the same when
307n/a # this test is run directly: foo is packaging.tests.test_config.Foo
308n/a # because get_command_class uses the full name, but a bare "Foo" in
309n/a # this file would be __main__.Foo when run as "python test_config.py".
310n/a # The name FooBarBazTest should be unique enough to prevent
311n/a # collisions.
312n/a self.assertEqual(dist.get_command_obj('foo').__class__.__name__,
313n/a 'FooBarBazTest')
314n/a
315n/a # did the README got loaded ?
316n/a self.assertEqual(dist.metadata['description'], 'yeah')
317n/a
318n/a # do we have the D Compiler enabled ?
319n/a self.assertIn('d', _COMPILERS)
320n/a d = new_compiler(compiler='d')
321n/a self.assertEqual(d.description, 'D Compiler')
322n/a
323n/a # check error reporting for invalid package_data value
324n/a self.write_file('setup.cfg', SETUP_CFG_PKGDATA_BUGGY_1)
325n/a self.assertRaises(PackagingOptionError, self.get_dist)
326n/a
327n/a self.write_file('setup.cfg', SETUP_CFG_PKGDATA_BUGGY_2)
328n/a self.assertRaises(PackagingOptionError, self.get_dist)
329n/a
330n/a def test_multiple_description_file(self):
331n/a self.write_setup({'description-file': 'README CHANGES'})
332n/a self.write_file('README', 'yeah')
333n/a self.write_file('CHANGES', 'changelog2')
334n/a dist = self.get_dist()
335n/a self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
336n/a
337n/a def test_multiline_description_file(self):
338n/a self.write_setup({'description-file': 'README\n CHANGES'})
339n/a self.write_file('README', 'yeah')
340n/a self.write_file('CHANGES', 'changelog')
341n/a dist = self.get_dist()
342n/a self.assertEqual(dist.metadata['description'], 'yeah\nchangelog')
343n/a self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
344n/a
345n/a def test_parse_extensions_in_config(self):
346n/a self.write_file('setup.cfg', EXT_SETUP_CFG)
347n/a dist = self.get_dist()
348n/a
349n/a ext_modules = dict((mod.name, mod) for mod in dist.ext_modules)
350n/a self.assertEqual(len(ext_modules), 3)
351n/a ext = ext_modules.get('one.speed_coconuts')
352n/a self.assertEqual(ext.sources, ['c_src/speed_coconuts.c'])
353n/a self.assertEqual(ext.define_macros, ['HAVE_CAIRO', 'HAVE_GTK2'])
354n/a libs = ['gecodeint', 'gecodekernel']
355n/a if sys.platform == 'win32':
356n/a libs = ['GecodeInt', 'GecodeKernel']
357n/a self.assertEqual(ext.libraries, libs)
358n/a self.assertEqual(ext.extra_link_args,
359n/a ['`gcc -print-file-name=libgcc.a`', '-shared'])
360n/a
361n/a ext = ext_modules.get('two.fast_taunt')
362n/a self.assertEqual(ext.sources,
363n/a ['cxx_src/utils_taunt.cxx', 'cxx_src/python_module.cxx'])
364n/a self.assertEqual(ext.include_dirs,
365n/a ['/usr/include/gecode', '/usr/include/blitz'])
366n/a cargs = ['-fPIC', '-O2']
367n/a if sys.platform == 'win32':
368n/a cargs.append("/DGECODE_VERSION=win32")
369n/a else:
370n/a cargs.append('-DGECODE_VERSION=$(./gecode_version)')
371n/a self.assertEqual(ext.extra_compile_args, cargs)
372n/a self.assertEqual(ext.language, 'cxx')
373n/a
374n/a self.write_file('setup.cfg', EXT_SETUP_CFG_BUGGY_1)
375n/a self.assertRaises(PackagingOptionError, self.get_dist)
376n/a
377n/a self.write_file('setup.cfg', EXT_SETUP_CFG_BUGGY_2)
378n/a self.assertRaises(PackagingOptionError, self.get_dist)
379n/a
380n/a self.write_file('setup.cfg', EXT_SETUP_CFG_BUGGY_3)
381n/a self.assertRaises(PackagingOptionError, self.get_dist)
382n/a
383n/a def test_project_setup_hook_works(self):
384n/a # Bug #11637: ensure the project directory is on sys.path to allow
385n/a # project-specific hooks
386n/a self.write_setup({'setup-hooks': 'hooks.logging_hook'})
387n/a self.write_file('README', 'yeah')
388n/a self.write_file('hooks.py', HOOKS_MODULE)
389n/a self.get_dist()
390n/a self.assertEqual(['logging_hook called'], self.get_logs())
391n/a self.assertIn('hooks', sys.modules)
392n/a
393n/a def test_missing_setup_hook_warns(self):
394n/a self.write_setup({'setup-hooks': 'does._not.exist'})
395n/a self.write_file('README', 'yeah')
396n/a self.get_dist()
397n/a logs = self.get_logs()
398n/a self.assertEqual(1, len(logs))
399n/a self.assertIn('cannot find setup hook', logs[0])
400n/a
401n/a def test_multiple_setup_hooks(self):
402n/a self.write_setup({
403n/a 'setup-hooks': '\n packaging.tests.test_config.first_hook'
404n/a '\n packaging.tests.test_config.missing_hook'
405n/a '\n packaging.tests.test_config.third_hook',
406n/a })
407n/a self.write_file('README', 'yeah')
408n/a dist = self.get_dist()
409n/a
410n/a self.assertEqual(['haven', 'first', 'third'], dist.py_modules)
411n/a logs = self.get_logs()
412n/a self.assertEqual(1, len(logs))
413n/a self.assertIn('cannot find setup hook', logs[0])
414n/a
415n/a def test_metadata_requires_description_files_missing(self):
416n/a self.write_setup({'description-file': 'README README2'})
417n/a self.write_file('README', 'yeah')
418n/a self.write_file('README2', 'yeah')
419n/a os.mkdir('src')
420n/a self.write_file(('src', 'haven.py'), '#')
421n/a self.write_file('script1.py', '#')
422n/a os.mkdir('scripts')
423n/a self.write_file(('scripts', 'find-coconuts'), '#')
424n/a os.mkdir('bin')
425n/a self.write_file(('bin', 'taunt'), '#')
426n/a
427n/a for pkg in ('one', 'two', 'three'):
428n/a pkg = os.path.join('src', pkg)
429n/a os.mkdir(pkg)
430n/a self.write_file((pkg, '__init__.py'), '#')
431n/a
432n/a dist = self.get_dist()
433n/a cmd = sdist(dist)
434n/a cmd.finalize_options()
435n/a cmd.get_file_list()
436n/a self.assertRaises(PackagingFileError, cmd.make_distribution)
437n/a
438n/a @requires_zlib
439n/a def test_metadata_requires_description_files(self):
440n/a # Create the following file structure:
441n/a # README
442n/a # README2
443n/a # script1.py
444n/a # scripts/
445n/a # find-coconuts
446n/a # bin/
447n/a # taunt
448n/a # src/
449n/a # haven.py
450n/a # one/__init__.py
451n/a # two/__init__.py
452n/a # three/__init__.py
453n/a
454n/a self.write_setup({'description-file': 'README\n README2',
455n/a 'extra-files': '\n README3'})
456n/a self.write_file('README', 'yeah 1')
457n/a self.write_file('README2', 'yeah 2')
458n/a self.write_file('README3', 'yeah 3')
459n/a os.mkdir('src')
460n/a self.write_file(('src', 'haven.py'), '#')
461n/a self.write_file('script1.py', '#')
462n/a os.mkdir('scripts')
463n/a self.write_file(('scripts', 'find-coconuts'), '#')
464n/a os.mkdir('bin')
465n/a self.write_file(('bin', 'taunt'), '#')
466n/a
467n/a for pkg in ('one', 'two', 'three'):
468n/a pkg = os.path.join('src', pkg)
469n/a os.mkdir(pkg)
470n/a self.write_file((pkg, '__init__.py'), '#')
471n/a
472n/a dist = self.get_dist()
473n/a self.assertIn('yeah 1\nyeah 2', dist.metadata['description'])
474n/a
475n/a cmd = sdist(dist)
476n/a cmd.finalize_options()
477n/a cmd.get_file_list()
478n/a self.assertRaises(PackagingFileError, cmd.make_distribution)
479n/a
480n/a self.write_setup({'description-file': 'README\n README2',
481n/a 'extra-files': '\n README2\n README'})
482n/a dist = self.get_dist()
483n/a cmd = sdist(dist)
484n/a cmd.finalize_options()
485n/a cmd.get_file_list()
486n/a cmd.make_distribution()
487n/a with open('MANIFEST') as fp:
488n/a self.assertIn('README\nREADME2\n', fp.read())
489n/a
490n/a def test_sub_commands(self):
491n/a self.write_setup()
492n/a self.write_file('README', 'yeah')
493n/a os.mkdir('src')
494n/a self.write_file(('src', 'haven.py'), '#')
495n/a self.write_file('script1.py', '#')
496n/a os.mkdir('scripts')
497n/a self.write_file(('scripts', 'find-coconuts'), '#')
498n/a os.mkdir('bin')
499n/a self.write_file(('bin', 'taunt'), '#')
500n/a
501n/a for pkg in ('one', 'two', 'three'):
502n/a pkg = os.path.join('src', pkg)
503n/a os.mkdir(pkg)
504n/a self.write_file((pkg, '__init__.py'), '#')
505n/a
506n/a # try to run the install command to see if foo is called
507n/a self.addCleanup(command._COMMANDS.__delitem__, 'foo')
508n/a dist = self.get_dist()
509n/a dist.run_command('install_dist')
510n/a cmd = dist.get_command_obj('foo')
511n/a self.assertEqual(cmd.__class__.__name__, 'FooBarBazTest')
512n/a self.assertEqual(cmd._record, ['foo has run'])
513n/a
514n/a
515n/adef test_suite():
516n/a return unittest.makeSuite(ConfigTestCase)
517n/a
518n/aif __name__ == '__main__':
519n/a unittest.main(defaultTest='test_suite')