Bug
The generic SpyMessage getter increments attr_name but does not release it when delegating to PyObject_GenericGetAttr. PyNeoDeviceEx_GetHandle/SetHandle retain new references from PyObject_GetAttrString, and the module-object helpers retain PyImport_ImportModule references. These leaks occur in ordinary repeated reads and API calls.
Reviewed commit: 45167e09de6b886cd451a4245f842c8221349711 (current master on 2026-09-15).
Source: src/object_spy_message.cpp:57; src/object_spy_message.cpp:133; src/methods.cpp:932; src/methods.cpp:794
Reproduction
import ics, sys
m = ics.SpyMessage()
name = ''.join(['Status', 'Bit', 'Field'])
getattr(m, name)
before = sys.getrefcount(name)
for _ in range(1000): getattr(m, name)
print(sys.getrefcount(name) - before)
Observed / expected
Reference count grows by approximately one per read (1,002 over the first 1,000 reads in the isolated run, including interpreter warmup). Independently, 1,000 get_device_status calls against a mock DLL increased the same device handle capsule reference count by exactly 1,000. Expected no reference-count growth after calls complete.
Suggested fix and regression coverage
Balance owned references on every path, including helper failures and SetHandle capsule creation. Remove the unnecessary attr_name INCREF or release it before generic delegation. Add stabilized reference-count regression tests for attribute reads, handle lookups, and module lookups.
Validation environment
Windows x64, CPython 3.14.5, extension rebuilt from the commit above. Hardware-dependent entry points below were exercised with a mock DLL, not a connected device. The existing 30 tests pass despite these findings.
Bug
The generic SpyMessage getter increments
attr_namebut does not release it when delegating to PyObject_GenericGetAttr. PyNeoDeviceEx_GetHandle/SetHandle retain new references from PyObject_GetAttrString, and the module-object helpers retain PyImport_ImportModule references. These leaks occur in ordinary repeated reads and API calls.Reviewed commit:
45167e09de6b886cd451a4245f842c8221349711(current master on 2026-09-15).Source: src/object_spy_message.cpp:57; src/object_spy_message.cpp:133; src/methods.cpp:932; src/methods.cpp:794
Reproduction
Observed / expected
Reference count grows by approximately one per read (1,002 over the first 1,000 reads in the isolated run, including interpreter warmup). Independently, 1,000 get_device_status calls against a mock DLL increased the same device handle capsule reference count by exactly 1,000. Expected no reference-count growth after calls complete.
Suggested fix and regression coverage
Balance owned references on every path, including helper failures and SetHandle capsule creation. Remove the unnecessary attr_name INCREF or release it before generic delegation. Add stabilized reference-count regression tests for attribute reads, handle lookups, and module lookups.
Validation environment
Windows x64, CPython 3.14.5, extension rebuilt from the commit above. Hardware-dependent entry points below were exercised with a mock DLL, not a connected device. The existing 30 tests pass despite these findings.