DiamondLightSource / pythonSoftIOC

Embed an EPICS IOC in a Python process
Apache License 2.0
31 stars 9 forks source link

Improve string representation of RecordWrapper instances #129

Closed AlexanderWells-diamond closed 1 year ago

AlexanderWells-diamond commented 1 year ago

Discussed in https://github.com/dls-controls/pythonSoftIOC/discussions/128

Originally posted by **jdmax** May 26, 2023 I'm trying to create an IOC dynamically chosen from set of device scripts. This means I'm trying to do builder calls in a module called by my main function using importlib. I can call the module to create the PVs and read the created PVs from the main function. But only before I call builder.LoadDatabase(), after which all the created PV instances are replaced with 'None.' I assume I need to pass the builder into the called module somehow, but I could use some advice as to how. I include a barebones example below. Thanks so much for any advice you might have. ```python from softioc import softioc, builder, asyncio_dispatcher import importlib import asyncio async def main(): dispatcher = asyncio_dispatcher.AsyncioDispatcher() builder.SetDeviceName('device_name') d = DeviceIOC() print(d.device.pvs['Test']) # Prints the name of the PV builder.LoadDatabase() print(d.device.pvs['Test']) # Prints "None" softioc.iocInit(dispatcher) async def loop(): while True: await d.loop() dispatcher(loop) softioc.interactive_ioc(globals()) class DeviceIOC(): def __init__(self): self.module = importlib.import_module('test_module') self.device = self.module.Device() async def loop(self): await asyncio.sleep(1) if __name__ == "__main__": asyncio.run(main()) ``` This is an example module file it will call, 'test_module': ```python from softioc import builder class Device(): def __init__(self): self.pvs = {} self.pvs['Test'] = builder.aIn('Test') ```

After discussing this issue with @Araneidae, it was decided that the str implementation could be improved. In its current form trying to print the record's name will print None after builder.LoadDatabase() is called.

We can instead use the value of self.__device._name, which is not lost after the database is loaded, resulting in consistent behaviour (and a more user-friendly experience)