Skip to content
This repository has been archived by the owner on Apr 7, 2022. It is now read-only.

Async mode server: dynamic attribute read/write/is_allowed are not called by worker #173

Closed
tiagocoutinho opened this issue Oct 27, 2017 · 7 comments

Comments

@tiagocoutinho
Copy link
Contributor

In async mode (gevent, asyncio, ...) the dynamic attributes read, write and is_allowed methods are not called through the worker like the standard attributes

@mliszcz
Copy link

mliszcz commented Nov 23, 2019

Probably got the same issue:

sys:1: RuntimeWarning: coroutine 'AsyncioDevice.getter' was never awaited

As a workaround I needed to decorate my methods:

def run_in_executor(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        get_worker().execute(wrapper, *args, **kwargs)
    return wrapper

Howeve it would be nice to have this handled by PyTango.

@stanislaw55
Copy link

Hi
I just hit this issue. Is there any chance that it will be fixed any time soon?

@ajoubertza
Copy link
Member

Hi @stanislaw55

I wasn't planning on working on this. Would you consider helping by submitting a pull request?

@stanislaw55
Copy link

Hi @ajoubertza
Actually, I would like to make time for contribution but the fix proposed by @mliszcz did not worked for me for some reason. I did not investigated in depth since docs mentioned experimental state of server GreenMode. However, now I will come back to this issue and try to generate more detailed bug report.

My setup:
pytango 9.2.5
Libtango 9.2.5
Python 3.6.8

I did not tested this fix with (Py)Tango 9.3.x because of lack of such libraries compiled. However, I'll try to test it and maybe latest version from develop.

Stay tuned!

@stanislaw55
Copy link

Okay, that was quick one

The fix that worker for me

import functools

from tango.server import get_worker

def run_in_executor(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        get_worker().execute(fn, *args, **kwargs)
    return wrapper

(mind that now in execute call there's fn, not wrapper)

@ajoubertza
Copy link
Member

@stanislaw55 Well spotted - the fn makes more sense than a recursive call!

If you're working on a PR, I suggest starting by adding one or more failing tests at the end of https://github.com/tango-controls/pytango/blob/develop/tests/test_server.py. I see there are actually no tests for is_allowed!

You could base them on something like the one below. The typed_values fixture will repeat the test for lots of different types of attributes. The server_green_mode will repeat the test for all the different green modes.

def test_read_write_attribute(typed_values, server_green_mode):
dtype, values, expected = typed_values
class TestDevice(Device):
green_mode = server_green_mode
@attribute(dtype=dtype, max_dim_x=10,
access=AttrWriteType.READ_WRITE)
def attr(self):
return self.attr_value
@attr.write
def attr(self, value):
self.attr_value = value
with DeviceTestContext(TestDevice) as proxy:
for value in values:
proxy.attr = value
assert_close(proxy.attr, expected(value))

If you explicitly need async or await keywords in the test, you have to jump through some hoops to keep the code readable in Python versions prior to 3.5.

if server_green_mode == GreenMode.Asyncio:
code = textwrap.dedent("""\
@asyncio.coroutine
def dev_status(self):
coro = super(type(self), self).dev_status()
result = {YIELD_FROM}(coro)
{RETURN}(3*result)
""").format(**globals())
exec(code)

@stanislaw55
Copy link

Thanks for detailed instrution on test, @ajoubertza!

I'll try to make some time for PR in days to come

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants