https://github.com/python/cpython/commit/f9910519af8beecba7d65e0348f48fb70b9a31c8
commit: f9910519af8beecba7d65e0348f48fb70b9a31c8
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-06-25T12:02:53Z
summary:

gh-151929: Get machine ID and uptime on Windows in pythoninfo (#152146)

* Replace "linux." prefix with "system." in pythoninfo.
* Add _winapi.GetTickCount64() function.

files:
M Lib/test/pythoninfo.py
M Modules/_winapi.c
M Modules/clinic/_winapi.c.h

diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
index 067e218f797364..7f3d566e988d80 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -8,6 +8,9 @@
 import warnings
 
 
+MS_WINDOWS = (sys.platform == "win32")
+
+
 def normalize_text(text):
     if text is None:
         return None
@@ -906,8 +909,30 @@ def collect_subprocess(info_add):
     copy_attributes(info_add, subprocess, 'subprocess.%s', 
('_USE_POSIX_SPAWN',))
 
 
+def winreg_query(path):
+    try:
+        import winreg
+    except ImportError:
+        return None
+
+    key, path = path.split('\\', 1)
+    sub_key, value = path.rsplit('\\', 1)
+    if key == "HKEY_LOCAL_MACHINE":
+        key = winreg.HKEY_LOCAL_MACHINE
+    else:
+        raise ValueError(f"unknown key {key!r}")
+
+    try:
+        access = winreg.KEY_READ | winreg.KEY_WOW64_64KEY
+        with winreg.OpenKey(key, sub_key, access=access) as key_handle:
+            result, _ = winreg.QueryValueEx(key_handle, value)
+        return result
+    except OSError:
+        return None
+
+
 def collect_windows(info_add):
-    if sys.platform != "win32":
+    if not MS_WINDOWS:
         # Code specific to Windows
         return
 
@@ -999,19 +1024,10 @@ def collect_windows(info_add):
             info_add('windows.ver', line)
 
     # windows.developer_mode: get AllowDevelopmentWithoutDevLicense registry
-    import winreg
-    try:
-        key = winreg.OpenKey(
-            winreg.HKEY_LOCAL_MACHINE,
-            r"SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock")
-        subkey = "AllowDevelopmentWithoutDevLicense"
-        try:
-            value, value_type = winreg.QueryValueEx(key, subkey)
-        finally:
-            winreg.CloseKey(key)
-    except OSError:
-        pass
-    else:
+    value = winreg_query(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows"
+                         r"\CurrentVersion\AppModelUnlock"
+                         r"\AllowDevelopmentWithoutDevLicense")
+    if value is not None:
         info_add('windows.developer_mode', "enabled" if value else "disabled")
 
 
@@ -1044,20 +1060,22 @@ def collect_libregrtest_utils(info_add):
     info_add('libregrtests.build_info', ' '.join(utils.get_build_info()))
 
 
-def linux_get_uptime():
-    # Use CLOCK_BOOTTIME if available
+def uptime_boottime():
+    # Use CLOCK_BOOTTIME
     import time
     try:
         return time.clock_gettime(time.CLOCK_BOOTTIME)
     except (AttributeError, OSError):
-        pass
+        return None
+
 
-    # Otherwise, parse the first member of /proc/uptime
-    uptime = read_first_line("/proc/uptime")
-    if not uptime:
+def uptime_linux():
+    # Parse the first member of /proc/uptime
+    line = read_first_line("/proc/uptime")
+    if not line:
         return
     try:
-        parts = uptime.split()
+        parts = line.split()
         if not parts:
             return
         return float(parts[0])
@@ -1065,17 +1083,48 @@ def linux_get_uptime():
         return
 
 
+def uptime_windows():
+    try:
+        import _winapi
+    except ImportError:
+        return None
+    else:
+        return _winapi.GetTickCount64() / 1000.
+
+
+def get_uptime():
+    for func in (uptime_boottime, uptime_linux, uptime_windows):
+        uptime = func()
+        if uptime is not None:
+            return uptime
+    return None
+
+
+def get_machine_id():
+    if MS_WINDOWS:
+        machine_guid = winreg_query(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft"
+                                    r"\Cryptography\MachineGuid")
+        if machine_guid:
+            return machine_guid
+
+    machine_id = read_first_line("/etc/machine-id")
+    if machine_id:
+        return machine_id
+
+    return None
+
+
 def collect_linux(info_add):
     boot_id = read_first_line("/proc/sys/kernel/random/boot_id")
     if boot_id:
-        info_add('linux.boot_id', boot_id)
+        info_add('system.boot_id', boot_id)
 
     # https://www.freedesktop.org/software/systemd/man/latest/machine-id.html
-    machine_id = read_first_line("/etc/machine-id")
+    machine_id = get_machine_id()
     if machine_id:
-        info_add('linux.machine_id', machine_id)
+        info_add('system.machine_id', machine_id)
 
-    uptime = linux_get_uptime()
+    uptime = get_uptime()
     if uptime is not None:
         # truncate microseconds
         uptime = int(uptime)
@@ -1084,7 +1133,7 @@ def collect_linux(info_add):
             uptime = str(datetime.timedelta(seconds=uptime))
         except ImportError:
             uptime = f'{uptime} sec'
-        info_add('linux.uptime', uptime)
+        info_add('system.uptime', uptime)
 
 
 def collect_info(info):
diff --git a/Modules/_winapi.c b/Modules/_winapi.c
index 5bbb02fe414bfa..535784adedb24d 100644
--- a/Modules/_winapi.c
+++ b/Modules/_winapi.c
@@ -3149,6 +3149,21 @@ _winapi_GetProcessMemoryInfo_impl(PyObject *module, 
HANDLE handle)
 }
 
 
+/*[clinic input]
+_winapi.GetTickCount64
+
+Number of milliseconds that have elapsed since the system was started.
+[clinic start generated code]*/
+
+static PyObject *
+_winapi_GetTickCount64_impl(PyObject *module)
+/*[clinic end generated code: output=cb33c0568f0b3ed1 input=77ed6539ac7d6590]*/
+{
+    ULONGLONG ticks = GetTickCount64();
+    return PyLong_FromUnsignedLongLong(ticks);
+}
+
+
 static PyMethodDef winapi_functions[] = {
     _WINAPI_CLOSEHANDLE_METHODDEF
     _WINAPI_CONNECTNAMEDPIPE_METHODDEF
@@ -3200,6 +3215,7 @@ static PyMethodDef winapi_functions[] = {
     _WINAPI_NEEDCURRENTDIRECTORYFOREXEPATH_METHODDEF
     _WINAPI_COPYFILE2_METHODDEF
     _WINAPI_GETPROCESSMEMORYINFO_METHODDEF
+    _WINAPI_GETTICKCOUNT64_METHODDEF
     {NULL, NULL}
 };
 
diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h
index dd9dbffaa9ac23..031a0783aef60b 100644
--- a/Modules/clinic/_winapi.c.h
+++ b/Modules/clinic/_winapi.c.h
@@ -2358,7 +2358,25 @@ _winapi_GetProcessMemoryInfo(PyObject *module, PyObject 
*arg)
     return return_value;
 }
 
+PyDoc_STRVAR(_winapi_GetTickCount64__doc__,
+"GetTickCount64($module, /)\n"
+"--\n"
+"\n"
+"Number of milliseconds that have elapsed since the system was started.");
+
+#define _WINAPI_GETTICKCOUNT64_METHODDEF    \
+    {"GetTickCount64", (PyCFunction)_winapi_GetTickCount64, METH_NOARGS, 
_winapi_GetTickCount64__doc__},
+
+static PyObject *
+_winapi_GetTickCount64_impl(PyObject *module);
+
+static PyObject *
+_winapi_GetTickCount64(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _winapi_GetTickCount64_impl(module);
+}
+
 #ifndef _WINAPI_GETSHORTPATHNAME_METHODDEF
     #define _WINAPI_GETSHORTPATHNAME_METHODDEF
 #endif /* !defined(_WINAPI_GETSHORTPATHNAME_METHODDEF) */
-/*[clinic end generated code: output=07dfd4bbacaed4a8 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=713a8ce97185b017 input=a9049054013a1b77]*/

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to