]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-151929: Get machine ID and uptime on Windows in pythoninfo (#152146) (...
authorVictor Stinner <vstinner@python.org>
Thu, 25 Jun 2026 12:34:16 +0000 (14:34 +0200)
committerGitHub <noreply@github.com>
Thu, 25 Jun 2026 12:34:16 +0000 (12:34 +0000)
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)

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

index 067e218f797364c1cd518e3b3855052765428891..7f3d566e988d804c0fc7939c168fb56f6745c941 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
@@ -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):
index 683c19e4d0c4a128ef203b180440436ef6bcafb2..1ca9c90f107bb7bf91a777fe697d0ce619588567 100644 (file)
@@ -3087,6 +3087,21 @@ _winapi_ReportEvent_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
@@ -3137,6 +3152,7 @@ static PyMethodDef winapi_functions[] = {
     _WINAPI__MIMETYPES_READ_WINDOWS_REGISTRY_METHODDEF
     _WINAPI_NEEDCURRENTDIRECTORYFOREXEPATH_METHODDEF
     _WINAPI_COPYFILE2_METHODDEF
+    _WINAPI_GETTICKCOUNT64_METHODDEF
     {NULL, NULL}
 };
 
index 00cce91dca43b1c302cf22337a50a4f6a1dc87df..029c6e32e8f87430bbf7054e51af8b330a170216 100644 (file)
@@ -2331,7 +2331,25 @@ exit:
     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=4ab94eaee93a0a90 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=daa24d62e7db458d input=a9049054013a1b77]*/