Skip to content

Commit e66ee49

Browse files
author
Henri Ervasti
committed
[py314-and-log-dir-patch] Making QMI Python 3.14 compatible and fixed a bug in log directory location for services.
1 parent e3d9c51 commit e66ee49

File tree

7 files changed

+35
-30
lines changed

7 files changed

+35
-30
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,7 @@ dmypy.json
129129
.pyre/
130130

131131
# VS code settings
132-
.vscode
132+
.vscode/
133+
134+
# PyCharm
135+
.idea

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77

8+
## [0.51.2] - 2026-02-20
9+
10+
### Added
11+
- `run` method in `QMI_Context` class as this is now required for Python 3.14, after changes in `threading` module.
12+
13+
### Fixed
14+
- Log file location config for services in `qmi_proc.py`. Location string starting with tilde (~) is now interpreted correctly to be the user's home directory.
15+
- Minor edits in docstrings, tutorial and .gitignore file.
16+
817
## [0.51.1] - 2026-01-26
918

1019
### Fixed

documentation/sphinx/source/tutorial.rst

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -860,16 +860,15 @@ Connecting with USBTMC devices on Windows can be tricky. Make sure you have libu
860860
https://pypi.org/project/libusb1/ and https://pypi.org/project/PyVISA/ (and perhaps pyvisa-py).
861861

862862
Then you'll need to have the backend set-up correctly, in case the ``libusb-1.0.dll`` is not found in your path.
863-
An example script to set-up and test the backend is
864-
```python
865-
import usb.core
866-
from usb.backend import libusb1
863+
An example script to set-up and test the backend is::
867864

868-
backend = libusb1.get_backend(
869-
find_library=lambda x: "<path_to_your_env>\\Lib\\site-packages\\usb1\\libusb-1.0.dll")
865+
import usb.core
866+
from usb.backend import libusb1
870867

871-
dev = list(usb.core.find(find_all=True))
872-
```
868+
backend = libusb1.get_backend(
869+
find_library=lambda x: "<path_to_your_env>\\Lib\\site-packages\\usb1\\libusb-1.0.dll")
870+
871+
dev = list(usb.core.find(find_all=True))
873872

874873
If you can now find devices, the backend is set correctly. There are of course other ways to set-up your backend
875874
as well, but as said, it can be tricky...

qmi/core/context.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,12 @@ def resolve_file_name(self, file_name: str) -> str:
461461

462462
return os.path.normpath(string.Template(file_name).substitute(mapping))
463463

464+
def run(self, _rpc_thread_run: Callable) -> None:
465+
"""Run thread. From Python 3.14 onwards objects that are run within a context, need a `run` method with
466+
a single input where the callable to run is given. Addition of this function should have no effect when
467+
using earlier Python versions."""
468+
_rpc_thread_run()
469+
464470
def start(self) -> None:
465471
"""Start the context.
466472

qmi/core/transport.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1321,7 +1321,7 @@ def create_transport(
13211321
- "port" (for UDP and TCP transports) specifies the UDP/TCP port number of the server/client.
13221322
- "connect_timeout" is TCP connection timeout.
13231323
1324-
Serial:
1324+
Serial:
13251325
- "device" is the name of the serial port, for example "COM3" or "/dev/ttyUSB0".
13261326
- "baudrate" specifies the number of bits per second.
13271327
This attribute is only required for instruments with a configurable baud rate.

qmi/tools/proc.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ def start_local_process(context_name: str) -> int:
326326
else:
327327
output_dir = qmi.context().get_qmi_home_dir()
328328

329+
# Replace tilde with full path as otherwise it might be literally used
330+
output_dir = output_dir.replace("~", os.path.expanduser("~")) if output_dir.startswith("~") else output_dir
329331
# Create output log file.
330332
datetime = time.strftime("%Y%m%d_%H%M%S", time.gmtime())
331333
output_file_name = os.path.join(output_dir, context_name + "_" + datetime + ".out")

tests/core/test_transport.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -118,18 +118,11 @@ async def the_call():
118118
with open_close(QMI_UdpTransport("localhost", int(self.server_port) - 1)) as trans:
119119

120120
# Send some bytes from server to transport.
121-
loop = asyncio.get_event_loop()
122-
if loop.is_closed():
123-
asyncio.set_event_loop(loop)
124-
125-
loop.run_until_complete(the_call())
121+
asyncio.run(the_call())
126122
# Receive message through transport.
127123
data = trans.read_until(b"\n", timeout=1.0)
128124
self.assertEqual(data, b"aap noot\n")
129125

130-
if loop.is_running():
131-
loop.close()
132-
133126
w_data = b"aap noot\n"
134127
trans.write(w_data)
135128
data = trans.read(len(w_data))
@@ -603,10 +596,10 @@ def test_udp_large_packages(self):
603596
with self.assertRaises(qmi.core.exceptions.QMI_RuntimeException):
604597
read = trans.read(100, timeout=1.0) # The `read` will set the size to 4096 in any case
605598

606-
except AssertionError as ass:
599+
except AssertionError as a_err:
607600
# Catch this as some servers apparently fragment the message to be max of 4096 bytes, so it does not crash
608601
if len(read) != 100:
609-
raise AssertionError from ass
602+
raise AssertionError from a_err
610603

611604
trans.discard_read()
612605

@@ -618,26 +611,19 @@ async def the_call():
618611
l.stop()
619612

620613
# Send some bytes from server to transport.
621-
loop = asyncio.get_event_loop()
622-
if loop.is_closed():
623-
asyncio.set_event_loop(loop)
624-
625-
loop.run_until_complete(the_call())
614+
asyncio.run(the_call())
626615
try:
627616
# The same should happen with read_until (triggers exception).
628617
with self.assertRaises(qmi.core.exceptions.QMI_RuntimeException):
629618
trans.read_until(b"\n", timeout=1.0)
630619

631-
except qmi.core.exceptions.QMI_TimeoutException as tim:
620+
except qmi.core.exceptions.QMI_TimeoutException as t_exc:
632621
# Catch this as some servers apparently fragment the message to be max of 4096 bytes, so it does not crash
633622
if len(trans._read_buffer) != 4096:
634-
raise AssertionError from tim
623+
raise AssertionError from t_exc
635624

636625
trans.discard_read()
637626

638-
if loop.is_running():
639-
loop.close()
640-
641627
# Send some bytes from server to transport.
642628
self.server_sock.sendto(s.encode(), trans._address)
643629

0 commit comments

Comments
 (0)