ยปCore Development>Code coverage>Lib/packaging/tests/test_command_build_ext.py

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

#countcontent
1n/aimport os
2n/aimport sys
3n/aimport site
4n/aimport sysconfig
5n/aimport textwrap
6n/afrom packaging.dist import Distribution
7n/afrom packaging.errors import (UnknownFileError, CompileError,
8n/a PackagingPlatformError)
9n/afrom packaging.command.build_ext import build_ext
10n/afrom packaging.compiler.extension import Extension
11n/a
12n/afrom test.script_helper import assert_python_ok
13n/afrom packaging.tests import support, unittest
14n/a
15n/a
16n/aclass BuildExtTestCase(support.TempdirManager,
17n/a support.LoggingCatcher,
18n/a unittest.TestCase):
19n/a def setUp(self):
20n/a super(BuildExtTestCase, self).setUp()
21n/a self.tmp_dir = self.mkdtemp()
22n/a self.old_user_base = site.USER_BASE
23n/a site.USER_BASE = self.mkdtemp()
24n/a
25n/a def tearDown(self):
26n/a site.USER_BASE = self.old_user_base
27n/a super(BuildExtTestCase, self).tearDown()
28n/a
29n/a def test_build_ext(self):
30n/a support.copy_xxmodule_c(self.tmp_dir)
31n/a xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
32n/a xx_ext = Extension('xx', [xx_c])
33n/a dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
34n/a dist.package_dir = self.tmp_dir
35n/a cmd = build_ext(dist)
36n/a support.fixup_build_ext(cmd)
37n/a cmd.build_lib = self.tmp_dir
38n/a cmd.build_temp = self.tmp_dir
39n/a cmd.ensure_finalized()
40n/a cmd.run()
41n/a
42n/a code = textwrap.dedent("""\
43n/a import sys
44n/a sys.path.insert(0, %r)
45n/a
46n/a import xx
47n/a
48n/a for attr in ('error', 'foo', 'new', 'roj'):
49n/a assert hasattr(xx, attr)
50n/a
51n/a assert xx.foo(2, 5) == 7
52n/a assert xx.foo(13, 15) == 28
53n/a assert xx.new().demo() is None
54n/a doc = 'This is a template module just for instruction.'
55n/a assert xx.__doc__ == doc
56n/a assert isinstance(xx.Null(), xx.Null)
57n/a assert isinstance(xx.Str(), xx.Str)
58n/a """)
59n/a code = code % self.tmp_dir
60n/a assert_python_ok('-c', code)
61n/a
62n/a def test_solaris_enable_shared(self):
63n/a dist = Distribution({'name': 'xx'})
64n/a cmd = build_ext(dist)
65n/a old = sys.platform
66n/a
67n/a sys.platform = 'sunos' # fooling finalize_options
68n/a
69n/a old_var = sysconfig.get_config_var('Py_ENABLE_SHARED')
70n/a sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] = 1
71n/a try:
72n/a cmd.ensure_finalized()
73n/a finally:
74n/a sys.platform = old
75n/a if old_var is None:
76n/a del sysconfig._CONFIG_VARS['Py_ENABLE_SHARED']
77n/a else:
78n/a sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] = old_var
79n/a
80n/a # make sure we get some library dirs under solaris
81n/a self.assertGreater(len(cmd.library_dirs), 0)
82n/a
83n/a def test_user_site(self):
84n/a dist = Distribution({'name': 'xx'})
85n/a cmd = build_ext(dist)
86n/a
87n/a # making sure the user option is there
88n/a options = [name for name, short, label in
89n/a cmd.user_options]
90n/a self.assertIn('user', options)
91n/a
92n/a # setting a value
93n/a cmd.user = True
94n/a
95n/a # setting user based lib and include
96n/a lib = os.path.join(site.USER_BASE, 'lib')
97n/a incl = os.path.join(site.USER_BASE, 'include')
98n/a os.mkdir(lib)
99n/a os.mkdir(incl)
100n/a
101n/a # let's run finalize
102n/a cmd.ensure_finalized()
103n/a
104n/a # see if include_dirs and library_dirs
105n/a # were set
106n/a self.assertIn(lib, cmd.library_dirs)
107n/a self.assertIn(lib, cmd.rpath)
108n/a self.assertIn(incl, cmd.include_dirs)
109n/a
110n/a def test_optional_extension(self):
111n/a
112n/a # this extension will fail, but let's ignore this failure
113n/a # with the optional argument.
114n/a modules = [Extension('foo', ['xxx'], optional=False)]
115n/a dist = Distribution({'name': 'xx', 'ext_modules': modules})
116n/a cmd = build_ext(dist)
117n/a cmd.ensure_finalized()
118n/a self.assertRaises((UnknownFileError, CompileError),
119n/a cmd.run) # should raise an error
120n/a
121n/a modules = [Extension('foo', ['xxx'], optional=True)]
122n/a dist = Distribution({'name': 'xx', 'ext_modules': modules})
123n/a cmd = build_ext(dist)
124n/a cmd.ensure_finalized()
125n/a cmd.run() # should pass
126n/a
127n/a def test_finalize_options(self):
128n/a # Make sure Python's include directories (for Python.h, pyconfig.h,
129n/a # etc.) are in the include search path.
130n/a modules = [Extension('foo', ['xxx'], optional=False)]
131n/a dist = Distribution({'name': 'xx', 'ext_modules': modules})
132n/a cmd = build_ext(dist)
133n/a cmd.finalize_options()
134n/a
135n/a py_include = sysconfig.get_path('include')
136n/a self.assertIn(py_include, cmd.include_dirs)
137n/a
138n/a plat_py_include = sysconfig.get_path('platinclude')
139n/a self.assertIn(plat_py_include, cmd.include_dirs)
140n/a
141n/a # make sure cmd.libraries is turned into a list
142n/a # if it's a string
143n/a cmd = build_ext(dist)
144n/a cmd.libraries = 'my_lib, other_lib lastlib'
145n/a cmd.finalize_options()
146n/a self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib'])
147n/a
148n/a # make sure cmd.library_dirs is turned into a list
149n/a # if it's a string
150n/a cmd = build_ext(dist)
151n/a cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep
152n/a cmd.finalize_options()
153n/a self.assertIn('my_lib_dir', cmd.library_dirs)
154n/a self.assertIn('other_lib_dir', cmd.library_dirs)
155n/a
156n/a # make sure rpath is turned into a list
157n/a # if it's a string
158n/a cmd = build_ext(dist)
159n/a cmd.rpath = 'one%stwo' % os.pathsep
160n/a cmd.finalize_options()
161n/a self.assertEqual(cmd.rpath, ['one', 'two'])
162n/a
163n/a # XXX more tests to perform for win32
164n/a
165n/a # make sure define is turned into 2-tuples
166n/a # strings if they are ','-separated strings
167n/a cmd = build_ext(dist)
168n/a cmd.define = 'one,two'
169n/a cmd.finalize_options()
170n/a self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
171n/a
172n/a # make sure undef is turned into a list of
173n/a # strings if they are ','-separated strings
174n/a cmd = build_ext(dist)
175n/a cmd.undef = 'one,two'
176n/a cmd.finalize_options()
177n/a self.assertEqual(cmd.undef, ['one', 'two'])
178n/a
179n/a # make sure swig_opts is turned into a list
180n/a cmd = build_ext(dist)
181n/a cmd.swig_opts = None
182n/a cmd.finalize_options()
183n/a self.assertEqual(cmd.swig_opts, [])
184n/a
185n/a cmd = build_ext(dist)
186n/a cmd.swig_opts = '1 2'
187n/a cmd.finalize_options()
188n/a self.assertEqual(cmd.swig_opts, ['1', '2'])
189n/a
190n/a def test_get_source_files(self):
191n/a modules = [Extension('foo', ['xxx'], optional=False)]
192n/a dist = Distribution({'name': 'xx', 'ext_modules': modules})
193n/a cmd = build_ext(dist)
194n/a cmd.ensure_finalized()
195n/a self.assertEqual(cmd.get_source_files(), ['xxx'])
196n/a
197n/a def test_compiler_option(self):
198n/a # cmd.compiler is an option and
199n/a # should not be overriden by a compiler instance
200n/a # when the command is run
201n/a dist = Distribution()
202n/a cmd = build_ext(dist)
203n/a cmd.compiler = 'unix'
204n/a cmd.ensure_finalized()
205n/a cmd.run()
206n/a self.assertEqual(cmd.compiler, 'unix')
207n/a
208n/a def test_get_outputs(self):
209n/a tmp_dir = self.mkdtemp()
210n/a c_file = os.path.join(tmp_dir, 'foo.c')
211n/a self.write_file(c_file, 'void PyInit_foo(void) {}\n')
212n/a ext = Extension('foo', [c_file], optional=False)
213n/a dist = Distribution({'name': 'xx',
214n/a 'ext_modules': [ext]})
215n/a cmd = build_ext(dist)
216n/a support.fixup_build_ext(cmd)
217n/a cmd.ensure_finalized()
218n/a self.assertEqual(len(cmd.get_outputs()), 1)
219n/a
220n/a cmd.build_lib = os.path.join(self.tmp_dir, 'build')
221n/a cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
222n/a
223n/a # issue #5977 : distutils build_ext.get_outputs
224n/a # returns wrong result with --inplace
225n/a other_tmp_dir = os.path.realpath(self.mkdtemp())
226n/a old_wd = os.getcwd()
227n/a os.chdir(other_tmp_dir)
228n/a try:
229n/a cmd.inplace = True
230n/a cmd.run()
231n/a so_file = cmd.get_outputs()[0]
232n/a finally:
233n/a os.chdir(old_wd)
234n/a self.assertTrue(os.path.exists(so_file))
235n/a so_ext = sysconfig.get_config_var('SO')
236n/a self.assertTrue(so_file.endswith(so_ext))
237n/a so_dir = os.path.dirname(so_file)
238n/a self.assertEqual(so_dir, other_tmp_dir)
239n/a
240n/a cmd.inplace = False
241n/a cmd.run()
242n/a so_file = cmd.get_outputs()[0]
243n/a self.assertTrue(os.path.exists(so_file))
244n/a self.assertTrue(so_file.endswith(so_ext))
245n/a so_dir = os.path.dirname(so_file)
246n/a self.assertEqual(so_dir, cmd.build_lib)
247n/a
248n/a # inplace = False, cmd.package = 'bar'
249n/a build_py = cmd.get_finalized_command('build_py')
250n/a build_py.package_dir = 'bar'
251n/a path = cmd.get_ext_fullpath('foo')
252n/a # checking that the last directory is the build_dir
253n/a path = os.path.split(path)[0]
254n/a self.assertEqual(path, cmd.build_lib)
255n/a
256n/a # inplace = True, cmd.package = 'bar'
257n/a cmd.inplace = True
258n/a other_tmp_dir = os.path.realpath(self.mkdtemp())
259n/a old_wd = os.getcwd()
260n/a os.chdir(other_tmp_dir)
261n/a try:
262n/a path = cmd.get_ext_fullpath('foo')
263n/a finally:
264n/a os.chdir(old_wd)
265n/a # checking that the last directory is bar
266n/a path = os.path.split(path)[0]
267n/a lastdir = os.path.split(path)[-1]
268n/a self.assertEqual(lastdir, 'bar')
269n/a
270n/a def test_ext_fullpath(self):
271n/a ext = sysconfig.get_config_vars()['SO']
272n/a # building lxml.etree inplace
273n/a #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
274n/a #etree_ext = Extension('lxml.etree', [etree_c])
275n/a #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
276n/a dist = Distribution()
277n/a cmd = build_ext(dist)
278n/a cmd.inplace = True
279n/a cmd.distribution.package_dir = 'src'
280n/a cmd.distribution.packages = ['lxml', 'lxml.html']
281n/a curdir = os.getcwd()
282n/a wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
283n/a path = cmd.get_ext_fullpath('lxml.etree')
284n/a self.assertEqual(wanted, path)
285n/a
286n/a # building lxml.etree not inplace
287n/a cmd.inplace = False
288n/a cmd.build_lib = os.path.join(curdir, 'tmpdir')
289n/a wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
290n/a path = cmd.get_ext_fullpath('lxml.etree')
291n/a self.assertEqual(wanted, path)
292n/a
293n/a # building twisted.runner.portmap not inplace
294n/a build_py = cmd.get_finalized_command('build_py')
295n/a build_py.package_dir = None
296n/a cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
297n/a path = cmd.get_ext_fullpath('twisted.runner.portmap')
298n/a wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
299n/a 'portmap' + ext)
300n/a self.assertEqual(wanted, path)
301n/a
302n/a # building twisted.runner.portmap inplace
303n/a cmd.inplace = True
304n/a path = cmd.get_ext_fullpath('twisted.runner.portmap')
305n/a wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
306n/a self.assertEqual(wanted, path)
307n/a
308n/a @unittest.skipUnless(sys.platform == 'darwin',
309n/a 'test only relevant for Mac OS X')
310n/a def test_deployment_target_default(self):
311n/a # Issue 9516: Test that, in the absence of the environment variable,
312n/a # an extension module is compiled with the same deployment target as
313n/a # the interpreter.
314n/a self._try_compile_deployment_target('==', None)
315n/a
316n/a @unittest.skipUnless(sys.platform == 'darwin',
317n/a 'test only relevant for Mac OS X')
318n/a def test_deployment_target_too_low(self):
319n/a # Issue 9516: Test that an extension module is not allowed to be
320n/a # compiled with a deployment target less than that of the interpreter.
321n/a self.assertRaises(PackagingPlatformError,
322n/a self._try_compile_deployment_target, '>', '10.1')
323n/a
324n/a @unittest.skipUnless(sys.platform == 'darwin',
325n/a 'test only relevant for Mac OS X')
326n/a def test_deployment_target_higher_ok(self):
327n/a # Issue 9516: Test that an extension module can be compiled with a
328n/a # deployment target higher than that of the interpreter: the ext
329n/a # module may depend on some newer OS feature.
330n/a deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
331n/a if deptarget:
332n/a # increment the minor version number (i.e. 10.6 -> 10.7)
333n/a deptarget = [int(x) for x in deptarget.split('.')]
334n/a deptarget[-1] += 1
335n/a deptarget = '.'.join(str(i) for i in deptarget)
336n/a self._try_compile_deployment_target('<', deptarget)
337n/a
338n/a def _try_compile_deployment_target(self, operator, target):
339n/a orig_environ = os.environ
340n/a os.environ = orig_environ.copy()
341n/a self.addCleanup(setattr, os, 'environ', orig_environ)
342n/a
343n/a if target is None:
344n/a if os.environ.get('MACOSX_DEPLOYMENT_TARGET'):
345n/a del os.environ['MACOSX_DEPLOYMENT_TARGET']
346n/a else:
347n/a os.environ['MACOSX_DEPLOYMENT_TARGET'] = target
348n/a
349n/a deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c')
350n/a
351n/a with open(deptarget_c, 'w') as fp:
352n/a fp.write(textwrap.dedent('''\
353n/a #include <AvailabilityMacros.h>
354n/a
355n/a int dummy;
356n/a
357n/a #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED
358n/a #else
359n/a #error "Unexpected target"
360n/a #endif
361n/a
362n/a ''' % operator))
363n/a
364n/a # get the deployment target that the interpreter was built with
365n/a target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
366n/a target = tuple(map(int, target.split('.')))
367n/a target = '%02d%01d0' % target
368n/a
369n/a deptarget_ext = Extension(
370n/a 'deptarget',
371n/a [deptarget_c],
372n/a extra_compile_args=['-DTARGET=%s' % (target,)],
373n/a )
374n/a dist = Distribution({
375n/a 'name': 'deptarget',
376n/a 'ext_modules': [deptarget_ext],
377n/a })
378n/a dist.package_dir = self.tmp_dir
379n/a cmd = build_ext(dist)
380n/a cmd.build_lib = self.tmp_dir
381n/a cmd.build_temp = self.tmp_dir
382n/a
383n/a try:
384n/a cmd.ensure_finalized()
385n/a cmd.run()
386n/a except CompileError:
387n/a self.fail("Wrong deployment target during compilation")
388n/a
389n/a
390n/adef test_suite():
391n/a return unittest.makeSuite(BuildExtTestCase)
392n/a
393n/aif __name__ == '__main__':
394n/a unittest.main(defaultTest='test_suite')