add type hints to Experiment.py and dac.py, set up pyrefly as dev dependency
Build Debian Packages / build (bookworm, debian12) (push) Successful in 9m3s
Build Debian Packages / build (trixie, debian13) (push) Successful in 9m9s

This commit is contained in:
markusro committed 2026-10-17 21:52:10 +02:00
1 parent f9d8de7697
commit efb01250c8
4 files changed
+85 -44

No files matched your search

+12
View File
@@ -39,6 +39,9 @@ maintainers = [
[project.scripts]
DAMARIS3 = "damaris.__main__:main"
[dependency-groups]
dev = ["pyrefly"]
[tool.setuptools.packages.find]
where = ["src"]
@@ -50,6 +53,15 @@ version = {attr = "damaris.__version__"}
[tool.setuptools.data-files]
"share/python3-damaris/images" = ["src/damaris/gui/DAMARIS3.png", "src/damaris/gui/DAMARIS3.ico"]
[tool.pyrefly]
project-includes = [
"src/damaris/experiments/Experiment.py",
"src/damaris/experiments/dac.py",
]
search-path = ["src"]
python-version = "3.11"
python-platform = "linux"
#"share/python3-damaris/doc" = ["doc/index.html"]
#"share/python3-damaris/doc/reference-html" = ["doc/reference-html/*"]
#"share/python3-damaris/doc/tutorial-html" = ["doc/tutorial-html/*"]
+45 -43
View File
@@ -3,20 +3,20 @@ import types
import numpy
class StateBase(object):
def __init__(self):
def __init__(self) -> None:
pass
def to_xml(self, indent = ""):
def to_xml(self, indent: str = "") -> str:
return indent + "<!-- " + repr(self) + " -->"
class StateSimple(StateBase):
def __init__(self, time, content=None):
def __init__(self, time: float, content: object | None = None) -> None:
super(StateSimple, self).__init__()
if time < 0:
raise AssertionError("time for state is negative!")
self.time = time
self.content = content
def to_xml(self, indent = ""):
def to_xml(self, indent: str = "") -> str:
s = indent + '<state time="%s"' % repr(self.time)
if self.content is None:
return s + '/>\n'
@@ -24,14 +24,14 @@ class StateSimple(StateBase):
s += indent + ' ' + str(self.content) + '\n'
s += indent + '</state>\n'
return s
def __repr__(self):
def __repr__(self) -> str:
return 'StateSimple(%s, %s)' % (self.time, repr(self.content))
class StateList(StateBase):
def __init__(self):
def __init__(self) -> None:
super(StateList, self).__init__()
self.list = []
def to_xml(self, indent = " "):
self.list: list[object] = []
def to_xml(self, indent: str = " ") -> str:
s = ""
for k in self.list:
if hasattr(k, "to_xml"):
@@ -39,21 +39,21 @@ class StateList(StateBase):
else:
s += indent + str(k)
return s
def append(self, val):
def append(self, val: object) -> None:
self.list.append(val)
class StateLoop(StateList):
"""Represents a loop in the state tree"""
def __init__(self, repeat):
def __init__(self, repeat: int) -> None:
super(StateLoop, self).__init__()
self.repeat = repeat
def to_xml(self, indent = ""):
def to_xml(self, indent: str = "") -> str:
s = indent + ('<sequent repeat="%d">\n' % self.repeat)
s += super(StateLoop, self).to_xml(indent + " ")
s += indent + '</sequent>\n'
return s
def __repr__(self):
def __repr__(self) -> str:
return 'StateLoop(repeat=%d, %s)' \
% (self.repeat, repr(self.list))
@@ -78,32 +78,32 @@ class Experiment:
:param list rf_gates: list of rf gates ttl channels len(rf_gates) == len(rf_sources)
"""
job_id = 0
job_id: int = 0
def __init__(self, gating=None, rf_sources=[], rf_gates=[]):
self.job_id = Experiment.job_id
def __init__(self, gating: float | None = None, rf_sources: list[int] = [], rf_gates: list[int] = []) -> None:
self.job_id: int = Experiment.job_id
Experiment.job_id += 1
self.state_list = StateList()
self.list_stack = []
self.description = { }
self.gating = gating
self.state_list: StateList = StateList()
self.list_stack: list[StateList] = []
self.description: dict[str, object] = { }
self.gating: float | None = gating
assert type(rf_sources) == type(list()), "rf_sources needs to be a list with the channels"
assert type(rf_gates) == type(list()), "rf_gates needs to be a list with the channels"
assert len(rf_gates) == len(rf_sources), "rf_sources and rf_gates must have equal number of entries"
self.rf_sources = rf_sources
self.rf_gates = rf_gates
self.rf_sources: list[int] = rf_sources
self.rf_gates: list[int] = rf_gates
#for tracking the experiment length:
#because loops are possible we need to track the length for each loop level
self.total_time=[0.0]
self.total_time: list[float] = [0.0]
#and we need to know the number of iterations of the loops to multiply the state.
self.loop_iterations=[1]
self.loop_iterations: list[int] = [1]
# Commands -------------------------------------------------------------------------------------
def ttl_pulse(self, length, channel = None, value = None):
def ttl_pulse(self, length: float, channel: int | None = None, value: int | None = None) -> None:
"""
Creates a state with length **length** and switches requested bits of the pulse programmer to HIGH.
@@ -151,7 +151,7 @@ class Experiment:
self.total_time[-1] += length
## Same as ttl_pulse, but no *channel* keyword
def ttls(self, length = None, value = None):
def ttls(self, length: float | None = None, value: int | None = None) -> None:
"""
Same as ttl_pulse, but no *channel* keyword
@@ -159,6 +159,7 @@ class Experiment:
:param int value: lines to set (integer)
:return:
"""
assert value is not None, "ttls needs an integer value"
the_value=int(value)
s_content = '<ttlout value="0x%06x"/>' % the_value
if length is not None:
@@ -168,7 +169,7 @@ class Experiment:
self.state_list.append(s_content)
def rf_pulse(self, length=None, phase=0, source=0):
def rf_pulse(self, length: float | None = None, phase: float = 0, source: int = 0) -> None:
"""
Make an rf pulse, including gating and phase switching. Only possible if Experiment is configured.
@@ -183,7 +184,7 @@ class Experiment:
self.ttls(length=length, value=self.rf_gates[source]+self.rf_sources[source])
## Beginning of a new state
def state_start(self, time):
def state_start(self, time: float) -> None:
"""
Starts a state in the pulse programs with duration *time*.
This must be closed with :func:`state_end`.
@@ -194,13 +195,13 @@ class Experiment:
self.total_time[-1] += time
## End of *state_start*
def state_end(self):
def state_end(self) -> None:
"""
Closes a previous :func:`state_start`
"""
self.state_list.append('</state>\n')
def wait(self, time, ttls=None, gating=False):
def wait(self, time: float, ttls: int | None = None, gating: bool = False) -> None:
"""
Waits for a specified amount of time without performing any actions.
@@ -241,6 +242,7 @@ class Experiment:
"""
if gating:
assert self.gating is not None, "gating time must be set for gating=True"
time -= self.gating
if ttls is not None:
s_content = '<ttlout value="0x%06x"/>' % ttls
@@ -251,7 +253,7 @@ class Experiment:
self.total_time[-1] += time
def record(self, samples, frequency, timelength=None, sensitivity = None, ttls=None, channels = 3, offset = None, impedance = None):
def record(self, samples: int, frequency: float, timelength: float | None = None, sensitivity: float | list[float] | None = None, ttls: int | None = None, channels: int = 3, offset: int | list[int] | None = None, impedance: float | list[float] | None = None) -> None:
"""
Records data with a given number of samples, sampling frequency, and sensitivity.
This command starts data acquisition from the ADC (Analog-to-Digital Converter).
@@ -365,7 +367,7 @@ class Experiment:
## Create a loop on the pulse programmer. Loop contents can not change inside the loop.
# @params iterations Number of loop iterations
def loop_start(self, iterations):
def loop_start(self, iterations: int) -> None:
"""
creates a loop of given number of iterations and has to be closed by loop_end().
Commands inside the loop can not change, i.e., the parameters are the same for each loop run.
@@ -384,7 +386,7 @@ class Experiment:
self.loop_iterations.append(iterations)
## End loop state
def loop_end(self):
def loop_end(self) -> None:
"""
Closes a l:func:`loop_start` statement.
"""
@@ -396,7 +398,7 @@ class Experiment:
self.total_time[-1] += looptime*loopiterations
def set_frequency(self, frequency, phase, ttls=0):
def set_frequency(self, frequency: float, phase: float, ttls: int = 0) -> None:
"""
Sets the frequency and phase of the frequency source and optionally further channels.
The state length is 2 µs. Stabilisation time depends on RF source.
@@ -412,7 +414,7 @@ class Experiment:
self.total_time[-1] += 2e-6
def set_pfg(self, dac_value=None, length=None, shape=('rec', 0), trigger=4, is_seq=False):
def set_pfg(self, dac_value: int | None = None, length: float | None = None, shape: tuple[str, float] = ('rec', 0), trigger: int = 4, is_seq: bool = False) -> None:
"""
This sets the value for the PFG, it also sets it back to dac_value=0 automatically.
If you don't wish to do so (i.e. line shapes) set is_seq=1
@@ -480,7 +482,7 @@ class Experiment:
else: # don't know what to do
raise SyntaxError("form is unknown: %s"%form)
def set_dac(self, dac_value, dac_id=1, length=None, is_seq=False, ttls=0):
def set_dac(self, dac_value: int, dac_id: int = 1, length: float | None = None, is_seq: bool = False, ttls: int = 0) -> None:
"""
Sets the value for the DAC (Digital-to-Analog Converter) and optionally additional TTL lines.
This command is used to control analog output signals, such as pulsed field gradients.
@@ -544,7 +546,7 @@ class Experiment:
self.total_time[-1] += 42*9e-8
def set_phase(self, phase, ttls=0):
def set_phase(self, phase: float, ttls: int = 0) -> None:
"""
Sets the phase of the RF source to the specified value.
@@ -589,7 +591,7 @@ class Experiment:
self.total_time[-1] += 0.5e-6
def set_description(self, key, value):
def set_description(self, key: str, value: object) -> None:
"""
Sets a description key-value pair in the experiment description dictionary.
@@ -629,7 +631,7 @@ class Experiment:
print('Warning: Overwriting existing description "%s" = "%s" with "%s"' % (key, self.description[key], value))
self.description[key] = value
def set_pts_local(self):
def set_pts_local(self) -> None:
"""
Sets the PTS310/PTS500 frequency source to local mode.
State length is 2 µs.
@@ -653,7 +655,7 @@ class Experiment:
return self.job_id
def write_xml_string(self):
def write_xml_string(self) -> str:
"""
Returns the current program as a string
@@ -706,14 +708,14 @@ class Experiment:
return xml_string
def write_quit_job(self):
def write_quit_job(self) -> str:
"""
Returns a xml quit-job
:returns: XML quit-job
"""
return '<?xml version="1.0" encoding="ISO-8859-1"?>\n<quit/>'
def get_length(self):
def get_length(self) -> float:
timelength = 0.0
#calculate the correct timelength also for unclosed loops, if there is no unclosed loop
#the timelength of this experiment is self.total_time[-1].
@@ -723,13 +725,13 @@ class Experiment:
return timelength
class Quit(Experiment):
def write_xml_string(self):
def write_xml_string(self) -> str:
return '<?xml version="1.0" encoding="ISO-8859-1"?>\n<quit no="%d"/>'%self.job_id
# /Public Methods ------------------------------------------------------------------------------
def self_test():
def self_test() -> None:
e = Experiment()
e.set_description("key", "value")
e.set_frequency(85e6, 90, ttls=16)
+1 -1
View File
@@ -2,7 +2,7 @@
"""
This module holds everything connected with the DAC and PFG
"""
def conv(I_out=0):
def conv(I_out: float = 0) -> int:
"""
converts the demanded Output current in Integer
"""
Generated
+27
View File
@@ -938,6 +938,25 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
]
[[package]]
name = "pyrefly"
version = "1.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b3/3e/ea6893be167fa14307d7f9a924bed9ecbbbd91b01953890b9b3c58ed5359/pyrefly-1.4.0.tar.gz", hash = "sha256:bb57c004a4842cfe93f93aebb5203ca6209a5f0c8bda47e8bc289e70940fd910", size = 6962062, upload-time = "2026-10-15T16:54:11.819Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/17/dfce6611acc00cb26667e5e1011438cc510f3067f961e0f07f7a0c68f416/pyrefly-1.4.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:460cec39bc8743915e9c2c8c10ba926ad0b29b099d89c1859fb97e0059f42fa7", size = 15705624, upload-time = "2026-10-15T16:53:43.412Z" },
{ url = "https://files.pythonhosted.org/packages/a6/78/a7b96442123fa6ed733de0d1ea933e35d6fc6b7c9cf16e05279abb493493/pyrefly-1.4.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5a6370d686b6cbbac8869832f09a52012888371956fbc68d446a349bc85ccd43", size = 14978669, upload-time = "2026-10-15T16:53:46.546Z" },
{ url = "https://files.pythonhosted.org/packages/23/97/dfcfec6a9d8948ab4b2746f7e87558a82bffabb7a54c22e7d3ce4bdc1932/pyrefly-1.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f1b16923a6583a6764e8aec07787e2ef4df644c7030294cba71339783a9451c", size = 15438858, upload-time = "2026-10-15T16:53:48.904Z" },
{ url = "https://files.pythonhosted.org/packages/31/12/6fa414aa87e398d6ddc7145a5d993b0975fc3511198dbb72c47873653ab6/pyrefly-1.4.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:688ad4cb4d6e711d67026fdcfb37b599f86061efe11ba5704287330e7041a69e", size = 16852241, upload-time = "2026-10-15T16:53:51.496Z" },
{ url = "https://files.pythonhosted.org/packages/a8/73/2d7312262cf6481d6ad46eed55b8f2592c3cff60f95cc2f30a114cd1484e/pyrefly-1.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5f3c9dddb6f3128beb49cf4e0f5cb88d4d17f039008d8e0c2be653966acd234", size = 16751917, upload-time = "2026-10-15T16:53:54.39Z" },
{ url = "https://files.pythonhosted.org/packages/2c/65/b44a1cb23667bcbcdc88558cbef79fa9e07a1152c2a1da148036b1a2e0d1/pyrefly-1.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6951f51687304caebfedf13dd1a943aeafdbc95abcc4e24a683b97467a77007", size = 16066345, upload-time = "2026-10-15T16:53:57.436Z" },
{ url = "https://files.pythonhosted.org/packages/a6/07/7ca6ff0c53e3a90ecdab05790d33f430fca5c48687613a0b182744166d98/pyrefly-1.4.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:838de4d4a8b9b8ed39bedd0b8b55629c69661d73a6fa3a3a4adff24b9dbeb399", size = 15461223, upload-time = "2026-10-15T16:54:00.364Z" },
{ url = "https://files.pythonhosted.org/packages/61/be/ec7b2f38a898ce5b2219dda730e87d31fc8cfb2108a3d5844f9d590f1281/pyrefly-1.4.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:afb9ce2e0ae3e6106fb45f6d47536ac92b6423e601e08401cdefa850066444f5", size = 16119099, upload-time = "2026-10-15T16:54:02.664Z" },
{ url = "https://files.pythonhosted.org/packages/6e/16/1b9549168b38a575d9c168e17b4b4ee30028d602ec0f7b07c198ef265dbe/pyrefly-1.4.0-py3-none-win32.whl", hash = "sha256:839db536d0ac29f1b23e2d974fb911075c2283e3e623755c45d826aafac175bc", size = 14858880, upload-time = "2026-10-15T16:54:05.114Z" },
{ url = "https://files.pythonhosted.org/packages/03/02/18f075d4de0e255f184f42fc28c1f0ecc9f49dd528678479fc90cf12246c/pyrefly-1.4.0-py3-none-win_amd64.whl", hash = "sha256:7aff37c2b711c8240ed650da0c6148237b327bf7e5dfacee65884f1adea22305", size = 15726220, upload-time = "2026-10-15T16:54:07.452Z" },
{ url = "https://files.pythonhosted.org/packages/92/62/92947076681550904c72a9dd03b24e92694ede9a48548f5e487c849e66ea/pyrefly-1.4.0-py3-none-win_arm64.whl", hash = "sha256:88879f0cb749e5fea81d9ff4c2d5431a70349c458bcdf0b61de72c79214d90a5", size = 14943690, upload-time = "2026-10-15T16:54:09.689Z" },
]
[[package]]
name = "pyserial"
version = "3.5"
@@ -976,6 +995,11 @@ dependencies = [
{ name = "tables" },
]
[package.dev-dependencies]
dev = [
{ name = "pyrefly" },
]
[package.metadata]
requires-dist = [
{ name = "matplotlib", specifier = ">=1.4.1,<=3.6" },
@@ -990,6 +1014,9 @@ requires-dist = [
{ name = "tables", specifier = ">=3.1.1" },
]
[package.metadata.requires-dev]
dev = [{ name = "pyrefly" }]
[[package]]
name = "pyxdg"
version = "0.28"