Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Lib/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,11 +427,16 @@ def _win32_ver(version, csd, ptype):

winver = getwindowsversion()
is_client = (getattr(winver, 'product_type', 1) == 1)
try:
version = _syscmd_ver()[2]
major, minor, build = map(int, version.split('.'))
except ValueError:
major, minor, build = winver.platform_version or winver[:3]

if winver.device_family == "Desktop":
try:
version = _syscmd_ver()[2]
major, minor, build = map(int, version.split('.'))
except ValueError:
major, minor, build = winver.platform_version or winver[:3]
version = '{0}.{1}.{2}'.format(major, minor, build)
else:
major, minor, build = winver[:3]
version = '{0}.{1}.{2}'.format(major, minor, build)

# getwindowsversion() reflect the compatibility mode Python is
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix errors in :func:`sys.getwindowsversion` for Universal Windows Platform
build.
18 changes: 12 additions & 6 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,7 @@ static PyStructSequence_Field windows_version_fields[] = {
{"suite_mask", "Bit mask identifying available product suites"},
{"product_type", "System product type"},
{"platform_version", "Diagnostic version number"},
{"device_family", "Desktop or UWP"},
{0}
};

Expand All @@ -1645,13 +1646,10 @@ static PyStructSequence_Desc windows_version_desc = {
via indexing, the rest are name only */
};

#ifdef MS_WINDOWS_DESKTOP
static PyObject *
_sys_getwindowsversion_from_kernel32(void)
{
#ifndef MS_WINDOWS_DESKTOP
PyErr_SetString(PyExc_OSError, "cannot read version info on this platform");
return NULL;
#else
HANDLE hKernel32;
wchar_t kernel32_path[MAX_PATH];
LPVOID verblock;
Expand Down Expand Up @@ -1688,8 +1686,8 @@ _sys_getwindowsversion_from_kernel32(void)
realBuild = HIWORD(ffi->dwProductVersionLS);
PyMem_RawFree(verblock);
return Py_BuildValue("(kkk)", realMajor, realMinor, realBuild);
#endif /* !MS_WINDOWS_DESKTOP */
}
#endif /* MS_WINDOWS_DESKTOP */

/* Disable deprecation warnings about GetVersionEx as the result is
being passed straight through to the caller, who is responsible for
Expand Down Expand Up @@ -1719,7 +1717,6 @@ sys_getwindowsversion_impl(PyObject *module)
{
PyObject *version;
int pos = 0;
OSVERSIONINFOEXW ver;

if (PyObject_GetOptionalAttrString(module, "_cached_windows_version", &version) < 0) {
return NULL;
Expand All @@ -1729,6 +1726,8 @@ sys_getwindowsversion_impl(PyObject *module)
}
Py_XDECREF(version);

OSVERSIONINFOEXW ver;
ZeroMemory(&ver, sizeof(ver));
ver.dwOSVersionInfoSize = sizeof(ver);
if (!GetVersionExW((OSVERSIONINFOW*) &ver))
return PyErr_SetFromWindowsErr(0);
Expand Down Expand Up @@ -1756,10 +1755,12 @@ sys_getwindowsversion_impl(PyObject *module)
SET_VERSION_INFO(PyLong_FromLong(ver.wSuiteMask));
SET_VERSION_INFO(PyLong_FromLong(ver.wProductType));

#ifdef MS_WINDOWS_DESKTOP
// GetVersion will lie if we are running in a compatibility mode.
// We need to read the version info from a system file resource
// to accurately identify the OS version. If we fail for any reason,
// just return whatever GetVersion said.
// UWP return correct version from GetVersionExW, this is not necessary.
PyObject *realVersion = _sys_getwindowsversion_from_kernel32();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well add a comment saying that this whole #if block should be removed and replaced with the #else and we're just not doing it yet (unless you want to do it now?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what to do here... all the proposed options have been rejected, and the main goal wasn't really to improve things for Windows Desktop... keep in mind that if this is removed without adding an alternative, things will be much worse, as it will return that it's running on Windows 8 in many cases for embedded applications.

@thexai thexai Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nowadays most fields here are obsolete:

    {"service_pack", "Latest Service Pack installed on the system"},
    {"service_pack_major", "Service Pack major version number"},
    {"service_pack_minor", "Service Pack minor version number"},
    {"suite_mask", "Bit mask identifying available product suites"},
    {"product_type", "System product type"},

Current method should return current relevant info, e.g

Windows 11 25H2 10.0.26200.8875

25H2 --> is missing and can be obtained from registry.
8875 --> (UBR update build revision) is missing and can be obtained from registry and API for UWP:

  // get the system version number
  auto sv = AnalyticsInfo::VersionInfo().DeviceFamilyVersion();
  wchar_t* end;
  unsigned long long  v = wcstoull(sv.c_str(), &end, 10);
  unsigned long long v1 = (v & 0xFFFF000000000000L) >> 48;
  unsigned long long v2 = (v & 0x0000FFFF00000000L) >> 32;
  unsigned long long v3 = (v & 0x00000000FFFF0000L) >> 16;
  unsigned long long v4 = (v & 0x000000000000FFFFL);
  kernelVersionFull = StringUtils::Format("{}.{}.{}", v1, v2, v3);
  if (v4)
    kernelVersionFull += StringUtils::Format(".{}", v4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, nowadays most feature checks involving the version number are incorrect, which is why the compatibility shims on the version number are important - they're more likely to affect the ones that actually apply.

An embedding app that isn't properly manifested should get the version number the OS intends to give it. This is why it's designed this way. The rejected proposals were all ways to circumvent this, but we don't want to circumvent it.

The "current relevant info" for the sys module is "what conditions is this Python runtime running under". For the platform module it's "what is installed on this machine". That's why the platform module does extra work to get the real version, while this function should get the most applicable version, which Windows has decided should be changed for the sake of compatibility.

if (!realVersion) {
if (!PyErr_ExceptionMatches(PyExc_WindowsError)) {
Expand All @@ -1775,6 +1776,11 @@ sys_getwindowsversion_impl(PyObject *module)
}

SET_VERSION_INFO(realVersion);
SET_VERSION_INFO(PyUnicode_FromString("Desktop"));
#else
SET_VERSION_INFO(Py_BuildValue("(kkk)", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber));
SET_VERSION_INFO(PyUnicode_FromString("UWP"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to do this (which I like, tbqh), then we should handle all the API partitions that we have checks for, rather than assuming that anything "not DESKTOP" is UWP.

I believe the UWP case is covered by MS_WINDOWS_APP, and then we should also handle MS_WINDOWS_SYSTEM and MS_WINDOWS_GAMES.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MS_WINDOWS_SYSTEM is only for build system drivers/services and it doesn't seem relevant to Python or applications that has Python embedded. I don't think it needs to be defined, and I believe the instances where it appears should be removed from the code.

MS_WINDOWS_GAMES is only for Xbox Games and it's another flavor of UWP.

MS_WINDOWS_APP is for Xbox Apps and formerly also for "Windows Phone" (which no longer exists). It has the peculiarity (or complication) that it's also defined when compiling for Desktop (because Universal Apps can run also on Desktop).

Then maybe define names "Desktop" "UWP-Games" and "UWP-Apps" or even better:

"Desktop" "Xbox" "UWP" (because MS_WINDOWS_APP is still "universal").

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you've changed here looks fine to me. It wouldn't surprise me if one day we regret using "Xbox" instead of "Games", but that day is likely to be a long way out, and it won't be any worse than places where we used "darwin" instead of "macos".

#endif

#undef SET_VERSION_INFO

Expand Down
Loading