]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-151929: Get machine ID and uptime on Windows in pythoninfo (#152146) (...
authorVictor Stinner <vstinner@python.org>
Thu, 25 Jun 2026 13:08:35 +0000 (15:08 +0200)
committerGitHub <noreply@github.com>
Thu, 25 Jun 2026 13:08:35 +0000 (13:08 +0000)
[3.15] gh-151929: Get machine ID and uptime on Windows in pythoninfo (#152146) (#152187)

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

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

(cherry picked from commit f9910519af8beecba7d65e0348f48fb70b9a31c8)
(cherry picked from commit ae4c2c126b5a67bcf22f182ce818294c1e71b1c5)

Lib/test/pythoninfo.py
Modules/_winapi.c
Modules/clinic/_winapi.c.h

index 657552e7aa0ca8a1384140100c234eb79789c7e9..0d3899d0972165cd503224e17e79d167bd57fed9 100644 (file)
@@ -8,6 +8,9 @@ import traceback
 import warnings
 
 
+MS_WINDOWS = (sys.platform == "win32")
+
+
 def normalize_text(text):
     if text is None:
         return None
@@ -894,8 +897,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
 
@@ -980,19 +1005,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")
 
 
@@ -1025,20 +1041,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])
@@ -1046,17 +1064,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)
@@ -1065,7 +1114,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):
index f4e73828121c80c7f45d8e8bdd4d81246f2fc7b9..767a5b35f45664f53b9fdc2dbb69e4e84f307bcb 100644 (file)
@@ -2960,6 +2960,21 @@ _winapi_CopyFile2_impl(PyObject *module, LPCWSTR existing_file_name,
 }
 
 
+/*[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
@@ -3006,6 +3021,7 @@ static PyMethodDef winapi_functions[] = {
     _WINAPI__MIMETYPES_READ_WINDOWS_REGISTRY_METHODDEF
     _WINAPI_NEEDCURRENTDIRECTORYFOREXEPATH_METHODDEF
     _WINAPI_COPYFILE2_METHODDEF
+    _WINAPI_GETTICKCOUNT64_METHODDEF
     {NULL, NULL}
 };
 
index f8b623fca082d2e411dafc0587a75123be2f7632..bd8a64bb57391cac15d63a05aa5ce5b961b851cc 100644 (file)
@@ -2161,4 +2161,22 @@ exit:
 
     return return_value;
 }
-/*[clinic end generated code: output=6cd07628af447d0a input=a9049054013a1b77]*/
+
+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);
+}
+/*[clinic end generated code: output=4a408e816118d0a6 input=a9049054013a1b77]*/