Python's "platform.mac_ver()" reports incorrect MacOS version

Viewed 1199

I'm using Python platform module to identify the MacOS version like this:

import platform
print(platform.mac_ver())

Output:

In [1]: import platform

In [2]: platform.mac_ver()
Out[2]: ('10.16', ('', '', ''), 'x86_64')

I have updated to BigSur and the version is incorrect, it should be 11.0.1

enter image description here

I looked at the source code of platform and it seems to parse a this file /System/Library/CoreServices/SystemVersion.plist to get the information. When reading this file from Python I get an incorrect version but from bash it is the correct one

Bash:

Amirs-MacBook-Pro:~ arossert$ cat /System/Library/CoreServices/SystemVersion.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ProductBuildVersion</key>
    <string>20B50</string>
    <key>ProductCopyright</key>
    <string>1983-2020 Apple Inc.</string>
    <key>ProductName</key>
    <string>macOS</string>
    <key>ProductUserVisibleVersion</key>
    <string>11.0.1</string>
    <key>ProductVersion</key>
    <string>11.0.1</string>
    <key>iOSSupportVersion</key>
    <string>14.2</string>
</dict>
</plist>

Python:

In [4]: print(open("/System/Library/CoreServices/SystemVersion.plist").read())
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ProductBuildVersion</key>
    <string>20B50</string>
    <key>ProductCopyright</key>
    <string>1983-2020 Apple Inc.</string>
    <key>ProductName</key>
    <string>Mac OS X</string>
    <key>ProductUserVisibleVersion</key>
    <string>10.16</string>
    <key>ProductVersion</key>
    <string>10.16</string>
    <key>iOSSupportVersion</key>
    <string>14.2</string>
</dict>
</plist>

What am I missing?

This is the output from the same ipython session

In [3]: print(open("/System/Library/CoreServices/SystemVersion.plist").read())
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ProductBuildVersion</key>
    <string>20B50</string>
    <key>ProductCopyright</key>
    <string>1983-2020 Apple Inc.</string>
    <key>ProductName</key>
    <string>Mac OS X</string>
    <key>ProductUserVisibleVersion</key>
    <string>10.16</string>
    <key>ProductVersion</key>
    <string>10.16</string>
    <key>iOSSupportVersion</key>
    <string>14.2</string>
</dict>
</plist>


In [4]: cat /System/Library/CoreServices/SystemVersion.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>ProductBuildVersion</key>
    <string>20B50</string>
    <key>ProductCopyright</key>
    <string>1983-2020 Apple Inc.</string>
    <key>ProductName</key>
    <string>macOS</string>
    <key>ProductUserVisibleVersion</key>
    <string>11.0.1</string>
    <key>ProductVersion</key>
    <string>11.0.1</string>
    <key>iOSSupportVersion</key>
    <string>14.2</string>
</dict>
</plist>
3 Answers

In the Known Issues section of the Big Sur release notes, the following is present:

Some third-party scripts might produce unexpected results due to the change in macOS version from 10.x to 11. (62477208)

Workaround: Set SYSTEM_VERSION_COMPAT=1 in the calling environment, for example: $ SYSTEM_VERSION_COMPAT=1 legacy_script.pl

There is also a rather extensive 3rd-party writeup at https://eclecticlight.co/2020/08/13/macos-version-numbering-isnt-so-simple/


Applications compiled for Big Sur or later get back "11.0" as the operating system version.

Applications compiled for earlier versions get "10.16". This is so logic that assumes 10 as the prefix will not break.

The environment variable SYSTEM_VERSION_COMPAT can be used to control which version of the file is returned; SYSTEM_VERSION_COMPAT=0 cat /System/Library/CoreServices/SystemVersion.plist returns 11.0.1, whereas SYSTEM_VERSION_COMPAT=1 cat /System/Library/CoreServices/SystemVersion.plist returns 10.16. (Note that there should be a space, not a newline, between the assignment and the invocation of cat, such that the shell sees this as a transient environment variable assignment rather than the assignment of a non-exported shell variable).

Here is a quick fix before this bug gets fixed.

    p = subprocess.Popen("sw_vers", stdout=subprocess.PIPE)
    result = p.communicate()[0]
    print result

