(root)/
Python-3.12.0/
Lib/
test/
test_platform.py
       1  import os
       2  import copy
       3  import pickle
       4  import platform
       5  import subprocess
       6  import sys
       7  import unittest
       8  from unittest import mock
       9  
      10  from test import support
      11  from test.support import os_helper
      12  
      13  FEDORA_OS_RELEASE = """\
      14  NAME=Fedora
      15  VERSION="32 (Thirty Two)"
      16  ID=fedora
      17  VERSION_ID=32
      18  VERSION_CODENAME=""
      19  PLATFORM_ID="platform:f32"
      20  PRETTY_NAME="Fedora 32 (Thirty Two)"
      21  ANSI_COLOR="0;34"
      22  LOGO=fedora-logo-icon
      23  CPE_NAME="cpe:/o:fedoraproject:fedora:32"
      24  HOME_URL="https://fedoraproject.org/"
      25  DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f32/system-administrators-guide/"
      26  SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"
      27  BUG_REPORT_URL="https://bugzilla.redhat.com/"
      28  REDHAT_BUGZILLA_PRODUCT="Fedora"
      29  REDHAT_BUGZILLA_PRODUCT_VERSION=32
      30  REDHAT_SUPPORT_PRODUCT="Fedora"
      31  REDHAT_SUPPORT_PRODUCT_VERSION=32
      32  PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
      33  """
      34  
      35  UBUNTU_OS_RELEASE = """\
      36  NAME="Ubuntu"
      37  VERSION="20.04.1 LTS (Focal Fossa)"
      38  ID=ubuntu
      39  ID_LIKE=debian
      40  PRETTY_NAME="Ubuntu 20.04.1 LTS"
      41  VERSION_ID="20.04"
      42  HOME_URL="https://www.ubuntu.com/"
      43  SUPPORT_URL="https://help.ubuntu.com/"
      44  BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
      45  PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
      46  VERSION_CODENAME=focal
      47  UBUNTU_CODENAME=focal
      48  """
      49  
      50  TEST_OS_RELEASE = r"""
      51  # test data
      52  ID_LIKE="egg spam viking"
      53  EMPTY=
      54  # comments and empty lines are ignored
      55  
      56  SINGLE_QUOTE='single'
      57  EMPTY_SINGLE=''
      58  DOUBLE_QUOTE="double"
      59  EMPTY_DOUBLE=""
      60  QUOTES="double\'s"
      61  SPECIALS="\$\`\\\'\""
      62  # invalid lines
      63  =invalid
      64  =
      65  INVALID
      66  IN-VALID=value
      67  IN VALID=value
      68  """
      69  
      70  
      71  class ESC[4;38;5;81mPlatformTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      72      def clear_caches(self):
      73          platform._platform_cache.clear()
      74          platform._sys_version_cache.clear()
      75          platform._uname_cache = None
      76          platform._os_release_cache = None
      77  
      78      def test_architecture(self):
      79          res = platform.architecture()
      80  
      81      @os_helper.skip_unless_symlink
      82      @support.requires_subprocess()
      83      def test_architecture_via_symlink(self): # issue3762
      84          with support.PythonSymlink() as py:
      85              cmd = "-c", "import platform; print(platform.architecture())"
      86              self.assertEqual(py.call_real(*cmd), py.call_link(*cmd))
      87  
      88      def test_platform(self):
      89          for aliased in (False, True):
      90              for terse in (False, True):
      91                  res = platform.platform(aliased, terse)
      92  
      93      def test_system(self):
      94          res = platform.system()
      95  
      96      def test_node(self):
      97          res = platform.node()
      98  
      99      def test_release(self):
     100          res = platform.release()
     101  
     102      def test_version(self):
     103          res = platform.version()
     104  
     105      def test_machine(self):
     106          res = platform.machine()
     107  
     108      def test_processor(self):
     109          res = platform.processor()
     110  
     111      def setUp(self):
     112          self.save_version = sys.version
     113          self.save_git = sys._git
     114          self.save_platform = sys.platform
     115  
     116      def tearDown(self):
     117          sys.version = self.save_version
     118          sys._git = self.save_git
     119          sys.platform = self.save_platform
     120  
     121      def test_sys_version(self):
     122          # Old test.
     123          for input, output in (
     124              ('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]',
     125               ('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')),
     126              ('2.4.3 (truncation, date, t) \n[GCC]',
     127               ('CPython', '2.4.3', '', '', 'truncation', 'date t', 'GCC')),
     128              ('2.4.3 (truncation, date, ) \n[GCC]',
     129               ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
     130              ('2.4.3 (truncation, date,) \n[GCC]',
     131               ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
     132              ('2.4.3 (truncation, date) \n[GCC]',
     133               ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
     134              ('2.4.3 (truncation, d) \n[GCC]',
     135               ('CPython', '2.4.3', '', '', 'truncation', 'd', 'GCC')),
     136              ('2.4.3 (truncation, ) \n[GCC]',
     137               ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
     138              ('2.4.3 (truncation,) \n[GCC]',
     139               ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
     140              ('2.4.3 (truncation) \n[GCC]',
     141               ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
     142              ):
     143              # branch and revision are not "parsed", but fetched
     144              # from sys._git.  Ignore them
     145              (name, version, branch, revision, buildno, builddate, compiler) \
     146                     = platform._sys_version(input)
     147              self.assertEqual(
     148                  (name, version, '', '', buildno, builddate, compiler), output)
     149  
     150          # Tests for python_implementation(), python_version(), python_branch(),
     151          # python_revision(), python_build(), and python_compiler().
     152          sys_versions = {
     153              ("2.6.1 (r261:67515, Dec  6 2008, 15:26:00) \n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]",
     154               ('CPython', 'tags/r261', '67515'), self.save_platform)
     155              :
     156                  ("CPython", "2.6.1", "tags/r261", "67515",
     157                   ('r261:67515', 'Dec  6 2008 15:26:00'),
     158                   'GCC 4.0.1 (Apple Computer, Inc. build 5370)'),
     159  
     160              ("3.10.8 (tags/v3.10.8:aaaf517424, Feb 14 2023, 16:28:12) [GCC 9.4.0]",
     161               None, "linux")
     162              :
     163                  ('CPython', '3.10.8', '', '',
     164                  ('tags/v3.10.8:aaaf517424', 'Feb 14 2023 16:28:12'), 'GCC 9.4.0'),
     165  
     166              ("2.5 (trunk:6107, Mar 26 2009, 13:02:18) \n[Java HotSpot(TM) Client VM (\"Apple Computer, Inc.\")]",
     167              ('Jython', 'trunk', '6107'), "java1.5.0_16")
     168              :
     169                  ("Jython", "2.5.0", "trunk", "6107",
     170                   ('trunk:6107', 'Mar 26 2009'), "java1.5.0_16"),
     171  
     172              ("2.5.2 (63378, Mar 26 2009, 18:03:29)\n[PyPy 1.0.0]",
     173               ('PyPy', 'trunk', '63378'), self.save_platform)
     174              :
     175                  ("PyPy", "2.5.2", "trunk", "63378", ('63378', 'Mar 26 2009'),
     176                   "")
     177              }
     178          for (version_tag, scm, sys_platform), info in \
     179                  sys_versions.items():
     180              sys.version = version_tag
     181              if scm is None:
     182                  if hasattr(sys, "_git"):
     183                      del sys._git
     184              else:
     185                  sys._git = scm
     186              if sys_platform is not None:
     187                  sys.platform = sys_platform
     188              self.assertEqual(platform.python_implementation(), info[0])
     189              self.assertEqual(platform.python_version(), info[1])
     190              self.assertEqual(platform.python_branch(), info[2])
     191              self.assertEqual(platform.python_revision(), info[3])
     192              self.assertEqual(platform.python_build(), info[4])
     193              self.assertEqual(platform.python_compiler(), info[5])
     194  
     195          with self.assertRaises(ValueError):
     196              platform._sys_version('2. 4.3 (truncation) \n[GCC]')
     197  
     198      def test_system_alias(self):
     199          res = platform.system_alias(
     200              platform.system(),
     201              platform.release(),
     202              platform.version(),
     203          )
     204  
     205      def test_uname(self):
     206          res = platform.uname()
     207          self.assertTrue(any(res))
     208          self.assertEqual(res[0], res.system)
     209          self.assertEqual(res[-6], res.system)
     210          self.assertEqual(res[1], res.node)
     211          self.assertEqual(res[-5], res.node)
     212          self.assertEqual(res[2], res.release)
     213          self.assertEqual(res[-4], res.release)
     214          self.assertEqual(res[3], res.version)
     215          self.assertEqual(res[-3], res.version)
     216          self.assertEqual(res[4], res.machine)
     217          self.assertEqual(res[-2], res.machine)
     218          self.assertEqual(res[5], res.processor)
     219          self.assertEqual(res[-1], res.processor)
     220          self.assertEqual(len(res), 6)
     221  
     222      @unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
     223      def test_uname_win32_without_wmi(self):
     224          def raises_oserror(*a):
     225              raise OSError()
     226  
     227          with support.swap_attr(platform, '_wmi_query', raises_oserror):
     228              self.test_uname()
     229  
     230      def test_uname_cast_to_tuple(self):
     231          res = platform.uname()
     232          expected = (
     233              res.system, res.node, res.release, res.version, res.machine,
     234              res.processor,
     235          )
     236          self.assertEqual(tuple(res), expected)
     237  
     238      def test_uname_replace(self):
     239          res = platform.uname()
     240          new = res._replace(
     241              system='system', node='node', release='release',
     242              version='version', machine='machine')
     243          self.assertEqual(new.system, 'system')
     244          self.assertEqual(new.node, 'node')
     245          self.assertEqual(new.release, 'release')
     246          self.assertEqual(new.version, 'version')
     247          self.assertEqual(new.machine, 'machine')
     248          # processor cannot be replaced
     249          self.assertEqual(new.processor, res.processor)
     250  
     251      def test_uname_copy(self):
     252          uname = platform.uname()
     253          self.assertEqual(copy.copy(uname), uname)
     254          self.assertEqual(copy.deepcopy(uname), uname)
     255  
     256      def test_uname_pickle(self):
     257          orig = platform.uname()
     258          for proto in range(pickle.HIGHEST_PROTOCOL + 1):
     259              with self.subTest(protocol=proto):
     260                  pickled = pickle.dumps(orig, proto)
     261                  restored = pickle.loads(pickled)
     262                  self.assertEqual(restored, orig)
     263  
     264      def test_uname_slices(self):
     265          res = platform.uname()
     266          expected = tuple(res)
     267          self.assertEqual(res[:], expected)
     268          self.assertEqual(res[:5], expected[:5])
     269  
     270      def test_uname_fields(self):
     271          self.assertIn('processor', platform.uname()._fields)
     272  
     273      def test_uname_asdict(self):
     274          res = platform.uname()._asdict()
     275          self.assertEqual(len(res), 6)
     276          self.assertIn('processor', res)
     277  
     278      @unittest.skipIf(sys.platform in ['win32', 'OpenVMS'], "uname -p not used")
     279      @support.requires_subprocess()
     280      def test_uname_processor(self):
     281          """
     282          On some systems, the processor must match the output
     283          of 'uname -p'. See Issue 35967 for rationale.
     284          """
     285          try:
     286              proc_res = subprocess.check_output(['uname', '-p'], text=True).strip()
     287              expect = platform._unknown_as_blank(proc_res)
     288          except (OSError, subprocess.CalledProcessError):
     289              expect = ''
     290          self.assertEqual(platform.uname().processor, expect)
     291  
     292      @unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
     293      def test_uname_win32_ARCHITEW6432(self):
     294          # Issue 7860: make sure we get architecture from the correct variable
     295          # on 64 bit Windows: if PROCESSOR_ARCHITEW6432 exists we should be
     296          # using it, per
     297          # http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx
     298  
     299          # We also need to suppress WMI checks, as those are reliable and
     300          # overrule the environment variables
     301          def raises_oserror(*a):
     302              raise OSError()
     303  
     304          with support.swap_attr(platform, '_wmi_query', raises_oserror):
     305              with os_helper.EnvironmentVarGuard() as environ:
     306                  try:
     307                      if 'PROCESSOR_ARCHITEW6432' in environ:
     308                          del environ['PROCESSOR_ARCHITEW6432']
     309                      environ['PROCESSOR_ARCHITECTURE'] = 'foo'
     310                      platform._uname_cache = None
     311                      system, node, release, version, machine, processor = platform.uname()
     312                      self.assertEqual(machine, 'foo')
     313                      environ['PROCESSOR_ARCHITEW6432'] = 'bar'
     314                      platform._uname_cache = None
     315                      system, node, release, version, machine, processor = platform.uname()
     316                      self.assertEqual(machine, 'bar')
     317                  finally:
     318                      platform._uname_cache = None
     319  
     320      def test_java_ver(self):
     321          res = platform.java_ver()
     322          if sys.platform == 'java':  # Is never actually checked in CI
     323              self.assertTrue(all(res))
     324  
     325      def test_win32_ver(self):
     326          res = platform.win32_ver()
     327  
     328      def test_mac_ver(self):
     329          res = platform.mac_ver()
     330  
     331          if platform.uname().system == 'Darwin':
     332              # We are on a macOS system, check that the right version
     333              # information is returned
     334              output = subprocess.check_output(['sw_vers'], text=True)
     335              for line in output.splitlines():
     336                  if line.startswith('ProductVersion:'):
     337                      real_ver = line.strip().split()[-1]
     338                      break
     339              else:
     340                  self.fail(f"failed to parse sw_vers output: {output!r}")
     341  
     342              result_list = res[0].split('.')
     343              expect_list = real_ver.split('.')
     344              len_diff = len(result_list) - len(expect_list)
     345              # On Snow Leopard, sw_vers reports 10.6.0 as 10.6
     346              if len_diff > 0:
     347                  expect_list.extend(['0'] * len_diff)
     348              # For compatibility with older binaries, macOS 11.x may report
     349              # itself as '10.16' rather than '11.x.y'.
     350              if result_list != ['10', '16']:
     351                  self.assertEqual(result_list, expect_list)
     352  
     353              # res[1] claims to contain
     354              # (version, dev_stage, non_release_version)
     355              # That information is no longer available
     356              self.assertEqual(res[1], ('', '', ''))
     357  
     358              if sys.byteorder == 'little':
     359                  self.assertIn(res[2], ('i386', 'x86_64', 'arm64'))
     360              else:
     361                  self.assertEqual(res[2], 'PowerPC')
     362  
     363  
     364      @unittest.skipUnless(sys.platform == 'darwin', "OSX only test")
     365      def test_mac_ver_with_fork(self):
     366          # Issue7895: platform.mac_ver() crashes when using fork without exec
     367          #
     368          # This test checks that the fix for that issue works.
     369          #
     370          pid = os.fork()
     371          if pid == 0:
     372              # child
     373              info = platform.mac_ver()
     374              os._exit(0)
     375  
     376          else:
     377              # parent
     378              support.wait_process(pid, exitcode=0)
     379  
     380      @unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten")
     381      def test_libc_ver(self):
     382          # check that libc_ver(executable) doesn't raise an exception
     383          if os.path.isdir(sys.executable) and \
     384             os.path.exists(sys.executable+'.exe'):
     385              # Cygwin horror
     386              executable = sys.executable + '.exe'
     387          elif sys.platform == "win32" and not os.path.exists(sys.executable):
     388              # App symlink appears to not exist, but we want the
     389              # real executable here anyway
     390              import _winapi
     391              executable = _winapi.GetModuleFileName(0)
     392          else:
     393              executable = sys.executable
     394          platform.libc_ver(executable)
     395  
     396          filename = os_helper.TESTFN
     397          self.addCleanup(os_helper.unlink, filename)
     398  
     399          with mock.patch('os.confstr', create=True, return_value='mock 1.0'):
     400              # test os.confstr() code path
     401              self.assertEqual(platform.libc_ver(), ('mock', '1.0'))
     402  
     403              # test the different regular expressions
     404              for data, expected in (
     405                  (b'__libc_init', ('libc', '')),
     406                  (b'GLIBC_2.9', ('glibc', '2.9')),
     407                  (b'libc.so.1.2.5', ('libc', '1.2.5')),
     408                  (b'libc_pthread.so.1.2.5', ('libc', '1.2.5_pthread')),
     409                  (b'', ('', '')),
     410              ):
     411                  with open(filename, 'wb') as fp:
     412                      fp.write(b'[xxx%sxxx]' % data)
     413                      fp.flush()
     414  
     415                  # os.confstr() must not be used if executable is set
     416                  self.assertEqual(platform.libc_ver(executable=filename),
     417                                   expected)
     418  
     419          # binary containing multiple versions: get the most recent,
     420          # make sure that 1.9 is seen as older than 1.23.4
     421          chunksize = 16384
     422          with open(filename, 'wb') as f:
     423              # test match at chunk boundary
     424              f.write(b'x'*(chunksize - 10))
     425              f.write(b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0')
     426          self.assertEqual(platform.libc_ver(filename, chunksize=chunksize),
     427                           ('glibc', '1.23.4'))
     428  
     429      @support.cpython_only
     430      def test__comparable_version(self):
     431          from platform import _comparable_version as V
     432          self.assertEqual(V('1.2.3'), V('1.2.3'))
     433          self.assertLess(V('1.2.3'), V('1.2.10'))
     434          self.assertEqual(V('1.2.3.4'), V('1_2-3+4'))
     435          self.assertLess(V('1.2spam'), V('1.2dev'))
     436          self.assertLess(V('1.2dev'), V('1.2alpha'))
     437          self.assertLess(V('1.2dev'), V('1.2a'))
     438          self.assertLess(V('1.2alpha'), V('1.2beta'))
     439          self.assertLess(V('1.2a'), V('1.2b'))
     440          self.assertLess(V('1.2beta'), V('1.2c'))
     441          self.assertLess(V('1.2b'), V('1.2c'))
     442          self.assertLess(V('1.2c'), V('1.2RC'))
     443          self.assertLess(V('1.2c'), V('1.2rc'))
     444          self.assertLess(V('1.2RC'), V('1.2.0'))
     445          self.assertLess(V('1.2rc'), V('1.2.0'))
     446          self.assertLess(V('1.2.0'), V('1.2pl'))
     447          self.assertLess(V('1.2.0'), V('1.2p'))
     448  
     449          self.assertLess(V('1.5.1'), V('1.5.2b2'))
     450          self.assertLess(V('3.10a'), V('161'))
     451          self.assertEqual(V('8.02'), V('8.02'))
     452          self.assertLess(V('3.4j'), V('1996.07.12'))
     453          self.assertLess(V('3.1.1.6'), V('3.2.pl0'))
     454          self.assertLess(V('2g6'), V('11g'))
     455          self.assertLess(V('0.9'), V('2.2'))
     456          self.assertLess(V('1.2'), V('1.2.1'))
     457          self.assertLess(V('1.1'), V('1.2.2'))
     458          self.assertLess(V('1.1'), V('1.2'))
     459          self.assertLess(V('1.2.1'), V('1.2.2'))
     460          self.assertLess(V('1.2'), V('1.2.2'))
     461          self.assertLess(V('0.4'), V('0.4.0'))
     462          self.assertLess(V('1.13++'), V('5.5.kw'))
     463          self.assertLess(V('0.960923'), V('2.2beta29'))
     464  
     465  
     466      def test_macos(self):
     467          self.addCleanup(self.clear_caches)
     468  
     469          uname = ('Darwin', 'hostname', '17.7.0',
     470                   ('Darwin Kernel Version 17.7.0: '
     471                    'Thu Jun 21 22:53:14 PDT 2018; '
     472                    'root:xnu-4570.71.2~1/RELEASE_X86_64'),
     473                   'x86_64', 'i386')
     474          arch = ('64bit', '')
     475          with mock.patch.object(platform, 'uname', return_value=uname), \
     476               mock.patch.object(platform, 'architecture', return_value=arch):
     477              for mac_ver, expected_terse, expected in [
     478                  # darwin: mac_ver() returns empty strings
     479                  (('', '', ''),
     480                   'Darwin-17.7.0',
     481                   'Darwin-17.7.0-x86_64-i386-64bit'),
     482                  # macOS: mac_ver() returns macOS version
     483                  (('10.13.6', ('', '', ''), 'x86_64'),
     484                   'macOS-10.13.6',
     485                   'macOS-10.13.6-x86_64-i386-64bit'),
     486              ]:
     487                  with mock.patch.object(platform, 'mac_ver',
     488                                         return_value=mac_ver):
     489                      self.clear_caches()
     490                      self.assertEqual(platform.platform(terse=1), expected_terse)
     491                      self.assertEqual(platform.platform(), expected)
     492  
     493      def test_freedesktop_os_release(self):
     494          self.addCleanup(self.clear_caches)
     495          self.clear_caches()
     496  
     497          if any(os.path.isfile(fn) for fn in platform._os_release_candidates):
     498              info = platform.freedesktop_os_release()
     499              self.assertIn("NAME", info)
     500              self.assertIn("ID", info)
     501  
     502              info["CPYTHON_TEST"] = "test"
     503              self.assertNotIn(
     504                  "CPYTHON_TEST",
     505                  platform.freedesktop_os_release()
     506              )
     507          else:
     508              with self.assertRaises(OSError):
     509                  platform.freedesktop_os_release()
     510  
     511      def test_parse_os_release(self):
     512          info = platform._parse_os_release(FEDORA_OS_RELEASE.splitlines())
     513          self.assertEqual(info["NAME"], "Fedora")
     514          self.assertEqual(info["ID"], "fedora")
     515          self.assertNotIn("ID_LIKE", info)
     516          self.assertEqual(info["VERSION_CODENAME"], "")
     517  
     518          info = platform._parse_os_release(UBUNTU_OS_RELEASE.splitlines())
     519          self.assertEqual(info["NAME"], "Ubuntu")
     520          self.assertEqual(info["ID"], "ubuntu")
     521          self.assertEqual(info["ID_LIKE"], "debian")
     522          self.assertEqual(info["VERSION_CODENAME"], "focal")
     523  
     524          info = platform._parse_os_release(TEST_OS_RELEASE.splitlines())
     525          expected = {
     526              "ID": "linux",
     527              "NAME": "Linux",
     528              "PRETTY_NAME": "Linux",
     529              "ID_LIKE": "egg spam viking",
     530              "EMPTY": "",
     531              "DOUBLE_QUOTE": "double",
     532              "EMPTY_DOUBLE": "",
     533              "SINGLE_QUOTE": "single",
     534              "EMPTY_SINGLE": "",
     535              "QUOTES": "double's",
     536              "SPECIALS": "$`\\'\"",
     537          }
     538          self.assertEqual(info, expected)
     539          self.assertEqual(len(info["SPECIALS"]), 5)
     540  
     541  
     542  if __name__ == '__main__':
     543      unittest.main()