Skip to content

Commit 1340ee7

Browse files
authored
Merge branch 'master' into master
2 parents c040cff + 51aa747 commit 1340ee7

11 files changed

Lines changed: 588 additions & 49 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
##
2+
# Copyright (C) 2026 Intel Corporation
3+
#
4+
# SPDX-License-Identifier: MIT
5+
#
6+
##
7+
8+
name: Python Bindings - Validation Checks
9+
10+
on:
11+
pull_request:
12+
branches:
13+
- '**'
14+
paths:
15+
- 'bindings/sysman/python/**'
16+
push:
17+
branches:
18+
- main
19+
- master
20+
- python_bindings
21+
paths:
22+
- 'bindings/sysman/python/**'
23+
workflow_dispatch:
24+
25+
env:
26+
PYTHON_VERSION: '3.10'
27+
28+
jobs:
29+
validation-checks:
30+
name: Validation Checks
31+
runs-on: ubuntu-latest
32+
defaults:
33+
run:
34+
working-directory: bindings/sysman/python
35+
permissions:
36+
contents: read
37+
38+
steps:
39+
- name: Checkout code
40+
uses: actions/checkout@v4
41+
42+
- name: Set up Python ${{ env.PYTHON_VERSION }}
43+
uses: actions/setup-python@v5
44+
with:
45+
python-version: ${{ env.PYTHON_VERSION }}
46+
47+
- name: Install dependencies for Level-Zero loader build
48+
run: |
49+
sudo apt-get update
50+
sudo apt-get install -y cmake build-essential
51+
52+
- name: Build and install Level-Zero loader
53+
working-directory: ${{ github.workspace }}
54+
run: |
55+
mkdir -p build
56+
cd build
57+
cmake .. -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr
58+
cmake --build . --parallel $(nproc)
59+
sudo cmake --build . --target install
60+
sudo ldconfig
61+
62+
- name: Create virtual environment and install
63+
run: |
64+
python -m venv .venv
65+
source .venv/bin/activate
66+
pip install --upgrade pip --quiet
67+
pip install -e . --quiet
68+
69+
- name: Run validation checks
70+
run: |
71+
source .venv/bin/activate
72+
73+
# Check 1: Verify installation location
74+
PYZES_LOCATION=$(python -c "import pyzes; print(pyzes.__file__ if hasattr(pyzes, '__file__') else 'N/A')")
75+
if [[ ! "$PYZES_LOCATION" =~ "bindings/sysman/python/source" ]]; then
76+
echo "❌ FAIL: pyzes is not loading from local source"
77+
echo "Location: $PYZES_LOCATION"
78+
exit 1
79+
fi
80+
81+
# Check 2: Test silent import (no stdout output)
82+
OUTPUT=$(python -c "import pyzes" 2>/dev/null)
83+
if [ -n "$OUTPUT" ]; then
84+
echo "❌ FAIL: Import produced stdout output:"
85+
echo "$OUTPUT"
86+
echo ""
87+
echo "The 'import pyzes' statement should not print anything to stdout."
88+
echo "Please remove any print statements from the module initialization."
89+
exit 1
90+
fi
91+
92+
# Check 3: Validate structure definitions match C headers
93+
python test/validate_structures.py || {
94+
echo ""
95+
echo "Structure validation failed. Please ensure Python ctypes structures match the C headers."
96+
exit 1
97+
}
98+
99+
echo "✅ All validation checks passed: Local installation verified, import is silent, structures validated"

bindings/sysman/python/source/examples/pyzes_black_box_test.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ def get_temperature_sensor_string(temp_sensor):
216216
pz.ZES_TEMP_SENSORS_GPU: "ZES_TEMP_SENSORS_GPU",
217217
pz.ZES_TEMP_SENSORS_MEMORY: "ZES_TEMP_SENSORS_MEMORY",
218218
pz.ZES_TEMP_SENSORS_GLOBAL_MIN: "ZES_TEMP_SENSORS_GLOBAL_MIN",
219+
pz.ZES_TEMP_SENSORS_VOLTAGE_REGULATOR: "ZES_TEMP_SENSORS_VOLTAGE_REGULATOR",
219220
pz.ZES_TEMP_SENSORS_GPU_MIN: "ZES_TEMP_SENSORS_GPU_MIN",
220221
pz.ZES_TEMP_SENSORS_MEMORY_MIN: "ZES_TEMP_SENSORS_MEMORY_MIN",
221222
}
@@ -552,6 +553,10 @@ def test_global_operation(driver_handle, device_handle, device_index):
552553
ProcessArray = pz.zes_process_state_t * process_count.value
553554
processes = ProcessArray()
554555

556+
for i in range(process_count.value):
557+
processes[i].stype = pz.ZES_STRUCTURE_TYPE_PROCESS_STATE
558+
processes[i].pNext = None
559+
555560
rc = pz.zesDeviceProcessesGetState(
556561
device_handle, byref(process_count), processes
557562
)
@@ -563,11 +568,10 @@ def test_global_operation(driver_handle, device_handle, device_index):
563568
for i in range(process_count.value):
564569
process = processes[i]
565570
print_verbose(f" Process {i}:")
566-
print_verbose(f" PID: {process.pid}")
571+
print_verbose(f" PID: {process.processId}")
567572
print_verbose(f" Memory Size: {process.memSize} bytes")
568-
print_verbose(f" Shared Memory Size: {process.sharedMemSize} bytes")
569-
print_verbose(f" Engine Type Flags: 0x{process.engineType:08X}")
570-
print_verbose(f" Subdevice ID: {process.subdeviceId}")
573+
print_verbose(f" Shared Memory Size: {process.sharedSize} bytes")
574+
print_verbose(f" Engine Type Flags: 0x{process.engines:08X}")
571575

572576
return True
573577

@@ -750,18 +754,21 @@ def test_device_processes(device_handle, device_index):
750754
ProcessArray = pz.zes_process_state_t * process_count.value
751755
processes = ProcessArray()
752756

757+
for i in range(process_count.value):
758+
processes[i].stype = pz.ZES_STRUCTURE_TYPE_PROCESS_STATE
759+
processes[i].pNext = None
760+
753761
rc = pz.zesDeviceProcessesGetState(device_handle, byref(process_count), processes)
754762
if not check_rc(f"zesDeviceProcessesGetState(device {device_index}, handles)", rc):
755763
return False
756764

757765
for i in range(process_count.value):
758766
process = processes[i]
759767
print_verbose(f" Process {i}:")
760-
print_verbose(f" PID: {process.pid}")
768+
print_verbose(f" PID: {process.processId}")
761769
print_verbose(f" Memory Size: {process.memSize} bytes")
762-
print_verbose(f" Shared Memory Size: {process.sharedMemSize} bytes")
763-
print_verbose(f" Engine Type Flags: 0x{process.engineType:08X}")
764-
print_verbose(f" Subdevice ID: {process.subdeviceId}")
770+
print_verbose(f" Shared Memory Size: {process.sharedSize} bytes")
771+
print_verbose(f" Engine Type Flags: 0x{process.engines:08X}")
765772

766773
return True
767774

@@ -1287,13 +1294,13 @@ def test_temperature_sensors(device_handle, device_index):
12871294
print_verbose(" Temperature Config:")
12881295
print_verbose(f" Critical Enabled: {bool(temp_config.enableCritical)}")
12891296
print_verbose(
1290-
f" Threshold 1: {temp_config.threshold1:.1f} °C"
1291-
if temp_config.threshold1 >= 0
1297+
f" Threshold 1: {temp_config.threshold1.threshold:.1f} °C"
1298+
if temp_config.threshold1.threshold >= 0
12921299
else " Threshold 1: Not set"
12931300
)
12941301
print_verbose(
1295-
f" Threshold 2: {temp_config.threshold2:.1f} °C"
1296-
if temp_config.threshold2 >= 0
1302+
f" Threshold 2: {temp_config.threshold2.threshold:.1f} °C"
1303+
if temp_config.threshold2.threshold >= 0
12971304
else " Threshold 2: Not set"
12981305
)
12991306
else:

