Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1c4f733
[changing-rpc-constants-on-proxy] Modifying `qmi.core.rpc` such that …
May 19, 2026
8563565
[changing-rpc-constants-on-proxy] mypy fixes.
May 19, 2026
40ee94d
[changing-rpc-constants-on-proxy] Trying to make RPC constant call wi…
May 19, 2026
2cfb6c6
[changing-rpc-constants-on-proxy] Changed the forwarded proxy constan…
May 20, 2026
7311a08
[changing-rpc-constants-on-proxy] Adding an explanation on tutorial a…
May 20, 2026
e4841f3
[changing-rpc-constants-on-proxy] tutorial adjustments.
May 21, 2026
5cc23a1
[changing-rpc-constants-on-proxy] print also (parent) type of the RPC…
May 21, 2026
18f559c
test fix
May 21, 2026
aee9d4f
Update badges
May 21, 2026
2aacac4
[changing-rpc-constants-on-proxy] Added a FAIR badge in README.md
May 22, 2026
e735c4f
Merge branch 'changing-rpc-constants-on-proxy' of https://github.com/…
May 22, 2026
88deccc
Removing the FAIR checklist badge
May 26, 2026
1d91022
[changing-rpc-constants-on-proxy] Boy-scouting by adding missing rtsc…
Jun 4, 2026
c1acccb
[changing-rpc-constants-on-proxy] Added extra checks on the possible …
Jun 9, 2026
10c5a5c
[changing-rpc-constants-on-proxy] Changed and specified the `_rpc_con…
Jun 11, 2026
288fcc3
[changing-rpc-constants-on-proxy] Changed the "RPC Constants" to "RPC…
Jun 11, 2026
7c1c9b0
[changing-rpc-constants-on-proxy] fixed a mistake in adwin.
Jun 11, 2026
023e9e6
[changing-rpc-constants-on-proxy] Added a warning on use of `_rpc_con…
Jun 11, 2026
bef57fe
[changing-rpc-constants] trying a mypy fix
Jun 26, 2026
cef270b
Merge remote-tracking branch 'origin/main' into changing-rpc-constant…
Jun 29, 2026
f93feb7
[214-changing-rpc-constants-on-proxy] Fix an error in the unittests.
Jun 29, 2026
d17823c
[214-changing-rpc-constants-on-proxy] Fix an error in the unittests.
Jun 29, 2026
cb4bc3a
[214-changing-rpc-constants-on-proxy] Trying to fox coverage issue by…
Jun 29, 2026
2122472
[214-changing-rpc-constants-on-proxy] Trying to fox coverage issue by…
Jun 29, 2026
c427a8c
[214-changing-rpc-constants-on-proxy] Trying to fox coverage issue by…
Jun 29, 2026
b8b58a3
[214-changing-rpc-constants-on-proxy] Trying to fox coverage issue by…
Jun 29, 2026
4028fa5
[214-changing-rpc-constants-on-proxy] Trying to fix coverage issue by…
Jun 29, 2026
1d0cbf1
[214-changing-rpc-constants-on-proxy] Trying to fix coverage issue by…
Jun 29, 2026
69bfe10
[214-changing-rpc-constants-on-proxy] Removed previous coverage versi…
Jun 29, 2026
9ebb294
[214-changing-rpc-constants-on-proxy] Split coverage in multiple fold…
Jun 29, 2026
b76c090
[214-changing-rpc-constants-on-proxy] Test running first the regular …
Jun 29, 2026
bf1bfd7
[214-changing-rpc-constants-on-proxy] Now run unittests specifically …
Jun 29, 2026
92a5b15
[214-changing-rpc-constants-on-proxy] Editing RPC error catching such…
Jun 29, 2026
84bcd27
Update badges
Jun 29, 2026
f6c0903
[214-changing-rpc-constants-on-proxy] Remove extra test line in cover…
Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[run]
core = ctrace
branch = True
branch = True
4 changes: 2 additions & 2 deletions .github/badges/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion .github/workflows/reusable-ci-workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,24 @@ jobs:
path: mypy-${{ inputs.python-version }}.log

- name: Run coverage
timeout-minutes: 15
run: |
coverage run --branch --source=$SOURCE_DIRS -m unittest discover --start-directory=tests --pattern="test_*.py"
batch=0
for dir in "."; do
subdirs=$(find "tests/$dir" -mindepth 1 -maxdepth 1 -type d ! -name "__pycache__" ! -name "data")
if [ -n "$subdirs" ]; then
for subdir in $subdirs; do
echo "Running coverage for tests in $subdir"
coverage run --branch --data-file=".coverage.$batch" -m unittest discover --start-directory="$subdir" --pattern="test_*.py"
batch=$((batch + 1))
done
else
echo "Running coverage for tests in tests/$dir"
coverage run --branch --data-file=".coverage.$batch" -m unittest discover --start-directory=tests/"$dir" --pattern="test_*.py"
batch=$((batch + 1))
fi
done
coverage combine
coverage report --show-missing --fail-under=$COVERAGE_MIN_PERC | tee coverage-${{ inputs.python-version }}.log
COVERAGE_PERC=$(grep "TOTAL" coverage-${{ inputs.python-version }}.log | grep -Eo '[0-9.]+%' | sed 's/%//')
echo "Coverage: $COVERAGE_PERC%"
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.54.0-beta.0] - Unreleased