Basically get the version directly from the shell, hope this helps.

The platform.mac_ver() is giving you the Kernal version.

If you need to dynamically get the OS Name for your current mac. I built this to try and solve the problem. This is probably wildly overcooked code but, it works. lol

https://github.com/JayRizzo/JayRizzoTools/blob/master/pyMacOsName.py

My code will Return a Tuple of the OS with the build number. ('macOS', 'Monterey', '12.4', '21F79')

If you want to see it used in a different way: https://github.com/JayRizzo/JayRizzoTools/blob/master/pyHeaderMac.py this is used to generate the header shown in the code below. (first 10 lines)

Specifically # Created Syst: macOS Monterey 12.4 (21F79) Kernel: Darwin 21.5.0 is all dynamically generated.

Please view the end of my code to see some examples if you just need to get the name.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =============================================================================
# Created Syst: macOS Monterey 12.4 (21F79) Kernel: Darwin 21.5.0
# Created Plat: Python 3.9.5 ('v3.9.5:0a7dcbdb13', 'May  3 2021 13:17:02')
# Created By  : Jeromie Kirchoff
# Created Date: Mon Jun 13 15:22:59 2022 CDT
# Last ModDate: Tue Jun 21 20:59:51 2022 CDT
# =============================================================================
# Notes: Version 2 Class
# =============================================================================

import subprocess
from os import linesep

class MacOSName(object):
    """Setup INIT for MacOSName.
    List Derived From: https://apple.stackexchange.com/a/334337/55628
    This takes the "Major" & "Minor" Versions to generate the name & compares it to sw_vers()
    """
    def __init__(self):
        # super(MacOSName, self).__init__()
        self.escappedTab    = str(r'\t'),
        self.version        = "",
        self.productName    = "",
        self.oSNameFrmVer   = "",
        self.productVersion = "",
        self.buildVersion   = "",
        self.p              = "",
        self.v              = "",
        self.m              = "",
        self.major          = "",
        self.minor          = "",
        self.found          = "",
        self.result         = "",
        self.verz           = "",
        self.macOS          = {"12.04"  : "Monterey", "12.4"   : "Monterey", "12.03"  : "Monterey", "12.3"   : "Monterey", "12.02"  : "Monterey", "12.2"   : "Monterey", "12.01"  : "Monterey", "12.1"   : "Monterey", "12.00"  : "Monterey", "12.0"   : "Monterey", "11.20"  : "Big Sur", "11.19"  : "Big Sur", "11.18"  : "Big Sur", "11.17"  : "Big Sur", "11.16"  : "Big Sur", "11.15"  : "Big Sur", "11.14"  : "Big Sur", "11.13"  : "Big Sur", "11.12"  : "Big Sur", "11.11"  : "Big Sur", "11.10"  : "Big Sur", "11.09"  : "Big Sur", "11.9"   : "Big Sur", "11.08"  : "Big Sur", "11.8"   : "Big Sur", "11.07"  : "Big Sur", "11.7"   : "Big Sur", "11.06"  : "Big Sur", "11.6"   : "Big Sur", "11.05"  : "Big Sur", "11.5"   : "Big Sur", "11.04"  : "Big Sur", "11.4"   : "Big Sur", "11.03"  : "Big Sur", "11.3"   : "Big Sur", "11.02"  : "Big Sur", "11.2"   : "Big Sur", "11.01"  : "Big Sur", "11.1"   : "Big Sur", "11.00"  : "Big Sur", "11.0"   : "Big Sur", "10.20" : "Catalina", "10.19" : "Catalina", "10.18" : "Catalina", "10.17" : "Catalina", "10.16" : "Catalina", "10.15" : "Catalina", "10.14" : "Mojave", "10.13" : "High Sierra", "10.12" : "Sierra", "10.11" : "X El Capitan", "10.10" : "X Yosemite", "10.09" : "X Mavericks", "10.9"  : "X Mavericks", "10.08" : "X Mountain Lion", "10.8"  : "X Mountain Lion", "10.07" : "X Lion", "10.7"  : "X Lion", "10.06" : "X Snow Leopard", "10.6"  : "X Snow Leopard", "10.05" : "X Leopard", "10.5"  : "X Leopard", "10.04" : "X Tiger", "10.4"  : "X Tiger", "10.03" : "X Panther", "10.3"  : "X Panther", "10.02" : "X Jaguar", "10.2"  : "X Jaguar", "10.01" : "X Puma", "10.1"  : "X Puma", "10.0"  : "X Cheetah", "10.00" : "X Cheetah" }

    def getOsName(self, verz):
        self.version = verz
        if isinstance(self.version, set):
            self.version         = ''.join(self.version)
            self.major           = self.version.split(".")[0]
            try:
                self.minor       = str(self.version).split(".")[1]
            except IndexError as e:
                self.minor       = 0
            except Exception as e:
                print(f"\n\n\t\t\tSET ERROR: {e}")
            try:
                for j, k in self.macOS.items():
                    self.v = j.split('.')[0]
                    self.m = j.split('.')[1]
                    if self.v == self.major:
                        if self.m == self.minor:
                            self.found = k
                            break
            except Exception as e:
                print(f"\n\n\t\t\tERROR: {e}")

        elif isinstance(self.version, str):
            self.version         = ''.join(self.version)
            self.major           = self.version.split(".")[0]
            try:
                self.minor       = str(self.version).split(".")[1]
            except IndexError as e:
                self.minor       = 0
            except Exception as e:
                print(f"\n\n\t\t\tSTRING ERROR : {e}")
            try:
                for j, k in self.macOS.items():
                    self.v = j.split('.')[0]
                    self.m = j.split('.')[1]
                    if self.v == self.major:
                        if self.m == self.minor:
                            self.found = k
                            break
            except Exception as e:
                print(f"\n\n\t\t\tERROR: {e}")
        return self.found

    def getCurrentSystemInfo(self):
        self.p              = subprocess.Popen("/usr/bin/sw_vers", shell=True, stdout=subprocess.PIPE)
        self.result         = self.p.communicate()[0]
        self.result         = self.result.decode("utf-8").split(str('\t'))
        self.productName    = self.result[1].split(linesep)[0]
        self.productVersion = self.result[2].split(linesep)[0]
        self.buildVersion   = self.result[3].split(linesep)[0]
        self.oSNameFrmVer   = self.getOsName({'12.4'})
        self.productName    = self.result[1].split('\n')[0]
        # # return Example: ('macOS', 'Monterey', '12.4', '21F79')
        return self.productName, self.oSNameFrmVer, self.productVersion, self.buildVersion

    def main(self):
        return

