• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import sys
3import os
4import subprocess
5import shutil
6from copy import copy
7
8from test.support import (captured_stdout, PythonSymlink)
9from test.support.import_helper import import_module
10from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink,
11                                    change_cwd)
12from test.support.warnings_helper import check_warnings
13
14import sysconfig
15from sysconfig import (get_paths, get_platform, get_config_vars,
16                       get_path, get_path_names, _INSTALL_SCHEMES,
17                       get_default_scheme, get_scheme_names, get_config_var,
18                       _expand_vars, _get_preferred_schemes, _main)
19import _osx_support
20
21
22HAS_USER_BASE = sysconfig._HAS_USER_BASE
23
24
25class TestSysConfig(unittest.TestCase):
26
27    def setUp(self):
28        super(TestSysConfig, self).setUp()
29        self.sys_path = sys.path[:]
30        # patching os.uname
31        if hasattr(os, 'uname'):
32            self.uname = os.uname
33            self._uname = os.uname()
34        else:
35            self.uname = None
36            self._set_uname(('',)*5)
37        os.uname = self._get_uname
38        # saving the environment
39        self.name = os.name
40        self.platform = sys.platform
41        self.version = sys.version
42        self.sep = os.sep
43        self.join = os.path.join
44        self.isabs = os.path.isabs
45        self.splitdrive = os.path.splitdrive
46        self._config_vars = sysconfig._CONFIG_VARS, copy(sysconfig._CONFIG_VARS)
47        self._added_envvars = []
48        self._changed_envvars = []
49        for var in ('MACOSX_DEPLOYMENT_TARGET', 'PATH'):
50            if var in os.environ:
51                self._changed_envvars.append((var, os.environ[var]))
52            else:
53                self._added_envvars.append(var)
54
55    def tearDown(self):
56        sys.path[:] = self.sys_path
57        self._cleanup_testfn()
58        if self.uname is not None:
59            os.uname = self.uname
60        else:
61            del os.uname
62        os.name = self.name
63        sys.platform = self.platform
64        sys.version = self.version
65        os.sep = self.sep
66        os.path.join = self.join
67        os.path.isabs = self.isabs
68        os.path.splitdrive = self.splitdrive
69        sysconfig._CONFIG_VARS = self._config_vars[0]
70        sysconfig._CONFIG_VARS.clear()
71        sysconfig._CONFIG_VARS.update(self._config_vars[1])
72        for var, value in self._changed_envvars:
73            os.environ[var] = value
74        for var in self._added_envvars:
75            os.environ.pop(var, None)
76
77        super(TestSysConfig, self).tearDown()
78
79    def _set_uname(self, uname):
80        self._uname = os.uname_result(uname)
81
82    def _get_uname(self):
83        return self._uname
84
85    def _cleanup_testfn(self):
86        path = TESTFN
87        if os.path.isfile(path):
88            os.remove(path)
89        elif os.path.isdir(path):
90            shutil.rmtree(path)
91
92    def test_get_path_names(self):
93        self.assertEqual(get_path_names(), sysconfig._SCHEME_KEYS)
94
95    def test_get_paths(self):
96        scheme = get_paths()
97        default_scheme = get_default_scheme()
98        wanted = _expand_vars(default_scheme, None)
99        wanted = sorted(wanted.items())
100        scheme = sorted(scheme.items())
101        self.assertEqual(scheme, wanted)
102
103    def test_get_path(self):
104        config_vars = get_config_vars()
105        for scheme in _INSTALL_SCHEMES:
106            for name in _INSTALL_SCHEMES[scheme]:
107                expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
108                self.assertEqual(
109                    os.path.normpath(get_path(name, scheme)),
110                    os.path.normpath(expected),
111                )
112
113    def test_get_default_scheme(self):
114        self.assertIn(get_default_scheme(), _INSTALL_SCHEMES)
115
116    def test_get_preferred_schemes(self):
117        expected_schemes = {'prefix', 'home', 'user'}
118
119        # Windows.
120        os.name = 'nt'
121        schemes = _get_preferred_schemes()
122        self.assertIsInstance(schemes, dict)
123        self.assertEqual(set(schemes), expected_schemes)
124
125        # Mac and Linux, shared library build.
126        os.name = 'posix'
127        schemes = _get_preferred_schemes()
128        self.assertIsInstance(schemes, dict)
129        self.assertEqual(set(schemes), expected_schemes)
130
131        # Mac, framework build.
132        os.name = 'posix'
133        sys.platform = 'darwin'
134        sys._framework = True
135        self.assertIsInstance(schemes, dict)
136        self.assertEqual(set(schemes), expected_schemes)
137
138    def test_get_config_vars(self):
139        cvars = get_config_vars()
140        self.assertIsInstance(cvars, dict)
141        self.assertTrue(cvars)
142
143    def test_get_platform(self):
144        # windows XP, 32bits
145        os.name = 'nt'
146        sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
147                       '[MSC v.1310 32 bit (Intel)]')
148        sys.platform = 'win32'
149        self.assertEqual(get_platform(), 'win32')
150
151        # windows XP, amd64
152        os.name = 'nt'
153        sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
154                       '[MSC v.1310 32 bit (Amd64)]')
155        sys.platform = 'win32'
156        self.assertEqual(get_platform(), 'win-amd64')
157
158        # macbook
159        os.name = 'posix'
160        sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) '
161                       '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]')
162        sys.platform = 'darwin'
163        self._set_uname(('Darwin', 'macziade', '8.11.1',
164                   ('Darwin Kernel Version 8.11.1: '
165                    'Wed Oct 10 18:23:28 PDT 2007; '
166                    'root:xnu-792.25.20~1/RELEASE_I386'), 'PowerPC'))
167        _osx_support._remove_original_values(get_config_vars())
168        get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
169
170        get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
171                                       '-fwrapv -O3 -Wall -Wstrict-prototypes')
172
173        maxint = sys.maxsize
174        try:
175            sys.maxsize = 2147483647
176            self.assertEqual(get_platform(), 'macosx-10.3-ppc')
177            sys.maxsize = 9223372036854775807
178            self.assertEqual(get_platform(), 'macosx-10.3-ppc64')
179        finally:
180            sys.maxsize = maxint
181
182        self._set_uname(('Darwin', 'macziade', '8.11.1',
183                   ('Darwin Kernel Version 8.11.1: '
184                    'Wed Oct 10 18:23:28 PDT 2007; '
185                    'root:xnu-792.25.20~1/RELEASE_I386'), 'i386'))
186        _osx_support._remove_original_values(get_config_vars())
187        get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
188
189        get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
190                                       '-fwrapv -O3 -Wall -Wstrict-prototypes')
191        maxint = sys.maxsize
192        try:
193            sys.maxsize = 2147483647
194            self.assertEqual(get_platform(), 'macosx-10.3-i386')
195            sys.maxsize = 9223372036854775807
196            self.assertEqual(get_platform(), 'macosx-10.3-x86_64')
197        finally:
198            sys.maxsize = maxint
199
200        # macbook with fat binaries (fat, universal or fat64)
201        _osx_support._remove_original_values(get_config_vars())
202        get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
203        get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot '
204                                       '/Developer/SDKs/MacOSX10.4u.sdk  '
205                                       '-fno-strict-aliasing -fno-common '
206                                       '-dynamic -DNDEBUG -g -O3')
207
208        self.assertEqual(get_platform(), 'macosx-10.4-fat')
209
210        _osx_support._remove_original_values(get_config_vars())
211        get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot '
212                                       '/Developer/SDKs/MacOSX10.4u.sdk  '
213                                       '-fno-strict-aliasing -fno-common '
214                                       '-dynamic -DNDEBUG -g -O3')
215
216        self.assertEqual(get_platform(), 'macosx-10.4-intel')
217
218        _osx_support._remove_original_values(get_config_vars())
219        get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot '
220                                       '/Developer/SDKs/MacOSX10.4u.sdk  '
221                                       '-fno-strict-aliasing -fno-common '
222                                       '-dynamic -DNDEBUG -g -O3')
223        self.assertEqual(get_platform(), 'macosx-10.4-fat3')
224
225        _osx_support._remove_original_values(get_config_vars())
226        get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot '
227                                       '/Developer/SDKs/MacOSX10.4u.sdk  '
228                                       '-fno-strict-aliasing -fno-common '
229                                       '-dynamic -DNDEBUG -g -O3')
230        self.assertEqual(get_platform(), 'macosx-10.4-universal')
231
232        _osx_support._remove_original_values(get_config_vars())
233        get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot '
234                                       '/Developer/SDKs/MacOSX10.4u.sdk  '
235                                       '-fno-strict-aliasing -fno-common '
236                                       '-dynamic -DNDEBUG -g -O3')
237
238        self.assertEqual(get_platform(), 'macosx-10.4-fat64')
239
240        for arch in ('ppc', 'i386', 'x86_64', 'ppc64'):
241            _osx_support._remove_original_values(get_config_vars())
242            get_config_vars()['CFLAGS'] = ('-arch %s -isysroot '
243                                           '/Developer/SDKs/MacOSX10.4u.sdk  '
244                                           '-fno-strict-aliasing -fno-common '
245                                           '-dynamic -DNDEBUG -g -O3' % arch)
246
247            self.assertEqual(get_platform(), 'macosx-10.4-%s' % arch)
248
249        # linux debian sarge
250        os.name = 'posix'
251        sys.version = ('2.3.5 (#1, Jul  4 2007, 17:28:59) '
252                       '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]')
253        sys.platform = 'linux2'
254        self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7',
255                    '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686'))
256
257        self.assertEqual(get_platform(), 'linux-i686')
258
259        # XXX more platforms to tests here
260
261    def test_get_config_h_filename(self):
262        config_h = sysconfig.get_config_h_filename()
263        self.assertTrue(os.path.isfile(config_h), config_h)
264
265    def test_get_scheme_names(self):
266        wanted = ['nt', 'posix_home', 'posix_prefix']
267        if HAS_USER_BASE:
268            wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
269        self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
270
271    @skip_unless_symlink
272    def test_symlink(self): # Issue 7880
273        with PythonSymlink() as py:
274            cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
275            self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
276
277    def test_user_similar(self):
278        # Issue #8759: make sure the posix scheme for the users
279        # is similar to the global posix_prefix one
280        base = get_config_var('base')
281        if HAS_USER_BASE:
282            user = get_config_var('userbase')
283        # the global scheme mirrors the distinction between prefix and
284        # exec-prefix but not the user scheme, so we have to adapt the paths
285        # before comparing (issue #9100)
286        adapt = sys.base_prefix != sys.base_exec_prefix
287        for name in ('stdlib', 'platstdlib', 'purelib', 'platlib'):
288            global_path = get_path(name, 'posix_prefix')
289            if adapt:
290                global_path = global_path.replace(sys.exec_prefix, sys.base_prefix)
291                base = base.replace(sys.exec_prefix, sys.base_prefix)
292            elif sys.base_prefix != sys.prefix:
293                # virtual environment? Likewise, we have to adapt the paths
294                # before comparing
295                global_path = global_path.replace(sys.base_prefix, sys.prefix)
296                base = base.replace(sys.base_prefix, sys.prefix)
297            if HAS_USER_BASE:
298                user_path = get_path(name, 'posix_user')
299                expected = global_path.replace(base, user, 1)
300                # bpo-44860: platlib of posix_user doesn't use sys.platlibdir,
301                # whereas posix_prefix does.
302                if name == 'platlib':
303                    # Replace "/lib64/python3.11/site-packages" suffix
304                    # with "/lib/python3.11/site-packages".
305                    py_version_short = sysconfig.get_python_version()
306                    suffix = f'python{py_version_short}/site-packages'
307                    expected = expected.replace(f'/{sys.platlibdir}/{suffix}',
308                                                f'/lib/{suffix}')
309                self.assertEqual(user_path, expected)
310
311    def test_main(self):
312        # just making sure _main() runs and returns things in the stdout
313        with captured_stdout() as output:
314            _main()
315        self.assertTrue(len(output.getvalue().split('\n')) > 0)
316
317    @unittest.skipIf(sys.platform == "win32", "Does not apply to Windows")
318    def test_ldshared_value(self):
319        ldflags = sysconfig.get_config_var('LDFLAGS')
320        ldshared = sysconfig.get_config_var('LDSHARED')
321
322        self.assertIn(ldflags, ldshared)
323
324    @unittest.skipUnless(sys.platform == "darwin", "test only relevant on MacOSX")
325    def test_platform_in_subprocess(self):
326        my_platform = sysconfig.get_platform()
327
328        # Test without MACOSX_DEPLOYMENT_TARGET in the environment
329
330        env = os.environ.copy()
331        if 'MACOSX_DEPLOYMENT_TARGET' in env:
332            del env['MACOSX_DEPLOYMENT_TARGET']
333
334        p = subprocess.Popen([
335                sys.executable, '-c',
336                'import sysconfig; print(sysconfig.get_platform())',
337            ],
338            stdout=subprocess.PIPE,
339            stderr=subprocess.DEVNULL,
340            env=env)
341        test_platform = p.communicate()[0].strip()
342        test_platform = test_platform.decode('utf-8')
343        status = p.wait()
344
345        self.assertEqual(status, 0)
346        self.assertEqual(my_platform, test_platform)
347
348        # Test with MACOSX_DEPLOYMENT_TARGET in the environment, and
349        # using a value that is unlikely to be the default one.
350        env = os.environ.copy()
351        env['MACOSX_DEPLOYMENT_TARGET'] = '10.1'
352
353        p = subprocess.Popen([
354                sys.executable, '-c',
355                'import sysconfig; print(sysconfig.get_platform())',
356            ],
357            stdout=subprocess.PIPE,
358            stderr=subprocess.DEVNULL,
359            env=env)
360        test_platform = p.communicate()[0].strip()
361        test_platform = test_platform.decode('utf-8')
362        status = p.wait()
363
364        self.assertEqual(status, 0)
365        self.assertEqual(my_platform, test_platform)
366
367    def test_srcdir(self):
368        # See Issues #15322, #15364.
369        srcdir = sysconfig.get_config_var('srcdir')
370
371        self.assertTrue(os.path.isabs(srcdir), srcdir)
372        self.assertTrue(os.path.isdir(srcdir), srcdir)
373
374        if sysconfig._PYTHON_BUILD:
375            # The python executable has not been installed so srcdir
376            # should be a full source checkout.
377            Python_h = os.path.join(srcdir, 'Include', 'Python.h')
378            self.assertTrue(os.path.exists(Python_h), Python_h)
379            self.assertTrue(sysconfig._is_python_source_dir(srcdir))
380        elif os.name == 'posix':
381            makefile_dir = os.path.dirname(sysconfig.get_makefile_filename())
382            # Issue #19340: srcdir has been realpath'ed already
383            makefile_dir = os.path.realpath(makefile_dir)
384            self.assertEqual(makefile_dir, srcdir)
385
386    def test_srcdir_independent_of_cwd(self):
387        # srcdir should be independent of the current working directory
388        # See Issues #15322, #15364.
389        srcdir = sysconfig.get_config_var('srcdir')
390        with change_cwd(os.pardir):
391            srcdir2 = sysconfig.get_config_var('srcdir')
392        self.assertEqual(srcdir, srcdir2)
393
394    @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
395                     'EXT_SUFFIX required for this test')
396    def test_SO_deprecation(self):
397        self.assertWarns(DeprecationWarning,
398                         sysconfig.get_config_var, 'SO')
399
400    @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
401                     'EXT_SUFFIX required for this test')
402    def test_SO_value(self):
403        with check_warnings(('', DeprecationWarning)):
404            self.assertEqual(sysconfig.get_config_var('SO'),
405                             sysconfig.get_config_var('EXT_SUFFIX'))
406
407    @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
408                     'EXT_SUFFIX required for this test')
409    def test_EXT_SUFFIX_in_vars(self):
410        import _imp
411        vars = sysconfig.get_config_vars()
412        self.assertIsNotNone(vars['SO'])
413        self.assertEqual(vars['SO'], vars['EXT_SUFFIX'])
414        self.assertEqual(vars['EXT_SUFFIX'], _imp.extension_suffixes()[0])
415
416    @unittest.skipUnless(sys.platform == 'linux' and
417                         hasattr(sys.implementation, '_multiarch'),
418                         'multiarch-specific test')
419    def test_triplet_in_ext_suffix(self):
420        ctypes = import_module('ctypes')
421        import platform, re
422        machine = platform.machine()
423        suffix = sysconfig.get_config_var('EXT_SUFFIX')
424        if re.match('(aarch64|arm|mips|ppc|powerpc|s390|sparc)', machine):
425            self.assertTrue('linux' in suffix, suffix)
426        if re.match('(i[3-6]86|x86_64)$', machine):
427            if ctypes.sizeof(ctypes.c_char_p()) == 4:
428                self.assertTrue(suffix.endswith('i386-linux-gnu.so') or
429                                suffix.endswith('x86_64-linux-gnux32.so'),
430                                suffix)
431            else: # 8 byte pointer size
432                self.assertTrue(suffix.endswith('x86_64-linux-gnu.so'), suffix)
433
434    @unittest.skipUnless(sys.platform == 'darwin', 'OS X-specific test')
435    def test_osx_ext_suffix(self):
436        suffix = sysconfig.get_config_var('EXT_SUFFIX')
437        self.assertTrue(suffix.endswith('-darwin.so'), suffix)
438
439class MakefileTests(unittest.TestCase):
440
441    @unittest.skipIf(sys.platform.startswith('win'),
442                     'Test is not Windows compatible')
443    def test_get_makefile_filename(self):
444        makefile = sysconfig.get_makefile_filename()
445        self.assertTrue(os.path.isfile(makefile), makefile)
446
447    def test_parse_makefile(self):
448        self.addCleanup(unlink, TESTFN)
449        with open(TESTFN, "w") as makefile:
450            print("var1=a$(VAR2)", file=makefile)
451            print("VAR2=b$(var3)", file=makefile)
452            print("var3=42", file=makefile)
453            print("var4=$/invalid", file=makefile)
454            print("var5=dollar$$5", file=makefile)
455            print("var6=${var3}/lib/python3.5/config-$(VAR2)$(var5)"
456                  "-x86_64-linux-gnu", file=makefile)
457        vars = sysconfig._parse_makefile(TESTFN)
458        self.assertEqual(vars, {
459            'var1': 'ab42',
460            'VAR2': 'b42',
461            'var3': 42,
462            'var4': '$/invalid',
463            'var5': 'dollar$5',
464            'var6': '42/lib/python3.5/config-b42dollar$5-x86_64-linux-gnu',
465        })
466
467
468if __name__ == "__main__":
469    unittest.main()
470