bindings/sysman/python/source/pyzes.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class zes_engine_handle_t(c_void_p):
153153

154154
##
155155

156-
ze_bool_t = c_uint32
156+
ze_bool_t = c_uint8
157157
ze_device_property_flags_t = c_uint32
158158
zes_engine_type_flags_t = c_uint32
159159
zes_device_property_flags_t = c_uint32
@@ -260,6 +260,9 @@ class zes_engine_handle_t(c_void_p):
260260
ZES_FREQ_THROTTLE_REASON_FLAG_PSU_ALERT = 1 << 4
261261
ZES_FREQ_THROTTLE_REASON_FLAG_SW_RANGE = 1 << 5
262262
ZES_FREQ_THROTTLE_REASON_FLAG_HW_RANGE = 1 << 6
263+
ZES_FREQ_THROTTLE_REASON_FLAG_VOLTAGE = 1 << 7
264+
ZES_FREQ_THROTTLE_REASON_FLAG_THERMAL = 1 << 8
265+
ZES_FREQ_THROTTLE_REASON_FLAG_POWER = 1 << 9
263266
ZES_FREQ_THROTTLE_REASON_FLAG_FORCE_UINT32 = 0x7FFFFFFF
264267

265268
## Temperature sensor enums ##
@@ -270,6 +273,9 @@ class zes_engine_handle_t(c_void_p):
270273
ZES_TEMP_SENSORS_GLOBAL_MIN = 3
271274
ZES_TEMP_SENSORS_GPU_MIN = 4
272275
ZES_TEMP_SENSORS_MEMORY_MIN = 5
276+
ZES_TEMP_SENSORS_GPU_BOARD = 6
277+
ZES_TEMP_SENSORS_GPU_BOARD_MIN = 7
278+
ZES_TEMP_SENSORS_VOLTAGE_REGULATOR = 8
273279
ZES_TEMP_SENSORS_FORCE_UINT32 = 0x7FFFFFFF
274280

275281
## Engine type enums ##
@@ -361,8 +367,11 @@ class zes_engine_handle_t(c_void_p):
361367
ZES_STRUCTURE_TYPE_DEVICE_ECC_PROPERTIES = 0x26
362368
ZES_STRUCTURE_TYPE_POWER_LIMIT_EXT_DESC = 0x27
363369
ZES_STRUCTURE_TYPE_POWER_EXT_PROPERTIES = 0x28
370+
ZES_STRUCTURE_TYPE_PROCESS_STATE = 0x16
364371
ZES_STRUCTURE_TYPE_DEVICE_EXT_PROPERTIES = 0x2D # from zes_structure_type_t
365-
ZES_STRUCTURE_TYPE_SUBDEVICE_EXP_PROPERTIES = 0x2E # Experimental subdevice properties
372+
ZES_STRUCTURE_TYPE_SUBDEVICE_EXP_PROPERTIES = (
373+
0x00020004 # Experimental subdevice properties
374+
)
366375
ZES_STRUCTURE_TYPE_MEM_PROPERTIES = 0xB
367376
ZES_STRUCTURE_TYPE_MEM_STATE = 0x1E
368377
ZES_STRUCTURE_TYPE_FREQ_PROPERTIES = 0x9
@@ -423,14 +432,14 @@ class zes_device_properties_t(_PrintableStructure):
423432
## Sysman zes_process_state_t ##
424433
class zes_process_state_t(_PrintableStructure):
425434
_fields_ = [
426-
("pid", c_uint32),
427-
("command", c_char * ZES_STRING_PROPERTY_SIZE),
428-
("memSize", c_uint64), # in bytes
429-
("sharedMemSize", c_uint64), # in bytes
430-
("engineType", zes_engine_type_flags_t),
431-
("subdeviceId", c_uint32),
435+
("stype", c_int32),
436+
("pNext", c_void_p),
437+
("processId", c_uint32),
438+
("memSize", c_uint64),
439+
("sharedSize", c_uint64),
440+
("engines", zes_engine_type_flags_t),
432441
]
433-
_fmt_ = {"memSize": "%d bytes", "sharedMemSize": "%d bytes"}
442+
_fmt_ = {"memSize": "%d bytes", "sharedSize": "%d bytes"}
434443

