Clarify usage of platform.system()

Many folks use platform.system() to check the operating system their code is running on, possibly thanks to this StackOverflow question and similar ones.

I propose that the documentation for this function be updated to:

  1. Clarify whether platform.system() == "" checks are a sensible way to check the operating system that code is running on.
  2. Note a table of possible known values, similar to the sys.platform documentation

The sys.platform documentation lists possible values in an easy-to-read table.

The platform.system() documentation does not have such a table, but it seems like it should.

This would be useful for checking operating systems without having easy access to a Python interpreter running on that operating system. For example, Emscripten shows in platform.system() when running Python compiled for the web browser, but I don’t know what shows up for WASI. I assume it’s WASI, but it might be Wasi, wasi, or something else.

It might also be helpful to clarify in either/both the sys.platform and platform.system documentation the difference between the two and when/whether one is preferable. This may be a trickier add, as I’m not sure there’s even consensus on which system check is preferable. The platform.system function sells itself as a “user-facing” string, while sys.platform seems to be a slightly less predictable string (see this issue about linux2 versus linux3 in sys.platform, which eventually became simply linux instead and note that Windows always shows win32 in sys.platform thanks to this patch).

8 Likes

I’m surprised that it’s not documented - I feel like I’ve known the distinction forever, but I guess it came up on issues and mailing lists over the years.

The platform module is primarily intended for diagnostic information to be read by humans. Things that you may print into log files for later reference. There are a few functions that are more suitable for programmatic use (architecture() is one, though inconveniently specified), but broadly speaking most of platform’s data should be treated as opaque strings.

sys.platform is also somewhat poorly named, as it essentially refers to the API set of the operating system, rather than the OS itself (hence, Python built against the Win32 API gets win32, regardless of whether it’s an Intel 32-bit, 64-bit, or ARM64 based machine or OS).

The intended breakdown should be that humans read platform and programs read sys. It’s not currently perfect, but it’s the intent. Documentation PRs to improve clarity around here would certainly be welcome.

7 Likes

FWIW, I mostly try to avoid checking “What platform is the code running on?” and instead check for “Is the relevant operating system feature available?”.

That approach leads to check like hasattr(os, "add_dll_directory") instead of checking sys.platform.startswith("win") (or the narrower sys.platform == "win32").

For the specific question of sys.platform vs platform.system(), the former has a table in the docs because its behaviour is actually defined. The latter, not so much (hence the “An empty string is returned if the value cannot be determined.” caveat in its documentation).

As a result, checking platform.system() will probably be consistent across different Python versions and implementations on common platforms, while sys.platform has defined answers for all CPython supported platforms listed in PEP 11.

6 Likes

When I started development of the platform module, I was looking for ways to make it possible for people to tell what file to download from looking at the file name, so Steve’s explanation is correct.

At the time, I was running a project called mxCGIPython in the late 1990s and early 2000s, with a one-file Python interpreter (today, the project has been revived as PyRun). Since this was available for a wider selection of platforms and there was no installer or package store at the time, the filename had to be expressive enough to hint people at the right download. The platform.platform() API was the net result of this effort. Running python3 -m platform will return the string as well, so that it can be used in shell scripts to name the binary archives.

The platform.system() API and several other APIs in the platform module are a enhanced versions of what the uname C function returns on Unix systems. I took this API as an example and made it portable across other platforms with the help of people from the community at the time. Eventually, more and more platform information was added to give a complete picture.

Making the output human recognizable was key in the API design, so often marketing names were preferred over internal system names.

There is no table in the documentation of what the function returns, since unlike sys.platform, the value is not determined by Python, but rather mostly by what the underlying system APIs return (slightly modified to make the output more consistent).

What we could do is add a table of typical return values, but people should really be aware that those values are not set in stone.

3 Likes

This is extra context is helpful, though I’m now unsure what might make sense to note in the documentation to clarify the preferred usage between these two.

I’m curious whether platform.system() == "Windows" checks might be regarded as either an overuse of platform.system() or as less preferable to sys.platform == "win32" or os.name == "nt" checks.

From a bit of hunting in libraries I trust, I’ve seen platform.system() checks in pdm, hatch (platform.windows relies on it), and setuptools. All 3 of those libraries also use check sys.platform in other places (pdm, hatch, setuptools).

We do have a current discussion (spread over a few locations, but here is a starting point) about adding a sys attribute (potentially a sysconfig variable now, due to opposition) to more accurately specify the current platform.

It’s largely driven by the fact that on Windows, sys.platform is always win32 and platform.machine() is always the underlying OS, and nothing specifies the architecture of the runtime itself (which could be being emulated, so the machine could be ARM64 and the runtime be x64, but there’s no official way to check)[1].

Right now it’s at the stage where it seems people are happiest with enumerating all possible/supported values for MULTIARCH[2] and ensuring that sysconfig.get_config_var("MULTIARCH") is always available. If you have more input, it would be useful to hear it.


  1. And platform.architecture can only return “32” or “64”, and so can’t distinguish between Intel and ARM 64-bit platforms. ↩︎

  2. Which may already be specified somewhere, but I’ve not seen it. ↩︎

1 Like

sys.platform is for build time information. platform.system() for runtime information. I guess that pretty much defines the difference.

os.name is very coarse compared to these, as it only knows about three different platforms at the moment. It’s really only useful to learn which implementation of the os APIs was imported. It may make sense to use this for applications which need to branch depending on file system criteria, but does not really cover other details of the running OS.

3 Likes