blob: 9cf17726d92e0dc722fb115ebb9e9fcce6ff3a1d [file] [log] [blame]
Benjamin Peterson856ff5f2008-05-29 21:22:40 +00001import os
Walter Dörwaldc69d1c42005-11-21 17:48:12 +00002import platform
Hirokazu Yamamotoe6748402008-10-06 04:51:11 +00003import subprocess
Victor Stinner3a38a6d2011-06-10 13:59:59 +02004import sys
Steve Dowerf14c28f2018-09-20 13:38:34 -07005import sysconfig
Victor Stinner476b1132018-12-05 14:04:52 +01006import tempfile
Victor Stinner3a38a6d2011-06-10 13:59:59 +02007import unittest
Victor Stinner476b1132018-12-05 14:04:52 +01008from unittest import mock
Walter Dörwaldc69d1c42005-11-21 17:48:12 +00009
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Alexandre Vassalottie9f305f2008-05-16 04:39:54 +000011
Walter Dörwaldc69d1c42005-11-21 17:48:12 +000012class PlatformTest(unittest.TestCase):
Victor Stinnerea0ca212018-12-05 22:41:52 +010013 def clear_caches(self):
14 platform._platform_cache.clear()
15 platform._sys_version_cache.clear()
16 platform._uname_cache = None
17
Walter Dörwaldc69d1c42005-11-21 17:48:12 +000018 def test_architecture(self):
19 res = platform.architecture()
20
Brian Curtin3b4499c2010-12-28 14:31:47 +000021 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +000022 def test_architecture_via_symlink(self): # issue3762
Steve Dower109bc3a2016-09-10 12:19:42 -070023 # On Windows, the EXE needs to know where pythonXY.dll and *.pyd is at
Steve Dowerf14c28f2018-09-20 13:38:34 -070024 # so we add the directory to the path, PYTHONHOME and PYTHONPATH.
25 env = None
Brian Curtind40e6f72010-07-08 21:39:08 +000026 if sys.platform == "win32":
Steve Dowerf14c28f2018-09-20 13:38:34 -070027 env = {k.upper(): os.environ[k] for k in os.environ}
28 env["PATH"] = "{};{}".format(
29 os.path.dirname(sys.executable), env.get("PATH", ""))
30 env["PYTHONHOME"] = os.path.dirname(sys.executable)
31 if sysconfig.is_python_build(True):
32 env["PYTHONPATH"] = os.path.dirname(os.__file__)
Victor Stinner0fc55a22016-09-10 06:24:47 -040033
Steve Dowerf14c28f2018-09-20 13:38:34 -070034 def get(python, env=None):
Brian Curtind40e6f72010-07-08 21:39:08 +000035 cmd = [python, '-c',
36 'import platform; print(platform.architecture())']
Steve Dowerf14c28f2018-09-20 13:38:34 -070037 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
38 stderr=subprocess.PIPE, env=env)
39 r = p.communicate()
40 if p.returncode:
41 print(repr(r[0]))
42 print(repr(r[1]), file=sys.stderr)
43 self.fail('unexpected return code: {0} (0x{0:08X})'
44 .format(p.returncode))
45 return r
Victor Stinner0fc55a22016-09-10 06:24:47 -040046
Brian Curtind40e6f72010-07-08 21:39:08 +000047 real = os.path.realpath(sys.executable)
48 link = os.path.abspath(support.TESTFN)
49 os.symlink(real, link)
50 try:
Steve Dowerf14c28f2018-09-20 13:38:34 -070051 self.assertEqual(get(real), get(link, env=env))
Brian Curtind40e6f72010-07-08 21:39:08 +000052 finally:
53 os.remove(link)
Hirokazu Yamamotoe6748402008-10-06 04:51:11 +000054
Walter Dörwaldc69d1c42005-11-21 17:48:12 +000055 def test_platform(self):
56 for aliased in (False, True):
57 for terse in (False, True):
58 res = platform.platform(aliased, terse)
59
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +000060 def test_system(self):
61 res = platform.system()
62
63 def test_node(self):
64 res = platform.node()
65
66 def test_release(self):
67 res = platform.release()
68
69 def test_version(self):
70 res = platform.version()
71
72 def test_machine(self):
73 res = platform.machine()
74
Walter Dörwaldc69d1c42005-11-21 17:48:12 +000075 def test_processor(self):
76 res = platform.processor()
77
Benjamin Petersone549ead2009-03-28 21:42:05 +000078 def setUp(self):
79 self.save_version = sys.version
Ned Deily5c4b0d02017-03-04 00:19:55 -050080 self.save_git = sys._git
Benjamin Petersone549ead2009-03-28 21:42:05 +000081 self.save_platform = sys.platform
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +000082
Benjamin Petersone549ead2009-03-28 21:42:05 +000083 def tearDown(self):
84 sys.version = self.save_version
Ned Deily5c4b0d02017-03-04 00:19:55 -050085 sys._git = self.save_git
Benjamin Petersone549ead2009-03-28 21:42:05 +000086 sys.platform = self.save_platform
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +000087
Benjamin Petersone549ead2009-03-28 21:42:05 +000088 def test_sys_version(self):
89 # Old test.
90 for input, output in (
91 ('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]',
92 ('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')),
93 ('IronPython 1.0.60816 on .NET 2.0.50727.42',
94 ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')),
95 ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42',
96 ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')),
Martin Panter4e505532016-06-08 06:12:22 +000097 ('2.4.3 (truncation, date, t) \n[GCC]',
98 ('CPython', '2.4.3', '', '', 'truncation', 'date t', 'GCC')),
99 ('2.4.3 (truncation, date, ) \n[GCC]',
100 ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
101 ('2.4.3 (truncation, date,) \n[GCC]',
102 ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
103 ('2.4.3 (truncation, date) \n[GCC]',
104 ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
105 ('2.4.3 (truncation, d) \n[GCC]',
106 ('CPython', '2.4.3', '', '', 'truncation', 'd', 'GCC')),
107 ('2.4.3 (truncation, ) \n[GCC]',
108 ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
109 ('2.4.3 (truncation,) \n[GCC]',
110 ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
111 ('2.4.3 (truncation) \n[GCC]',
112 ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
Benjamin Petersone549ead2009-03-28 21:42:05 +0000113 ):
114 # branch and revision are not "parsed", but fetched
Ned Deily5c4b0d02017-03-04 00:19:55 -0500115 # from sys._git. Ignore them
Benjamin Petersone549ead2009-03-28 21:42:05 +0000116 (name, version, branch, revision, buildno, builddate, compiler) \
117 = platform._sys_version(input)
118 self.assertEqual(
119 (name, version, '', '', buildno, builddate, compiler), output)
Benjamin Peterson5f28b7b2009-03-26 21:49:58 +0000120
Benjamin Petersone549ead2009-03-28 21:42:05 +0000121 # Tests for python_implementation(), python_version(), python_branch(),
122 # python_revision(), python_build(), and python_compiler().
123 sys_versions = {
124 ("2.6.1 (r261:67515, Dec 6 2008, 15:26:00) \n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]",
125 ('CPython', 'tags/r261', '67515'), self.save_platform)
126 :
127 ("CPython", "2.6.1", "tags/r261", "67515",
128 ('r261:67515', 'Dec 6 2008 15:26:00'),
129 'GCC 4.0.1 (Apple Computer, Inc. build 5370)'),
Ezio Melottif076f532013-10-21 03:03:32 +0300130
Benjamin Petersone549ead2009-03-28 21:42:05 +0000131 ("IronPython 2.0 (2.0.0.0) on .NET 2.0.50727.3053", None, "cli")
132 :
133 ("IronPython", "2.0.0", "", "", ("", ""),
134 ".NET 2.0.50727.3053"),
Ezio Melottif076f532013-10-21 03:03:32 +0300135
136 ("2.6.1 (IronPython 2.6.1 (2.6.10920.0) on .NET 2.0.50727.1433)", None, "cli")
137 :
138 ("IronPython", "2.6.1", "", "", ("", ""),
139 ".NET 2.0.50727.1433"),
140
141 ("2.7.4 (IronPython 2.7.4 (2.7.0.40) on Mono 4.0.30319.1 (32-bit))", None, "cli")
142 :
143 ("IronPython", "2.7.4", "", "", ("", ""),
144 "Mono 4.0.30319.1 (32-bit)"),
145
Benjamin Petersone549ead2009-03-28 21:42:05 +0000146 ("2.5 (trunk:6107, Mar 26 2009, 13:02:18) \n[Java HotSpot(TM) Client VM (\"Apple Computer, Inc.\")]",
147 ('Jython', 'trunk', '6107'), "java1.5.0_16")
148 :
149 ("Jython", "2.5.0", "trunk", "6107",
150 ('trunk:6107', 'Mar 26 2009'), "java1.5.0_16"),
Ezio Melottif076f532013-10-21 03:03:32 +0300151
Benjamin Petersone549ead2009-03-28 21:42:05 +0000152 ("2.5.2 (63378, Mar 26 2009, 18:03:29)\n[PyPy 1.0.0]",
153 ('PyPy', 'trunk', '63378'), self.save_platform)
154 :
155 ("PyPy", "2.5.2", "trunk", "63378", ('63378', 'Mar 26 2009'),
156 "")
157 }
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +0100158 for (version_tag, scm, sys_platform), info in \
Benjamin Petersone549ead2009-03-28 21:42:05 +0000159 sys_versions.items():
160 sys.version = version_tag
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +0100161 if scm is None:
Ned Deily5c4b0d02017-03-04 00:19:55 -0500162 if hasattr(sys, "_git"):
163 del sys._git
Benjamin Petersone549ead2009-03-28 21:42:05 +0000164 else:
Victor Stinnerfe2d5ba2017-11-28 22:29:32 +0100165 sys._git = scm
Benjamin Petersone549ead2009-03-28 21:42:05 +0000166 if sys_platform is not None:
167 sys.platform = sys_platform
168 self.assertEqual(platform.python_implementation(), info[0])
169 self.assertEqual(platform.python_version(), info[1])
170 self.assertEqual(platform.python_branch(), info[2])
171 self.assertEqual(platform.python_revision(), info[3])
172 self.assertEqual(platform.python_build(), info[4])
173 self.assertEqual(platform.python_compiler(), info[5])
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000174
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000175 def test_system_alias(self):
176 res = platform.system_alias(
177 platform.system(),
178 platform.release(),
179 platform.version(),
180 )
181
182 def test_uname(self):
183 res = platform.uname()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000184 self.assertTrue(any(res))
Larry Hastings68386bc2012-06-24 14:30:41 -0700185 self.assertEqual(res[0], res.system)
186 self.assertEqual(res[1], res.node)
187 self.assertEqual(res[2], res.release)
188 self.assertEqual(res[3], res.version)
189 self.assertEqual(res[4], res.machine)
190 self.assertEqual(res[5], res.processor)
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000191
R. David Murrayca2edce2010-03-22 17:48:48 +0000192 @unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
193 def test_uname_win32_ARCHITEW6432(self):
194 # Issue 7860: make sure we get architecture from the correct variable
195 # on 64 bit Windows: if PROCESSOR_ARCHITEW6432 exists we should be
196 # using it, per
197 # http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx
198 try:
R. David Murraya1135542010-03-24 00:29:21 +0000199 with support.EnvironmentVarGuard() as environ:
R. David Murrayca2edce2010-03-22 17:48:48 +0000200 if 'PROCESSOR_ARCHITEW6432' in environ:
201 del environ['PROCESSOR_ARCHITEW6432']
202 environ['PROCESSOR_ARCHITECTURE'] = 'foo'
203 platform._uname_cache = None
204 system, node, release, version, machine, processor = platform.uname()
205 self.assertEqual(machine, 'foo')
206 environ['PROCESSOR_ARCHITEW6432'] = 'bar'
207 platform._uname_cache = None
208 system, node, release, version, machine, processor = platform.uname()
209 self.assertEqual(machine, 'bar')
210 finally:
211 platform._uname_cache = None
212
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000213 def test_java_ver(self):
214 res = platform.java_ver()
Alexandre Vassalottie9f305f2008-05-16 04:39:54 +0000215 if sys.platform == 'java':
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000216 self.assertTrue(all(res))
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000217
218 def test_win32_ver(self):
219 res = platform.win32_ver()
220
221 def test_mac_ver(self):
222 res = platform.mac_ver()
Benjamin Peterson856ff5f2008-05-29 21:22:40 +0000223
Larry Hastings68386bc2012-06-24 14:30:41 -0700224 if platform.uname().system == 'Darwin':
Victor Stinner3a521f02018-12-07 11:10:33 +0100225 # We are on a macOS system, check that the right version
226 # information is returned
227 output = subprocess.check_output(['sw_vers'], text=True)
228 for line in output.splitlines():
229 if line.startswith('ProductVersion:'):
230 real_ver = line.strip().split()[-1]
Benjamin Peterson856ff5f2008-05-29 21:22:40 +0000231 break
Victor Stinner3a521f02018-12-07 11:10:33 +0100232 else:
233 self.fail(f"failed to parse sw_vers output: {output!r}")
234
Brett Cannon353411d2009-09-03 21:29:20 +0000235 result_list = res[0].split('.')
236 expect_list = real_ver.split('.')
237 len_diff = len(result_list) - len(expect_list)
238 # On Snow Leopard, sw_vers reports 10.6.0 as 10.6
239 if len_diff > 0:
240 expect_list.extend(['0'] * len_diff)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000241 self.assertEqual(result_list, expect_list)
Benjamin Peterson856ff5f2008-05-29 21:22:40 +0000242
243 # res[1] claims to contain
244 # (version, dev_stage, non_release_version)
245 # That information is no longer available
Ezio Melottib3aedd42010-11-20 19:04:17 +0000246 self.assertEqual(res[1], ('', '', ''))
Benjamin Peterson856ff5f2008-05-29 21:22:40 +0000247
248 if sys.byteorder == 'little':
Ned Deily58e33502011-07-13 15:07:04 -0700249 self.assertIn(res[2], ('i386', 'x86_64'))
Benjamin Peterson856ff5f2008-05-29 21:22:40 +0000250 else:
Ezio Melottib3aedd42010-11-20 19:04:17 +0000251 self.assertEqual(res[2], 'PowerPC')
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000252
Ronald Oussorene186e382010-07-23 11:54:59 +0000253
254 @unittest.skipUnless(sys.platform == 'darwin', "OSX only test")
255 def test_mac_ver_with_fork(self):
256 # Issue7895: platform.mac_ver() crashes when using fork without exec
257 #
258 # This test checks that the fix for that issue works.
259 #
260 pid = os.fork()
261 if pid == 0:
262 # child
263 info = platform.mac_ver()
264 os._exit(0)
265
266 else:
267 # parent
268 cpid, sts = os.waitpid(pid, 0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000269 self.assertEqual(cpid, pid)
270 self.assertEqual(sts, 0)
Ronald Oussorene186e382010-07-23 11:54:59 +0000271
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000272 def test_libc_ver(self):
Victor Stinner476b1132018-12-05 14:04:52 +0100273 # check that libc_ver(executable) doesn't raise an exception
Alexandre Vassalottie9f305f2008-05-16 04:39:54 +0000274 if os.path.isdir(sys.executable) and \
275 os.path.exists(sys.executable+'.exe'):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000276 # Cygwin horror
Georg Brandl89fad142010-03-14 10:23:39 +0000277 executable = sys.executable + '.exe'
278 else:
279 executable = sys.executable
Victor Stinner476b1132018-12-05 14:04:52 +0100280 platform.libc_ver(executable)
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000281
Victor Stinner476b1132018-12-05 14:04:52 +0100282 filename = support.TESTFN
283 self.addCleanup(support.unlink, filename)
284
285 with mock.patch('os.confstr', create=True, return_value='mock 1.0'):
286 # test os.confstr() code path
287 self.assertEqual(platform.libc_ver(), ('mock', '1.0'))
288
289 # test the different regular expressions
290 for data, expected in (
291 (b'__libc_init', ('libc', '')),
292 (b'GLIBC_2.9', ('glibc', '2.9')),
293 (b'libc.so.1.2.5', ('libc', '1.2.5')),
294 (b'libc_pthread.so.1.2.5', ('libc', '1.2.5_pthread')),
295 (b'', ('', '')),
296 ):
297 with open(filename, 'wb') as fp:
298 fp.write(b'[xxx%sxxx]' % data)
299 fp.flush()
300
301 # os.confstr() must not be used if executable is set
302 self.assertEqual(platform.libc_ver(executable=filename),
303 expected)
304
305 # binary containing multiple versions: get the most recent,
306 # make sure that 1.9 is seen as older than 1.23.4
307 chunksize = 16384
308 with open(filename, 'wb') as f:
309 # test match at chunk boundary
310 f.write(b'x'*(chunksize - 10))
Serhiy Storchaka2a9b8ba2018-07-09 11:47:45 +0300311 f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0')
Victor Stinner476b1132018-12-05 14:04:52 +0100312 self.assertEqual(platform.libc_ver(filename, chunksize=chunksize),
Serhiy Storchaka2a9b8ba2018-07-09 11:47:45 +0300313 ('glibc', '1.23.4'))
314
Serhiy Storchaka7917aad2018-09-04 15:04:25 +0300315 @support.cpython_only
316 def test__comparable_version(self):
317 from platform import _comparable_version as V
318 self.assertEqual(V('1.2.3'), V('1.2.3'))
319 self.assertLess(V('1.2.3'), V('1.2.10'))
320 self.assertEqual(V('1.2.3.4'), V('1_2-3+4'))
321 self.assertLess(V('1.2spam'), V('1.2dev'))
322 self.assertLess(V('1.2dev'), V('1.2alpha'))
323 self.assertLess(V('1.2dev'), V('1.2a'))
324 self.assertLess(V('1.2alpha'), V('1.2beta'))
325 self.assertLess(V('1.2a'), V('1.2b'))
326 self.assertLess(V('1.2beta'), V('1.2c'))
327 self.assertLess(V('1.2b'), V('1.2c'))
328 self.assertLess(V('1.2c'), V('1.2RC'))
329 self.assertLess(V('1.2c'), V('1.2rc'))
330 self.assertLess(V('1.2RC'), V('1.2.0'))
331 self.assertLess(V('1.2rc'), V('1.2.0'))
332 self.assertLess(V('1.2.0'), V('1.2pl'))
333 self.assertLess(V('1.2.0'), V('1.2p'))
334
335 self.assertLess(V('1.5.1'), V('1.5.2b2'))
336 self.assertLess(V('3.10a'), V('161'))
337 self.assertEqual(V('8.02'), V('8.02'))
338 self.assertLess(V('3.4j'), V('1996.07.12'))
339 self.assertLess(V('3.1.1.6'), V('3.2.pl0'))
340 self.assertLess(V('2g6'), V('11g'))
341 self.assertLess(V('0.9'), V('2.2'))
342 self.assertLess(V('1.2'), V('1.2.1'))
343 self.assertLess(V('1.1'), V('1.2.2'))
344 self.assertLess(V('1.1'), V('1.2'))
345 self.assertLess(V('1.2.1'), V('1.2.2'))
346 self.assertLess(V('1.2'), V('1.2.2'))
347 self.assertLess(V('0.4'), V('0.4.0'))
348 self.assertLess(V('1.13++'), V('5.5.kw'))
349 self.assertLess(V('0.960923'), V('2.2beta29'))
350
Victor Stinner1dfd3802011-03-03 12:54:07 +0000351
Victor Stinnerea0ca212018-12-05 22:41:52 +0100352 def test_macos(self):
353 self.addCleanup(self.clear_caches)
354
355 uname = ('Darwin', 'hostname', '17.7.0',
356 ('Darwin Kernel Version 17.7.0: '
357 'Thu Jun 21 22:53:14 PDT 2018; '
358 'root:xnu-4570.71.2~1/RELEASE_X86_64'),
359 'x86_64', 'i386')
360 arch = ('64bit', '')
361 with mock.patch.object(platform, 'uname', return_value=uname), \
362 mock.patch.object(platform, 'architecture', return_value=arch):
363 for mac_ver, expected_terse, expected in [
364 # darwin: mac_ver() returns empty strings
365 (('', '', ''),
366 'Darwin-17.7.0',
367 'Darwin-17.7.0-x86_64-i386-64bit'),
368 # macOS: mac_ver() returns macOS version
369 (('10.13.6', ('', '', ''), 'x86_64'),
370 'macOS-10.13.6',
371 'macOS-10.13.6-x86_64-i386-64bit'),
372 ]:
373 with mock.patch.object(platform, 'mac_ver',
374 return_value=mac_ver):
375 self.clear_caches()
376 self.assertEqual(platform.platform(terse=1), expected_terse)
377 self.assertEqual(platform.platform(), expected)
378
379
Walter Dörwaldc69d1c42005-11-21 17:48:12 +0000380if __name__ == '__main__':
Berker Peksag2f3742b2015-05-13 12:32:20 +0300381 unittest.main()