435444

436445
## PCI structures ##
@@ -717,15 +726,23 @@ class zes_temp_properties_t(_PrintableStructure):
717726
_fmt_ = {"maxTemperature": "%.1f °C"}
718727

719728

729+
class zes_temp_threshold_t(_PrintableStructure):
730+
_fields_ = [
731+
("enableLowToHigh", ze_bool_t),
732+
("enableHighToLow", ze_bool_t),
733+
("threshold", c_double),
734+
]
735+
_fmt_ = {"threshold": "%.1f °C"}
736+
737+
720738
class zes_temp_config_t(_PrintableStructure):
721739
_fields_ = [
722740
("stype", c_int32), # ZES_STRUCTURE_TYPE_TEMP_CONFIG
723741
("pNext", c_void_p),
724742
("enableCritical", ze_bool_t), # enable critical temperature event
725-
("threshold1", c_double), # threshold 1 in degrees Celsius
726-
("threshold2", c_double), # threshold 2 in degrees Celsius
743+
("threshold1", zes_temp_threshold_t),
744+
("threshold2", zes_temp_threshold_t),
727745
]
728-
_fmt_ = {"threshold1": "%.1f °C", "threshold2": "%.1f °C"}
729746

730747

731748
## Engine structures ##

bindings/sysman/python/test/unit_tests/test_global_operations.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -264,28 +264,28 @@ def test_GivenValidDeviceHandleWhenCallingZesDeviceProcessesGetStateWithArrayThe
264264
self, mock_get_func
265265
):
266266
mock_process_count = 2
267-
mock_pid_1 = 1234
268-
mock_pid_2 = 5678
267+
mock_process_id_1 = 1234
268+
mock_process_id_2 = 5678
269269
mock_mem_size_1 = 1073741824 # 1GB
270270
mock_mem_size_2 = 2147483648 # 2GB
271-
mock_shared_mem_size_1 = 536870912 # 512MB
272-
mock_shared_mem_size_2 = 1073741824 # 1GB
273-
mock_subdevice_id_1 = 0
274-
mock_subdevice_id_2 = 1
271+
mock_shared_size_1 = 536870912 # 512MB
272+
mock_shared_size_2 = 1073741824 # 1GB
273+
mock_engines_1 = 0x3
274+
mock_engines_2 = 0x5
275275

276276
def mock_get_processes_state_with_data(device_handle, count_ptr, processes_ptr):
277277
if processes_ptr:
278278
# Fill in process data for first process
279-
processes_ptr[0].pid = mock_pid_1
279+
processes_ptr[0].processId = mock_process_id_1
280280
processes_ptr[0].memSize = mock_mem_size_1
281-
processes_ptr[0].sharedMemSize = mock_shared_mem_size_1
282-
processes_ptr[0].subdeviceId = mock_subdevice_id_1
281+
processes_ptr[0].sharedSize = mock_shared_size_1
282+
processes_ptr[0].engines = mock_engines_1
283283

284284
# Fill in process data for second process
285-
processes_ptr[1].pid = mock_pid_2
285+
processes_ptr[1].processId = mock_process_id_2
286286
processes_ptr[1].memSize = mock_mem_size_2
287-
processes_ptr[1].sharedMemSize = mock_shared_mem_size_2
288-
processes_ptr[1].subdeviceId = mock_subdevice_id_2
287+
processes_ptr[1].sharedSize = mock_shared_size_2
288+
processes_ptr[1].engines = mock_engines_2
289289
else:
290290
count_ptr._obj.value = mock_process_count
291291
return self.pyzes.ZE_RESULT_SUCCESS
@@ -298,6 +298,9 @@ def mock_get_processes_state_with_data(device_handle, count_ptr, processes_ptr):
298298

