Eel: Asynchronous calls with asyncio and aiohttp

Created on 3 Jun 2019  路  10Comments  路  Source: ChrisKnott/Eel

Hello,

I am very sorry for creating so many issues, but I am having some more trouble with my program that uses eel.

I go into detail here, but the main issue is that I am not able to run an eel exposed function once I have asynchronous asyncio tasks running.

As mentioned, I have tried following your example in the readme but haven't been able to get it to work.

Thanks.

Most helpful comment

https://github.com/namuyan/async-Eel
I replace bottle to aiohttp and gevent to asyncio.
I will use for my project, How about this?

All 10 comments

I think probably asyncio doesn't work well with gevent, which is what is used by bottle (which provides the webserver framework for Eel).

Is it possible for you to reengineer your app to use gevent for async?

@ChrisKnott I'm not sure if gevent will work. I'm doing stuff such as

async def get_output():
    p = await asyncio.create_subprocess_exec('python', 'sub.py', 'watch', 'ls', '-u',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.STDOUT,
    )  # run sub.py
    while True:
        line = await Unbuffered(p.stdout).readline()
        if not line:
            break
        print(line.decode("utf-8").replace("\r\n", ""))  # print command line outputs for sub.py
    await p.wait()

And I'm not sure if there's a gevent equivalent for such a thing. The synchronous way is to use subprocess.Popen, so I don't know if that can be monkey patched or something? I don't have a good understanding of monkey patching so it's hard to say.

Are you sure there is no way to get it to work with asyncio?

EDIT: I may be wrong actually, although I would still much prefer to find a way to make asyncio work if at all possible.

You could do it, but your caller function would need to run the coroutine

def call_me():
    asyncio.run(get_output())

@ahopkins

Thanks, but unfortunately it doesn't work.

C:\Users\x\AppData\Local\Programs\Python\Python37-32\python.exe C:/Users/x/Documents/Coding/testing/interface/gui2.py
Traceback (most recent call last):
  File "src\gevent\greenlet.py", line 766, in gevent._greenlet.Greenlet.run
  File "C:\Users\x\AppData\Local\Programs\Python\Python37-32\lib\site-packages\eel\__init__.py", line 207, in _process_message
    return_val = _exposed_functions[message['name']](*message['args'])
  File "C:/Users/x/Documents/Coding/testing/interface/gui2.py", line 111, in call_me
    asyncio.run(get_output())
  File "C:\Users\x\AppData\Local\Programs\Python\Python37-32\lib\asyncio\runners.py", line 34, in run
    "asyncio.run() cannot be called from a running event loop")
RuntimeError: asyncio.run() cannot be called from a running event loop
2019-06-05T18:40:29Z <Greenlet at 0x41dcf60: _process_message({'call': 6.222048676839939, 'name': 'call_me, <geventwebsocket.websocket.WebSocket object at 0x0)> failed with RuntimeError

C:\Users\x\AppData\Local\Programs\Python\Python37-32\lib\site-packages\gevent\libuv\loop.py:193: RuntimeWarning: coroutine 'get_output' was never awaited
  super(loop, self)._run_callbacks()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

I believe the asyncio.run() cannot be called from a running event loop error is due to the fact that you can't run it when there is already another asyncio event loop running.

I believe the asyncio.run() cannot be called from a running event loop error is due to the fact that you can't run it when there is already another asyncio event loop running.

True.

But you can do this:

loop = asyncio.get_event_loop()  # Or, if in 3.7, use get_running_loop()
loop.create_task(get_output())

get_event_loop will fetch the current running loop (or create if there is not one). If you are on Python 3.7, get_running_loop may be slightly better.

@ahopkins I tried it, but it's not working. call_me runs and 1 is printed, but get_output does not run and so 2 is not printed. There aren't any errors, just nothing happens.

main.py

@eel.expose
def call_me():
        print(1)
    loop = asyncio.get_running_loop()
    loop.create_task(get_output())


async def get_output():
    print(2)


async def start_eel():
    options = {
        'mode': 'custom',
        'args': ['node_modules/electron/dist/electron.exe', '.']
    }

    eel.init('web')
    eel.start('html/index.html', options=options)


async def main():
    asyncio.create_task(start_eel())


if __name__ == "__main__":
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
    asyncio.run(main())

index.js

function start_program_js() { // this is run when the user clicks the run button
    eel.call_me();
}

Why are you running start_eel as a task? I think this is a part of your problem.

import asyncio


def call_me():
    print(1)
    loop = asyncio.get_running_loop()
    loop.create_task(get_output())


async def get_output():
    print(2)


async def mock_eel():
    print("Mocking eel")
    while True:
        await asyncio.sleep(2)
        call_me()


async def main():
    print("Main")
    await mock_eel()


if __name__ == "__main__":
    asyncio.run(main())

The above will work as expected. The below change will cause the behavior you are seeing.

asyncio.create_task(mock_eel()

@ahopkins I'm confused as to why you added an additional function? Now which function is being exposed to eel?

Nothing new. Rather than running this through eel, I am using mock_eel() to mock the behavior of calling call_me from inside an existing loop. You can run this snippet by itself in a plain vanilla Python environment to see the impact and different between:

async def main():
    print("Main")
    await mock_eel()

and

async def main():
    print("Main")
    asyncio.create_task(mock_eel()

Fire it up and you will see the difference.

https://github.com/namuyan/async-Eel
I replace bottle to aiohttp and gevent to asyncio.
I will use for my project, How about this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

abdurahman-shiine picture abdurahman-shiine  路  3Comments

mattiaornaghi picture mattiaornaghi  路  5Comments

Jack-Stoller picture Jack-Stoller  路  6Comments

logxseven picture logxseven  路  6Comments

sameera-madushan picture sameera-madushan  路  3Comments