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

Infinite exception loop in asyncio coroutine mode on exit() or quit() #541

Open
brownan opened this issue Jun 27, 2023 · 1 comment
Open

Comments

@brownan
Copy link

brownan commented Jun 27, 2023

If you run the async example from here: https://github.com/prompt-toolkit/ptpython/blob/master/examples/asyncio-python-embed.py

and execute a exit() or quit() statement, the prompt goes into an infinite loop of errors that look like this:

Traceback (most recent call last):
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 955, in run
    loop = asyncio.get_event_loop()
  File "/usr/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-178'.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/venv/lib/python3.9/site-packages/ptpython/repl.py", line 202, in run_async
    text = await loop.run_in_executor(None, self.read)
  File "/usr/lib/python3.9/concurrent/futures/thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/ptpython/python_input.py", line 1095, in read
    result = self.app.run(pre_run=pre_run, in_thread=True)
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 943, in run
    raise exception
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 929, in run_in_thread
    result = self.run(
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 958, in run
    return asyncio.run(coro)
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 875, in run_async
    return await _run_async(f)
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/application/application.py", line 728, in _run_async
    with self.input.raw_mode(), self.input.attach(
  File "/tmp/venv/lib/python3.9/site-packages/prompt_toolkit/input/vt100.py", line 134, in raw_mode
    return raw_mode(self.stdin.fileno())
ValueError: I/O operation on closed file

I have to Ctrl-Z to pause the process or kill it from another window, since Ctrl-C is not effective at this point.

The thread ID in the exception message increases, so I gather it's continuously spawning threads which each crash since stdin has been closed by exit()

This is on Ubuntu linux 20.04 with Python 3.9

@brownan brownan changed the title Infitine exception loop in asyncio coroutine mode on exit() or quit() Infinite exception loop in asyncio coroutine mode on exit() or quit() Jun 27, 2023
@brownan
Copy link
Author

brownan commented Jun 27, 2023

Did some digging. In the repl.py function run_and_show_expression_async() a SystemExit is handled by returning.

ptpython/ptpython/repl.py

Lines 156 to 176 in 91d2c35

async def run_and_show_expression_async(self, text: str):
loop = asyncio.get_event_loop()
try:
result = await self.eval_async(text)
except KeyboardInterrupt: # KeyboardInterrupt doesn't inherit from Exception.
raise
except SystemExit:
return
except BaseException as e:
self._handle_exception(e)
else:
# Print.
if result is not None:
await loop.run_in_executor(None, lambda: self.show_result(result))
# Loop.
self.current_statement_index += 1
self.signatures = []
# Return the result for future consumers.
return result

This swallows the exception and causes the calling code in run_async() to proceed to its next iteration. Calling in to read a new line of input gets the error about I/O operation on closed file.

The sync version, however, re-raises a SystemExit:

ptpython/ptpython/repl.py

Lines 96 to 115 in 91d2c35

def run_and_show_expression(self, expression: str) -> None:
try:
# Eval.
try:
result = self.eval(expression)
except KeyboardInterrupt:
# KeyboardInterrupt doesn't inherit from Exception.
raise
except SystemExit:
raise
except BaseException as e:
self._handle_exception(e)
else:
# Print.
if result is not None:
self.show_result(result)
# Loop.
self.current_statement_index += 1
self.signatures = []

Is there any reason for the async version to not also do the same?

These two commits are related:
742c6d7
78c5a0d

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

1 participant