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.
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?
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?