Loading
Raise when a device is used before DeviceImpl.__init__
TL;DR
- Using a device in its
__init__before the baseDeviceImpl.__init__has run now raisesRuntimeError: Tango device used before DeviceImpl.__init__() was called: call the base class __init__ first. Previously the call ran on unconstructed memory and returned a wrong value, raised an unrelated error (MemoryError,UnicodeDecodeError,ValueError) or segfaulted. - Applies to every low-level device class and to the high-level
tango.server.Device. - The
repr()of such a device shows it as<Class>(unknown)instead of raising.
Closes #792 (closed)
Key Facts
- Root cause: when an instance has no C++ object yet, pybind11's generic caster lazily allocates one with the type's
operator_newand passes that raw, unconstructed memory to the bound function as the device. pybind11 only checks that the base__init__was called after the subclass__init__returns. - The fix replaces
operator_newin pybind11's per-type info (py::detail::get_type_info(...)->operator_new) with a function that throws, forDeviceImplandDevice_2Impl..Device_6Impl. pybind11 reads this field only for that lazy allocation, so there is no cost for constructed devices. It relies on apybind11::detailfield, in the same wayfix_dynamic_attr_deallocalready does. Review hotspot. - The check is on the device argument, not on individual methods. Bound methods, Python helpers that call into them (e.g.
get_device_properties(), which callsget_name()), and bound functions taking a device argument (e.g.AutoTangoMonitor(dev)) all raise the same error. - The allocation that used to leak in this situation is no longer made, so the instance stays empty. A device that catches the error and then calls the base
__init__starts normally (covered by a test). DServeris not covered: it has no Python constructor, so Python cannot create an instance without a C++ object.
Caveats
- This does not make
get_device_properties()before the base__init__work: such code now gets theRuntimeErrorinstead ofMemoryError. Restoring the 10.3.1 behaviour for classes without device properties is handled separately in !1087 (merged) (#791 (closed)).