• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os
2import platform
3import subprocess
4import sys
5import unittest
6from unittest import mock
7
8from test import support
9
10
11class PlatformTest(unittest.TestCase):
12    def clear_caches(self):
13        platform._platform_cache.clear()
14        platform._sys_version_cache.clear()
15        platform._uname_cache = None
16
17    def test_architecture(self):
18        res = platform.architecture()
19
20    @support.skip_unless_symlink
21    def test_architecture_via_symlink(self): # issue3762
22        with support.PythonSymlink() as py:
23            cmd = "-c", "import platform; print(platform.architecture())"
24            self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
25
26    def test_platform(self):
27        for aliased in (False, True):
28            for terse in (False, True):
29                res = platform.platform(aliased, terse)
30
31    def test_system(self):
32        res = platform.system()
33
34    def test_node(self):
35        res = platform.node()
36
37    def test_release(self):
38        res = platform.release()
39
40    def test_version(self):
41        res = platform.version()
42
43    def test_machine(self):
44        res = platform.machine()
45
46    def test_processor(self):
47        res = platform.processor()
48
49    def setUp(self):
50        self.save_version = sys.version
51        self.save_git = sys._git
52        self.save_platform = sys.platform
53
54    def tearDown(self):
55        sys.version = self.save_version
56        sys._git = self.save_git
57        sys.platform = self.save_platform
58
59    def test_sys_version(self):
60        # Old test.
61        for input, output in (
62            ('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]',
63             ('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')),
64            ('IronPython 1.0.60816 on .NET 2.0.50727.42',
65             ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')),
66            ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42',
67             ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')),
68            ('2.4.3 (truncation, date, t) \n[GCC]',
69             ('CPython', '2.4.3', '', '', 'truncation', 'date t', 'GCC')),
70            ('2.4.3 (truncation, date, ) \n[GCC]',
71             ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
72            ('2.4.3 (truncation, date,) \n[GCC]',
73             ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
74            ('2.4.3 (truncation, date) \n[GCC]',
75             ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
76            ('2.4.3 (truncation, d) \n[GCC]',
77             ('CPython', '2.4.3', '', '', 'truncation', 'd', 'GCC')),
78            ('2.4.3 (truncation, ) \n[GCC]',
79             ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
80            ('2.4.3 (truncation,) \n[GCC]',
81             ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
82            ('2.4.3 (truncation) \n[GCC]',
83             ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
84            ):
85            # branch and revision are not "parsed", but fetched
86            # from sys._git.  Ignore them
87            (name, version, branch, revision, buildno, builddate, compiler) \
88                   = platform._sys_version(input)
89            self.assertEqual(
90                (name, version, '', '', buildno, builddate, compiler), output)
91
92        # Tests for python_implementation(), python_version(), python_branch(),
93        # python_revision(), python_build(), and python_compiler().
94        sys_versions = {
95            ("2.6.1 (r261:67515, Dec  6 2008, 15:26:00) \n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]",
96             ('CPython', 'tags/r261', '67515'), self.save_platform)
97            :
98                ("CPython", "2.6.1", "tags/r261", "67515",
99                 ('r261:67515', 'Dec  6 2008 15:26:00'),
100                 'GCC 4.0.1 (Apple Computer, Inc. build 5370)'),
101
102            ("IronPython 2.0 (2.0.0.0) on .NET 2.0.50727.3053", None, "cli")
103            :
104                ("IronPython", "2.0.0", "", "", ("", ""),
105                 ".NET 2.0.50727.3053"),
106
107            ("2.6.1 (IronPython 2.6.1 (2.6.10920.0) on .NET 2.0.50727.1433)", None, "cli")
108            :
109                ("IronPython", "2.6.1", "", "", ("", ""),
110                 ".NET 2.0.50727.1433"),
111
112            ("2.7.4 (IronPython 2.7.4 (2.7.0.40) on Mono 4.0.30319.1 (32-bit))", None, "cli")
113            :
114                ("IronPython", "2.7.4", "", "", ("", ""),
115                 "Mono 4.0.30319.1 (32-bit)"),
116
117            ("2.5 (trunk:6107, Mar 26 2009, 13:02:18) \n[Java HotSpot(TM) Client VM (\"Apple Computer, Inc.\")]",
118            ('Jython', 'trunk', '6107'), "java1.5.0_16")
119            :
120                ("Jython", "2.5.0", "trunk", "6107",
121                 ('trunk:6107', 'Mar 26 2009'), "java1.5.0_16"),
122
123            ("2.5.2 (63378, Mar 26 2009, 18:03:29)\n[PyPy 1.0.0]",
124             ('PyPy', 'trunk', '63378'), self.save_platform)
125            :
126                ("PyPy", "2.5.2", "trunk", "63378", ('63378', 'Mar 26 2009'),
127                 "")
128            }
129        for (version_tag, scm, sys_platform), info in \
130                sys_versions.items():
131            sys.version = version_tag
132            if scm is None:
133                if hasattr(sys, "_git"):
134                    del sys._git
135            else:
136                sys._git = scm
137            if sys_platform is not None:
138                sys.platform = sys_platform
139            self.assertEqual(platform.python_implementation(), info[0])
140            self.assertEqual(platform.python_version(), info[1])
141            self.assertEqual(platform.python_branch(), info[2])
142            self.assertEqual(platform.python_revision(), info[3])
143            self.assertEqual(platform.python_build(), info[4])
144            self.assertEqual(platform.python_compiler(), info[5])
145
146    def test_system_alias(self):
147        res = platform.system_alias(
148            platform.system(),
149            platform.release(),
150            platform.version(),
151        )
152
153    def test_uname(self):
154        res = platform.uname()
155        self.assertTrue(any(res))
156        self.assertEqual(res[0], res.system)
157        self.assertEqual(res[1], res.node)
158        self.assertEqual(res[2], res.release)
159        self.assertEqual(res[3], res.version)
160        self.assertEqual(res[4], res.machine)
161        self.assertEqual(res[5], res.processor)
162
163    @unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
164    def test_uname_win32_ARCHITEW6432(self):
165        # Issue 7860: make sure we get architecture from the correct variable
166        # on 64 bit Windows: if PROCESSOR_ARCHITEW6432 exists we should be
167        # using it, per
168        # http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx
169        try:
170            with support.EnvironmentVarGuard() as environ:
171                if 'PROCESSOR_ARCHITEW6432' in environ:
172                    del environ['PROCESSOR_ARCHITEW6432']
173                environ['PROCESSOR_ARCHITECTURE'] = 'foo'
174                platform._uname_cache = None
175                system, node, release, version, machine, processor = platform.uname()
176                self.assertEqual(machine, 'foo')
177                environ['PROCESSOR_ARCHITEW6432'] = 'bar'
178                platform._uname_cache = None
179                system, node, release, version, machine, processor = platform.uname()
180                self.assertEqual(machine, 'bar')
181        finally:
182            platform._uname_cache = None
183
184    def test_java_ver(self):
185        res = platform.java_ver()
186        if sys.platform == 'java':
187            self.assertTrue(all(res))
188
189    def test_win32_ver(self):
190        res = platform.win32_ver()
191
192    def test_mac_ver(self):
193        res = platform.mac_ver()
194
195        if platform.uname().system == 'Darwin':
196            # We are on a macOS system, check that the right version
197            # information is returned
198            output = subprocess.check_output(['sw_vers'], text=True)
199            for line in output.splitlines():
200                if line.startswith('ProductVersion:'):
201                    real_ver = line.strip().split()[-1]
202                    break
203            else:
204                self.fail(f"failed to parse sw_vers output: {output!r}")
205
206            result_list = res[0].split('.')
207            expect_list = real_ver.split('.')
208            len_diff = len(result_list) - len(expect_list)
209            # On Snow Leopard, sw_vers reports 10.6.0 as 10.6
210            if len_diff > 0:
211                expect_list.extend(['0'] * len_diff)
212            self.assertEqual(result_list, expect_list)
213
214            # res[1] claims to contain
215            # (version, dev_stage, non_release_version)
216            # That information is no longer available
217            self.assertEqual(res[1], ('', '', ''))
218
219            if sys.byteorder == 'little':
220                self.assertIn(res[2], ('i386', 'x86_64'))
221            else:
222                self.assertEqual(res[2], 'PowerPC')
223
224
225    @unittest.skipUnless(sys.platform == 'darwin', "OSX only test")
226    def test_mac_ver_with_fork(self):
227        # Issue7895: platform.mac_ver() crashes when using fork without exec
228        #
229        # This test checks that the fix for that issue works.
230        #
231        pid = os.fork()
232        if pid == 0:
233            # child
234            info = platform.mac_ver()
235            os._exit(0)
236
237        else:
238            # parent
239            cpid, sts = os.waitpid(pid, 0)
240            self.assertEqual(cpid, pid)
241            self.assertEqual(sts, 0)
242
243    def test_libc_ver(self):
244        # check that libc_ver(executable) doesn't raise an exception
245        if os.path.isdir(sys.executable) and \
246           os.path.exists(sys.executable+'.exe'):
247            # Cygwin horror
248            executable = sys.executable + '.exe'
249        elif sys.platform == "win32" and not os.path.exists(sys.executable):
250            # App symlink appears to not exist, but we want the
251            # real executable here anyway
252            import _winapi
253            executable = _winapi.GetModuleFileName(0)
254        else:
255            executable = sys.executable
256        platform.libc_ver(executable)
257
258        filename = support.TESTFN
259        self.addCleanup(support.unlink, filename)
260
261        with mock.patch('os.confstr', create=True, return_value='mock 1.0'):
262            # test os.confstr() code path
263            self.assertEqual(platform.libc_ver(), ('mock', '1.0'))
264
265            # test the different regular expressions
266            for data, expected in (
267                (b'__libc_init', ('libc', '')),
268                (b'GLIBC_2.9', ('glibc', '2.9')),
269                (b'libc.so.1.2.5', ('libc', '1.2.5')),
270                (b'libc_pthread.so.1.2.5', ('libc', '1.2.5_pthread')),
271                (b'', ('', '')),
272            ):
273                with open(filename, 'wb') as fp:
274                    fp.write(b'[xxx%sxxx]' % data)
275                    fp.flush()
276
277                # os.confstr() must not be used if executable is set
278                self.assertEqual(platform.libc_ver(executable=filename),
279                                 expected)
280
281        # binary containing multiple versions: get the most recent,
282        # make sure that 1.9 is seen as older than 1.23.4
283        chunksize = 16384
284        with open(filename, 'wb') as f:
285            # test match at chunk boundary
286            f.write(b'x'*(chunksize - 10))
287            f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0')
288        self.assertEqual(platform.libc_ver(filename, chunksize=chunksize),
289                         ('glibc', '1.23.4'))
290
291    @support.cpython_only
292    def test__comparable_version(self):
293        from platform import _comparable_version as V
294        self.assertEqual(V('1.2.3'), V('1.2.3'))
295        self.assertLess(V('1.2.3'), V('1.2.10'))
296        self.assertEqual(V('1.2.3.4'), V('1_2-3+4'))
297        self.assertLess(V('1.2spam'), V('1.2dev'))
298        self.assertLess(V('1.2dev'), V('1.2alpha'))
299        self.assertLess(V('1.2dev'), V('1.2a'))
300        self.assertLess(V('1.2alpha'), V('1.2beta'))
301        self.assertLess(V('1.2a'), V('1.2b'))
302        self.assertLess(V('1.2beta'), V('1.2c'))
303        self.assertLess(V('1.2b'), V('1.2c'))
304        self.assertLess(V('1.2c'), V('1.2RC'))
305        self.assertLess(V('1.2c'), V('1.2rc'))
306        self.assertLess(V('1.2RC'), V('1.2.0'))
307        self.assertLess(V('1.2rc'), V('1.2.0'))
308        self.assertLess(V('1.2.0'), V('1.2pl'))
309        self.assertLess(V('1.2.0'), V('1.2p'))
310
311        self.assertLess(V('1.5.1'), V('1.5.2b2'))
312        self.assertLess(V('3.10a'), V('161'))
313        self.assertEqual(V('8.02'), V('8.02'))
314        self.assertLess(V('3.4j'), V('1996.07.12'))
315        self.assertLess(V('3.1.1.6'), V('3.2.pl0'))
316        self.assertLess(V('2g6'), V('11g'))
317        self.assertLess(V('0.9'), V('2.2'))
318        self.assertLess(V('1.2'), V('1.2.1'))
319        self.assertLess(V('1.1'), V('1.2.2'))
320        self.assertLess(V('1.1'), V('1.2'))
321        self.assertLess(V('1.2.1'), V('1.2.2'))
322        self.assertLess(V('1.2'), V('1.2.2'))
323        self.assertLess(V('0.4'), V('0.4.0'))
324        self.assertLess(V('1.13++'), V('5.5.kw'))
325        self.assertLess(V('0.960923'), V('2.2beta29'))
326
327
328    def test_macos(self):
329        self.addCleanup(self.clear_caches)
330
331        uname = ('Darwin', 'hostname', '17.7.0',
332                 ('Darwin Kernel Version 17.7.0: '
333                  'Thu Jun 21 22:53:14 PDT 2018; '
334                  'root:xnu-4570.71.2~1/RELEASE_X86_64'),
335                 'x86_64', 'i386')
336        arch = ('64bit', '')
337        with mock.patch.object(platform, 'uname', return_value=uname), \
338             mock.patch.object(platform, 'architecture', return_value=arch):
339            for mac_ver, expected_terse, expected in [
340                # darwin: mac_ver() returns empty strings
341                (('', '', ''),
342                 'Darwin-17.7.0',
343                 'Darwin-17.7.0-x86_64-i386-64bit'),
344                # macOS: mac_ver() returns macOS version
345                (('10.13.6', ('', '', ''), 'x86_64'),
346                 'macOS-10.13.6',
347                 'macOS-10.13.6-x86_64-i386-64bit'),
348            ]:
349                with mock.patch.object(platform, 'mac_ver',
350                                       return_value=mac_ver):
351                    self.clear_caches()
352                    self.assertEqual(platform.platform(terse=1), expected_terse)
353                    self.assertEqual(platform.platform(), expected)
354
355
356if __name__ == '__main__':
357    unittest.main()
358