Self-subscription is broken on asyncio device servers
Starting with pytango 9.5.0 subscription from a device server into one of its own attributes is broken when the server runs in asyncio green mode. While the act of subscribing yields a valid Subscription ID, no actual events flow, including the initial event that would present the current value. The issue doesn't exist with device servers in Synchronous green mode. No other green modes have been tested. No tests have been made trying to subscribe to a different device. The following code (I'm sure there are ways to further simplify it) reproduces the problem: ```python from multiprocessing import Pool import tango from tango.test_context import DeviceTestContext from tango.server import Device, GreenMode, attribute, command class MySyncDevice(Device): def init_device(self): super().init_device() self.value = 0 self.set_change_event("myAttr", True) self.this = None self.sub_id = None def delete_device(self): if self.this is not None: self.this.unsubscribe_event(self.sub_id) self.this = None self.sub_id = None @attribute(abs_change=1) def myAttr(self) -> int: return self.value @myAttr.write def myAttr(self, value: int) -> None: self.value = value self.push_change_event("myAttr", self.value) @command def SelfSub(self) -> None: self.this = tango.get_device_proxy(f"tango://127.0.0.1:{SYNC_DS_PORT}/{SYNC_DEVICE_NAME}#dbase=no") self.sub_id = self.this.subscribe_event("myAttr", tango.EventType.CHANGE_EVENT, tango.utils.EventCallback()) self.info_stream("Subscriptoin ID is %s", self.sub_id) class MyAsyncDevice(Device): green_mode = GreenMode.Asyncio async def init_device(self): await super().init_device() self.value = 0 self.set_change_event("myAttr", True) self.this = None self.sub_id = None async def delete_device(self): if self.this is not None: await self.this.unsubscribe_event(self.sub_id) self.this = None self.sub_id = None @attribute(abs_change=1) def myAttr(self) -> int: return self.value @myAttr.write def myAttr(self, value: int) -> None: self.value = value self.push_change_event("myAttr", self.value) @command async def SelfSub(self) -> None: self.this = await tango.get_device_proxy(f"tango://127.0.0.1:{ASYNC_DS_PORT}/{ASYNC_DEVICE_NAME}#dbase=no", green_mode=GreenMode.Asyncio) self.sub_id = await self.this.subscribe_event("myAttr", tango.EventType.CHANGE_EVENT, tango.utils.EventCallback()) self.info_stream("Subscriptoin ID is %s", self.sub_id) SYNC_DEVICE_NAME = "test/nodb/mysyncdevice" SYNC_DS_PORT = 8888 ASYNC_DEVICE_NAME = "test/nodb/myasyncdevice" ASYNC_DS_PORT = 8889 DEVICE_INFOS = ( (MySyncDevice, SYNC_DEVICE_NAME, SYNC_DS_PORT), (MyAsyncDevice, ASYNC_DEVICE_NAME, ASYNC_DS_PORT), ) def run_example(device_info): device, device_name, port = device_info with DeviceTestContext(device, device_name=device_name, host='127.0.0.1', port=port) as proxy: proxy.SelfSub() proxy.myAttr = 13 if __name__ == "__main__": print(tango.utils.info()) with Pool(processes=len(DEVICE_INFOS)) as pool: pool.map(run_example, DEVICE_INFOS) ``` Following are the results from running this on different versions of pytango. Note that for convenience I also switched between different versions of python to use existing pytango/numpy binary wheels. python 3.10 + pytango 9.4.2 + numpy 1.26: works as intended ``` PyTango 9.4.2 (9, 4, 2) PyTango compiled with: Python : 3.10.12 Numpy : 1.21.6 Tango : 9.4.2 Boost : 1.82.0 PyTango runtime is: Python : 3.10.15 Numpy : 1.26.4 Tango : 9.4.2 ... 2026-02-18 10:45:34.947684 TEST/NODB/MYSYNCDEVICE MYATTR#DBASE=NO CHANGE [ATTR_VALID] 0.0 2026-02-18 10:45:34.951010 TEST/NODB/MYASYNCDEVICE MYATTR#DBASE=NO CHANGE [ATTR_VALID] 0.0 2026-02-18T10:45:34,968776+0800 INFO (test.py:34) test/nodb/mysyncdevice Subscriptoin ID is 1 2026-02-18 10:45:34.969519 TEST/NODB/MYSYNCDEVICE MYATTR#DBASE=NO CHANGE [ATTR_VALID] 13.0 2026-02-18T10:45:34,971172+0800 INFO (test.py:67) test/nodb/myasyncdevice Subscriptoin ID is 1 2026-02-18 10:45:34.972530 TEST/NODB/MYASYNCDEVICE MYATTR#DBASE=NO CHANGE [ATTR_VALID] 13.0 ``` python 3.10 + pytango 9.5.0 + numpy 1.26: already shows the problem ``` PyTango 9.5.0 (9, 5, 0) PyTango compiled with: Python : 3.10.13 Numpy : 1.21.6 Tango : 9.5.0 Boost : 1.82.0 PyTango runtime is: Python : 3.10.15 Numpy : 1.26.4 Tango : 9.5.0 ... Ready to accept request Ready to accept request 2026-02-18 10:46:42.639906 TEST/NODB/MYSYNCDEVICE MYATTR#DBASE=NO CHANGE [ATTR_VALID] 0 2026-02-18T10:46:42,657044+0800 INFO (test.py:34) test/nodb/mysyncdevice Subscriptoin ID is 1 2026-02-18T10:46:42,657688+0800 INFO (test.py:67) test/nodb/myasyncdevice Subscriptoin ID is 1 2026-02-18 10:46:42.657689 TEST/NODB/MYSYNCDEVICE MYATTR#DBASE=NO CHANGE [ATTR_VALID] 13 ``` python 3.13 + pytango 10.1.3 + numpy 2.2.6 for bleeding edge: still doesn't work ``` PyTango 10.1.3 (10, 1, 3) PyTango compiled with: Python : 3.13.12 Numpy : 2.4.2 Tango : 10.1.2 pybind11 : 3.0.1 PyTango runtime is: Python : 3.13.7 Numpy : 2.2.6 Tango : 10.1.2 PyTango running on: uname_result(system='Linux', node='DEP66339.uniwa.uwa.edu.au', release='6.17.0-8-generic', version='#8-Ubuntu SMP PREEMPT_DYNAMIC Fri Nov 14 21:44:46 UTC 2025', machine='x86_64') Ready to accept request Ready to accept request 2026-02-18 10:42:28.311743 TEST/NODB/MYSYNCDEVICE MYATTR CHANGE SUBSUCCESS [ATTR_VALID] 0 2026-02-18T10:42:28,313580+0800 INFO (test.py:34) test/nodb/mysyncdevice Subscriptoin ID is 1 2026-02-18 10:42:28.314254 TEST/NODB/MYSYNCDEVICE MYATTR CHANGE UPDATE [ATTR_VALID] 13 2026-02-18T10:42:28,316007+0800 INFO (test.py:67) test/nodb/myasyncdevice Subscriptoin ID is 1 ```
issue