MacSysAdmin 2020 update: This One Goes to 11

In my Wednesday session for MacSysAdmin 2020 Online, I talk a bit about the dual-versioning of macOS Big Sur. Since the talk was recorded and submitted a few weeks ago, some things have changed!

When I recorded the presentation, Big Sur was on beta 6. In that version of Big Sur, the platform module in the bundled Python reported Big Sur’s version as 11.0:

# sw_vers
ProductName:	macOS
ProductVersion:	11.0
BuildVersion:	20A5364e
# /usr/bin/python

WARNING: Python 2.7 is not recommended. 
This version is included in macOS for compatibility with legacy software. 
Future versions of macOS will not include Python 2.7. 
Instead, it is recommended that you transition to using 'python3' from within Terminal.

Python 2.7.16 (default, Aug 24 2020, 12:22:49) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.1) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.mac_ver()
('11.0', ('', '', ''), 'x86_64')

In Big Sur beta 9, that behavior changed:

# sw_vers
ProductName:	macOS
ProductVersion:	11.0
BuildVersion:	20A5384c
# python

WARNING: Python 2.7 is not recommended. 
This version is included in macOS for compatibility with legacy software. 
Future versions of macOS will not include Python 2.7. 
Instead, it is recommended that you transition to using 'python3' from within Terminal.

Python 2.7.16 (default, Sep 20 2020, 08:12:36) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.3) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.mac_ver()
('10.16', ('', '', ''), 'x86_64')

platform.mac_ver() is now returning a version of “10.16” instead of “11.0”. Part of the reason for the previous behavior was because the bundled Python was compiled against the macOS 11 SDK. Code compiled against that SDK should “see” an OS version of 11.0; code compiled against older macOS SDKs “see” an OS version of 10.16. This is to enhance compatibility with OS version checks that may assume the first part of the OS version is always “10”.

So how and why did the behavior change? A friend who works for Apple assured me that the bundled Python is still compiled against the macOS 11 SDK, but he also noticed that the SYSTEM_VERSION_COMPAT environment variable was set to “1” inside the Python process:

>>> import os
>>> os.environ["SYSTEM_VERSION_COMPAT"]
'1'

When this environment variable is set to “1”, code that asks for the version of macOS will get “10.16” as an answer. I poked around a bit to understand why and how that variable got set, and noticed this:

# grep -R SYSTEM_VERSION_COMPAT /System/Library/Frameworks/Python.framework 
Binary file /System/Library/Frameworks/Python.framework/Versions/2.7/bin/python matches
Binary file /System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2 matches
Binary file /System/Library/Frameworks/Python.framework/Versions/2.7/bin/pythonw matches
Binary file /System/Library/Frameworks/Python.framework/Versions/2.7/bin/pythonw2.7 matches
Binary file /System/Library/Frameworks/Python.framework/Versions/2.7/bin/pythonw2 matches
Binary file /System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 matches

Setting that variable is hard-coded right into Apple’s python binaries! I didn’t expect that. So that’s the how. But why? I don’t know the answer, but I have a guess. There’s a clue when you start the built-in python:

WARNING: Python 2.7 is not recommended. 
This version is included in macOS for compatibility with legacy software. 

Since Apple now includes Python only “for compatibility with legacy software” like, say, package pre- and postinstall scripts, Apple may have decided that “legacy” OS version styles also made the most sense when using Python in Big Sur. In other words, a legacy script still using the legacy Python probably hasn’t been updated to deal with a macOS version that doesn’t start with “10”. So my guess is this change was made specifically to reduce breakage in “legacy” Python scripts that check the macOS version. One might wonder why the same thing hasn’t been done for perl and bash.

Side note:

The various python* binaries in /System/Library/Frameworks/Python.framework/Versions/2.7/bin are actually wrappers that ultimately call /System/Library/Frameworks/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python. If you use this binary directly instead of /usr/bin/python, the SYSTEM_VERSION_COMPAT variable is not set, and platform.mac_ver() returns “11.0” as the OS version…

# /System/Library/Frameworks/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python 

WARNING: Python 2.7 is not recommended. 
This version is included in macOS for compatibility with legacy software. 
Future versions of macOS will not include Python 2.7. 
Instead, it is recommended that you transition to using 'python3' from within Terminal.

Python 2.7.16 (default, Sep 20 2020, 08:12:36) 
[GCC Apple LLVM 12.0.0 (clang-1200.0.30.3) [+internal-os, ptrauth-isa=sign+stri on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.mac_ver()
('11.0', ('', '', ''), 'x86_64')
MacSysAdmin 2020 update: This One Goes to 11

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s