299299
# Create array for process states
300300
process_array = (self.pyzes.zes_process_state_t * mock_process_count)()
301+
for i in range(mock_process_count):
302+
process_array[i].stype = self.pyzes.ZES_STRUCTURE_TYPE_PROCESS_STATE
303+
process_array[i].pNext = None
301304

302305
result = self.pyzes.zesDeviceProcessesGetState(
303306
device_handle, byref(count), process_array
@@ -306,16 +309,16 @@ def mock_get_processes_state_with_data(device_handle, count_ptr, processes_ptr):
306309
self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS)
307310

308311
# Validate first process data
309-
self.assertEqual(process_array[0].pid, mock_pid_1)
312+
self.assertEqual(process_array[0].processId, mock_process_id_1)
310313
self.assertEqual(process_array[0].memSize, mock_mem_size_1)
311-
self.assertEqual(process_array[0].sharedMemSize, mock_shared_mem_size_1)
312-
self.assertEqual(process_array[0].subdeviceId, mock_subdevice_id_1)
314+
self.assertEqual(process_array[0].sharedSize, mock_shared_size_1)
315+
self.assertEqual(process_array[0].engines, mock_engines_1)
313316

314317
# Validate second process data
315-
self.assertEqual(process_array[1].pid, mock_pid_2)
318+
self.assertEqual(process_array[1].processId, mock_process_id_2)
316319
self.assertEqual(process_array[1].memSize, mock_mem_size_2)
317-
self.assertEqual(process_array[1].sharedMemSize, mock_shared_mem_size_2)
318-
self.assertEqual(process_array[1].subdeviceId, mock_subdevice_id_2)
320+
self.assertEqual(process_array[1].sharedSize, mock_shared_size_2)
321+
self.assertEqual(process_array[1].engines, mock_engines_2)
319322

320323
mock_get_func.assert_called_with("zesDeviceProcessesGetState")
321324
mock_func.assert_called_once()

bindings/sysman/python/test/unit_tests/test_temperature.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ def test_GivenValidTemperatureHandleWhenCallingZesTemperatureGetConfigThenCallSu
105105

106106
def mock_get_config(temp_handle, config_ptr):
107107
config_ptr._obj.enableCritical = mock_enable_critical
108-
config_ptr._obj.threshold1 = mock_threshold1
109-
config_ptr._obj.threshold2 = mock_threshold2
108+
config_ptr._obj.threshold1.threshold = mock_threshold1
109+
config_ptr._obj.threshold2.threshold = mock_threshold2
110110
return self.pyzes.ZE_RESULT_SUCCESS
111111

112112
mock_func = MagicMock(side_effect=mock_get_config)
@@ -118,8 +118,8 @@ def mock_get_config(temp_handle, config_ptr):
118118

119119
self.assertEqual(result, self.pyzes.ZE_RESULT_SUCCESS)
120120
self.assertEqual(temp_config.enableCritical, mock_enable_critical)
121-
self.assertEqual(temp_config.threshold1, mock_threshold1)
122-
self.assertEqual(temp_config.threshold2, mock_threshold2)
121+
self.assertEqual(temp_config.threshold1.threshold, mock_threshold1)
122+
self.assertEqual(temp_config.threshold2.threshold, mock_threshold2)
123123
mock_get_func.assert_called_with("zesTemperatureGetConfig")
124124
mock_func.assert_called_once()
125125

bindings/sysman/python/test/unit_tests/test_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ def test_GivenPrintableStructureWhenConvertedToStringThenProvidesFormattedOutput
3232
# Test _PrintableStructure string formatting functionality
3333
temp_state = self.pyzes.zes_temp_config_t()
3434
temp_state.enableCritical = True
35-
temp_state.threshold1 = 75.0
36-
temp_state.threshold2 = 80.0
35+
temp_state.threshold1.threshold = 75.0
36+
temp_state.threshold2.threshold = 80.0
3737

3838
# Test string representation functionality
3939
str_repr = str(temp_state)

0 commit comments

Comments
 (0)