### Added
- Functions in `qmi.core.rpc` that enable RPC messages to adjust class properties marked as `_rpc_properties` in QMI RPC objects from their proxies.
- In `qmi.core.util` added functions for checking that two values are of same type, or size and type if the type is an iterable (excluding size for strings). This is used for checking that we set only same type of values (and of same size for iterables) as modifiable `_rpc_properties`.

### Changed
- Improvement on exception messages from proxy calls: Exceptions caught in `qmi.core.rpc.QMI_RpcFuture` are now handled with `traceback` to extract the traceback from the exception. The traceback is sent along with the exception to `self._result` of the class so that also the full traceback of the exception can be logged, not just the exception raised.
- Typed `_rpc_properties` to be a _set_ and changed all definitions to be _sets_.

### Deprecated
- Set the `_rpc_constants` to be deprecated and to point to `_rpc_properties`.

## [0.53.0] - 2026-05-11

Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_anapico_apsin
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import time

import qmi
from qmi.instruments.anapico.apsin import Anapico_APSIN
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_bristol_fos
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ from contextlib import nullcontext, AbstractContextManager, ExitStack

import qmi
from qmi.instruments.bristol import Bristol_Fos
from qmi.utils.context_managers import start_stop



def run() -> None:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_highfinesse_ws
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ from collections.abc import Callable
import qmi
from qmi.core.exceptions import QMI_InstrumentException
from qmi.instruments.high_finesse.wlm import HighFinesse_Wlm
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_mcc_usb1808x
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sys

import qmi
from qmi.instruments.mcc.usb1808x import MCC_USB1808X
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_newport_ag_uc8
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sys

import qmi
from qmi.instruments.newport.ag_uc8 import Newport_AG_UC8, AxisStatus
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


CHANNELS = [1, 2, 3, 4]
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_quantum_composer_9530
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ from qmi.core.exceptions import QMI_ApplicationException
from qmi.instruments.quantum_composers.pulse_generator9530 import (
RefClkSource, PulseMode, TriggerMode, TriggerEdge, OutputDriver,
QuantumComposers_PulseGenerator9530)
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_siglent_ssa3000x
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sys

import qmi
from qmi.instruments.siglent.ssa3000x import SSA3000X
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_srs_dc205
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import sys

import qmi
from qmi.instruments.stanford_research_systems.dc205 import SRS_DC205
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_tenma_72psu
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sys

import qmi
from qmi.instruments.tenma import * # This may look like unused in IDE but will import all PSU classes.
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_teraxion_tfn
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import sys

import qmi
from qmi.instruments.teraxion.tfn import Teraxion_TFN, Teraxion_TFNElement
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_thorlabs_k10cr1
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sys

import qmi
from qmi.instruments.thorlabs.k10cr1 import Thorlabs_K10CR1
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_timebase_dim3000
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import time

import qmi
from qmi.instruments.timebase.dim3000 import TimeBase_DIM3000, DIM3000SweepMode, DIM3000FMDeviation
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_wavelength_tclab
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import time

import qmi
from qmi.instruments.wavelength.tclab import Wavelength_TC_Lab
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
2 changes: 1 addition & 1 deletion bin/instruments/qmi_wieserlabs_flexdds
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sys

import qmi
from qmi.instruments.wieserlabs.flexdds import Wieserlabs_FlexDDS_NG_Dual, OutputChannel
from qmi.utils.context_managers import start_stop, open_close
from qmi.utils.context_managers import open_close


def run() -> int:
Expand Down
93 changes: 84 additions & 9 deletions documentation/sphinx/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,95 @@ We can look at the documentation of the Proxy instance:
>>> help(nsg)

This prints the docstring of the NoisySineGenerator class.
It also shows a listing of all RPC methods, signals and class constants of the proxy instance.

As we can read in the help, our noisy sine generator ``nsg`` supports a bunch of methods, including the ``get_sample()`` method.
It also shows a listing of all RPC methods, signals and class properties of the proxy instance::
Help on QMI_RpcProxy in module qmi.core.rpc:

<rpc proxy for help.nsg (qmi.instruments.dummy.noisy_sine_generator.NoisySineGenerator)>
Simulated instrument, useful for testing.

Attributes:
max_frequency: Maximum allowed frequency that can be set.
max_amplitude: Maximum allowed amplitude that can be set.
max_wait: Maximum wait time duration.
max_noise: Maximum noise level that can be set. By default the same as max amplitude.