if __name__ == '__main__':
    print(f"{MacOSName().getCurrentSystemInfo()}")  # Returns TUPLE('macOS', 'Monterey', '12.4', '21F79')
    a = MacOSName().getCurrentSystemInfo()
    pt = a[0] # Product Type
    pn = a[1] # Product Name
    pv = a[2] # Product Version
    pb = a[3] # Product Build Number
    print(f"""
Product Type        : {pt}
Product Name        : {pn}
Product Version     : {pv}
Product Build Number: {pb}
""")
    # Product Type        : macOS
    # Product Name        : Monterey
    # Product Version     : 12.4
    # Product Build Number: 21F79
    # # Supports String Versions
    print(f"{MacOSName().getOsName('11.4')}")           # Returns Name  Big Sur
    print(f"{MacOSName().getOsName('11.04')}")          # Returns Name  Big Sur
    print(f"{MacOSName().getOsName('11.14')}")          # Returns Name  Big Sur
    print(f"{MacOSName().getOsName('10.02')}")          # Returns Name  Mojave
    print(f"{MacOSName().getOsName('10.9')}")           # Returns Name  X Mavericks
    print(f"{MacOSName().getOsName('10.09.424.24')}")   # Returns Name  X Mavericks  (ignores any subversion)
    # # Supports Sets Versions
    print(f"{MacOSName().getOsName(set({'11.4'}))}")    # Returns Name  Big Sur
    print(f"{MacOSName().getOsName(set({'11.4.2'}))}")  # Returns Name  Big Sur
    # Error:
    print(f"{MacOSName().getOsName('75.02')}")          # Returns Name  Empty Tuple
Related