Package: uldaq (PyPI, v1.2.3)
Method: AiDevice.t_in(channel, scale, flags)
What happened
I accidentally passed a TcType member as the scale argument instead of a TempScale member:
# Bug: TcType.K passed where TempScale is expected
val = ai_device.t_in(channel, TcType.K, TInFlag.DEFAULT)
# Correct call
val = ai_device.t_in(channel, TempScale.CELSIUS, TInFlag.DEFAULT)
The call succeeded without error. TcType.K has integer value 2; TempScale.FAHRENHEIT also has integer value 2. The C library received a valid integer and interpreted it as a request for Fahrenheit readings. My code then labelled those readings as Celsius.
Observed symptom: a thermocouple at ~22 °C read 71.6 — exactly (22 × 9/5) + 32, i.e. the correct Fahrenheit value. The bug was live in a lab data-acquisition system for several weeks because we had no thermocouples wired to live hardware during that period.
Why this is an easy mistake to make
TcType and TempScale appear together in typical usage: you call set_chan_tc_type(channel, TcType.K) to configure the channel, then t_in(channel, TempScale.CELSIUS, TInFlag.DEFAULT) to read it. The two enums are imported from the same module, used in the same workflow, and share at least one integer value. Passing the wrong one is a natural slip, not an exotic misuse.
Python enums are not strictly typed at runtime. Any IntEnum-compatible value crosses the CFFI boundary unvalidated, so the C library has no way to detect the mismatch.
Suggested fix (Python binding)
A type annotation plus a one-line runtime guard in ai_device.py would convert this silent data error into an immediate, readable exception:
def t_in(self, channel: int, scale: TempScale, flags: TInFlag) -> float:
if not isinstance(scale, TempScale):
raise TypeError(
f"scale must be a TempScale member (CELSIUS, FAHRENHEIT, KELVIN, or VOLTS), "
f"got {type(scale).__name__!r}"
)
# ... existing implementation
The same pattern could be applied to the flags argument and to similar methods (t_in_scan, etc.) for consistency, but scale on t_in is the highest-value fix given the integer collision with TcType.
I'm happy to open a PR with this change if that would be useful — the diff is small.
Package:
uldaq(PyPI, v1.2.3)Method:
AiDevice.t_in(channel, scale, flags)What happened
I accidentally passed a
TcTypemember as thescaleargument instead of aTempScalemember:The call succeeded without error.
TcType.Khas integer value2;TempScale.FAHRENHEITalso has integer value2. The C library received a valid integer and interpreted it as a request for Fahrenheit readings. My code then labelled those readings as Celsius.Observed symptom: a thermocouple at ~22 °C read 71.6 — exactly
(22 × 9/5) + 32, i.e. the correct Fahrenheit value. The bug was live in a lab data-acquisition system for several weeks because we had no thermocouples wired to live hardware during that period.Why this is an easy mistake to make
TcTypeandTempScaleappear together in typical usage: you callset_chan_tc_type(channel, TcType.K)to configure the channel, thent_in(channel, TempScale.CELSIUS, TInFlag.DEFAULT)to read it. The two enums are imported from the same module, used in the same workflow, and share at least one integer value. Passing the wrong one is a natural slip, not an exotic misuse.Python enums are not strictly typed at runtime. Any
IntEnum-compatible value crosses the CFFI boundary unvalidated, so the C library has no way to detect the mismatch.Suggested fix (Python binding)
A type annotation plus a one-line runtime guard in
ai_device.pywould convert this silent data error into an immediate, readable exception:The same pattern could be applied to the
flagsargument and to similar methods (t_in_scan, etc.) for consistency, butscaleont_inis the highest-value fix given the integer collision withTcType.I'm happy to open a PR with this change if that would be useful — the diff is small.