RPC methods:
- close() -> None
- get_amplitude() -> float
- get_frequency() -> float
- get_name() -> str
- get_noise() -> float
- get_sample() -> float
- get_signals() -> list[qmi.core.pubsub.SignalDescription]
- is_open() -> bool
- open() -> None
- set_amplitude(value: float) -> None
- set_frequency(value: float) -> None
- set_noise(value: float) -> None
- wait(duration: float) -> None

QMI signals:

RPC Properties:
- max_frequency: float = 1000000.0
- max_amplitude: float = 1000.0
- max_wait: = 10.0


Using RPC Properties
^^^^^^^^^^^^^^^^^^^

From the docstring printed out you can see that there are four class attributes present: `max_frequency`, `max_amplitude`, `max_noise` and `max_wait`. But only three of these are listed under "RPC Properties" and `max_noise` is not.
These attributes are used in the class limit different `set` and the `wait` functions to have a maximum possible settable value. Now, the maximum values defined as "RPC Properties" are now actually modifiable, while the `max_noise` is not.
So, using the usual way of adjusting class variables, the three attributes can given new values, f.ex.:

>>> nsg.max_amplitude
1000.0
>>> nsg.max_amplitude = 500.0
>>> nsg.max_amplitude
500.0

Note that to change the value, *the same value type must be used*. Trying to set the `max_amplitude` with an integer value (like `500`) will lead to an exception.
Also, trying to adjust `max_noise`, not included in RPC Properties, will lead to an error:

>>> nsg.max_noise = 20.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
nsg.max_noise = 20.0
^^^^^^^^^^^^^
File ".\qmi\core\rpc.py", line 834, in __setattr__
raise AttributeError("Not allowed to set new attributes on a proxy class.")
AttributeError: Not allowed to set new attributes on a proxy class.


Calling RPC methods
^^^^^^^^^^^^^^^^^^^

As we can read in the help, our noisy sine generator ``nsg`` supports a bunch of methods, including ``get_sample()`` method.
We can retrieve that method's docstring as well:

>>> help(nsg.get_sample)

Help on method get_sample in module qmi.core.rpc:

get_sample(*args, **kwargs) method of qmi.core.rpc.QMI_RpcProxy instance
rpc proxy for get_sample(self) -> float method of qmi.instruments.dummy.noisy_sine_generator.NoisySineGenerator instance.

Get a 'sample' from the virtual instrument sine wave.

Returns:
sample: A time-dependent sample value of sine wave with given frequency and amplitude,
and added Gaussian noise.

Now, let's give it a shot and see what happens:

>>> nsg.get_sample()

Whoops, we got an error! This is because we didn't "open" the instrument first.
Opening an instrument makes a connection to the instrument, which is potentially far away.
Closing the instrument then closes the connection again.
Now, for a virtual instrument this is of course not necessary, but as it simulates a real instrument, we also simulate opening and closing.
Now, for a virtual instrument this is of course not necessary, but as it simulates a real instrument, we also simulate opening and closing.

>>> nsg.open()
>>> nsg.get_sample()
Expand All @@ -106,7 +180,7 @@ We can make a very basic graph of *nsg* samples as follows:
... print(" " * int(40.0 + 0.25 * nsg.get_sample()) + "*")
... time.sleep(0.01)

Feel free to experiment a bit with the other NoisySineGenerator methods, which you can read about by executing the ``help(nsg)``.
Feel free to experiment a bit with the NoisySineGenerator properties and other methods, of which you can read about by executing the ``help(nsg)``.

Also, if you want, have a look at the source code of ``qmi.instruments.dummy.noisy_sine_generator``.
This should convince you that implementing device drivers for QMI instruments is pretty straightforward.
Expand All @@ -116,7 +190,8 @@ When done, close the instrument and exit your Python interpreter:
>>> nsg.close()
>>> qmi.stop()

From now on, we will no longer tell you to execute ``qmi.stop()``, but don't forget to do it.
From now on, we will no longer tell you to execute ``qmi.stop()``, but don't forget to do it, or use a context manager.


Locking an instrument
---------------------
Expand Down Expand Up @@ -166,11 +241,11 @@ The first proxy can interact with the instrument, but the second one cannot, bec
2021-11-30 14:50:55.786 | ERROR | qmi.core.rpc | nsg locked, method request without lock token is denied
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/qutech/Development/qmi/qmi/qmi/core/rpc.py", line 566, in <lambda>
File "./qmi/core/rpc.py", line 566, in <lambda>
blocking_rpc_method_call(self._context, self._rpc_object_address, method_name, self._lock_token,
File "/Users/qutech/Development/qmi/qmi/qmi/core/rpc.py", line 505, in blocking_rpc_method_call
File "./qmi/core/rpc.py", line 505, in blocking_rpc_method_call
return future.wait(rpc_timeout)
File "/Users/qutech/Development/qmi/qmi/qmi/core/rpc.py", line 458, in wait
File "./qmi/core/rpc.py", line 458, in wait
raise QMI_RuntimeException("The object is locked by another proxy")
qmi.core.exceptions.QMI_RuntimeException: The object is locked by another proxy

Expand Down
Loading
Loading