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