Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RunTime Error 'Application.run_async' was never awaited #80

Closed
SteffenBrinckmann opened this issue Oct 19, 2020 · 9 comments
Closed

RunTime Error 'Application.run_async' was never awaited #80

SteffenBrinckmann opened this issue Oct 19, 2020 · 9 comments

Comments

@SteffenBrinckmann
Copy link

Hallo Questionary,
I have been using questionary for a while now, but when I started linking an additional lib, a runtime warning/error gets triggered. (Although it is a warning it leads to immediate failure)
In the traceback there is mention of the ask_unsafe function. I wonder, can one from the python.api switch between the multiple ask functions that are available in question.py?

A short test script:

#!/usr/bin/python3
import subprocess
from questionary import prompt, Separator
from datalad import api as datalad

question = [{'type': 'list', 'name': 'choice', 'message': 'Menu', 'choices': ['Red pill','Blue pill']}]
print("You have to take four pills")
for i in range(4):
  answer = prompt(question)
  name =  answer['choice'].replace(' ','_')+str(i)
  print("Create path: "+name)
  ds = datalad.create(name, cfg_proc='text2git')

The trace is

Traceback (most recent call last):
  File "short.py", line 9, in <module>
    answer = prompt(question)
  File "/usr/local/lib/python3.8/dist-packages/questionary/prompt.py", line 97, in prompt
    answer = question.unsafe_ask(patch_stdout)
  File "/usr/local/lib/python3.8/dist-packages/questionary/question.py", line 59, in unsafe_ask
    return self.application.run()
  File "/usr/local/lib/python3.8/dist-packages/prompt_toolkit/application/application.py", line 816, in run
    return loop.run_until_complete(
  File "/usr/lib/python3.8/asyncio/base_events.py", line 591, in run_until_complete
    self._check_closed()
  File "/usr/lib/python3.8/asyncio/base_events.py", line 508, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
sys:1: RuntimeWarning: coroutine 'Application.run_async' was never awaited
@kiancross
Copy link
Collaborator

Hmm maybe a similar problem to prompt-toolkit/python-prompt-toolkit#1033?

@SteffenBrinckmann
Copy link
Author

I use Ubuntu 20.04 and python 3.8.
prompt-toolkit/python-prompt-toolkit#1033, was talking about Win32.
I guess it is not similar, also there is no progress bar involved.

@layday
Copy link
Contributor

layday commented Oct 20, 2020

The crux of the issue is the same. Library A (datalad) closes the default event loop. Library B (prompt-toolkit) attempts to use the default event loop after it was closed by library A. Libraries have no business using or closing the default event loop. As a stopgap you can run asyncio.set_event_loop(asyncio.new_event_loop()) before every prompt.

@layday
Copy link
Contributor

layday commented Oct 20, 2020

prompt-toolkit attempts to account for this in https://github.com/prompt-toolkit/python-prompt-toolkit/blob/dd3e552c00ee44247144a7aa9cd0392a7206570f/prompt_toolkit/application/application.py#L806-L814 but calling get_event_loop does not raise if the default event loop is simply closed as the comment would suggest - it only raises if it's been previously set to None. It might be intentional that a closed loop isn't recreated but this should probably be reported upstream.

@SteffenBrinckmann
Copy link
Author

SteffenBrinckmann commented Oct 20, 2020

I played with different versions: prompt-toolkit 2.0.10 works perfectly.
3.0.8 does not work. This version is the one which has the PR that followed the issue prompt-toolkit/python-prompt-toolkit#1033

I'm not sure how to handle it for my own software: make a requirement that excludes the latest prompt-toolkit, or choose the stopgap mentioned before or use system-calls/parse-output which also close the event loops correctly.

@davnat
Copy link

davnat commented Oct 22, 2020

I'm not sure it's the same issue, but at least it seems related.

Pasting this in test.py

import asyncio
import questionary

async def main() -> None:
    questionary.select(
        "What do you want to do?",
        choices=[
            "Order a pizza",
            "Make a reservation",
            "Ask for opening hours",
        ],
    ).ask()

asyncio.run(main())

and running python3 -m test.py (within an activated venv with the relevant packages), yelds:

Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 185, in _run_module_as_main
    mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
  File "/usr/lib/python3.8/runpy.py", line 111, in _get_module_details
    __import__(pkg_name)
  File "/home/davnat/test.py", line 16, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.8/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/home/davnat/test.py", line 6, in main
    questionary.select(
  File "/home/davnat/.venv/lib/python3.8/site-packages/questionary/question.py", line 45, in ask
    return self.unsafe_ask(patch_stdout)
  File "/home/davnat/.venv/lib/python3.8/site-packages/questionary/question.py", line 59, in unsafe_ask
    return self.application.run()
  File "/home/davnat/.venv/lib/python3.8/site-packages/prompt_toolkit/application/application.py", line 816, in run
    return loop.run_until_complete(
  File "/usr/lib/python3.8/asyncio/base_events.py", line 592, in run_until_complete
    self._check_running()
  File "/usr/lib/python3.8/asyncio/base_events.py", line 552, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
sys:1: RuntimeWarning: coroutine 'Application.run_async' was never awaited

@layday
Copy link
Contributor

layday commented Oct 22, 2020

That's because ask will start the main loop via prompt-toolkit before asyncio.run is able to. Use ask_async if you want to invoke Questionary asynchronously:

import asyncio
import questionary

async def main() -> None:
    await questionary.select(
        "What do you want to do?",
        choices=[
            "Order a pizza",
            "Make a reservation",
            "Ask for opening hours",
        ],
    ).ask_async()

asyncio.run(main())

@davnat
Copy link

davnat commented Oct 22, 2020

Thanks for your quick response.

Unfortunately in my use case it doesn't make sense using the async API, because all questionary calls need to be synchronous.
Also, I'm not really calling questionary directly (the test above is an oversimplified example): I need to support different toolkits, so I created a wrapper around questionary. To call the async API I'd need to expose it in the wrapper, without any real need for an async API: only to make it work.

But now it's clear this is a different issue: I'm going to open it as such.

@SteffenBrinckmann
Copy link
Author

The stopgap "asyncio.set_event_loop(asyncio.new_event_loop()) before every prompt" works perfectly for me.

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

No branches or pull requests

4 participants