Open sockduct opened 3 weeks ago
The other loop you probably meant is set by this:
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
But adding these at config.py doesn't make this work, nor that use of WindowsSelectorEventLoop
is recommended neither, as it limit the functionality of asyncio.
OK - thanks for the feedback. Any suggestions on approaching debugging this to see what's missing? Maybe I could at least document next steps???
I tried to figure out the internals but no luck so far, I'd need more time to look thru their source code, wish I had more time!
So far it seems to be already using asyncio internally:
# run_ptpython.py
def run() -> None:
a = create_parser().parse_args()
...
# When a file has been given, run that, otherwise start the shell.
if a.args and not a.interactive:
...
# Run interactive shell.
else:
enable_deprecation_warnings()
# Apply config file
def configure(repl: PythonRepl) -> None:
...
embed_result = embed( # type: ignore
vi_mode=a.vi,
history_filename=history_file,
configure=configure,
locals=__main__.__dict__,
globals=__main__.__dict__,
startup_paths=startup_paths,
title="Python REPL (ptpython)",
return_asyncio_coroutine=a.asyncio,
)
if a.asyncio:
print("Starting ptpython asyncio REPL")
print('Use "await" directly instead of "asyncio.run()".')
asyncio.run(embed_result)
Yet can't debug further around line asyncio.run()
, I'd try Pycharm later when I have time. I can only use vscode in company and it's so annoying to debug thru the builtin modules.
But so far feels like this section seems problematic:
async def run_and_show_expression_async(self, text: str) -> Any:
loop = asyncio.get_running_loop()
system_exit: SystemExit | None = None
try:
try:
# Create `eval` task. Ensure that control-c will cancel this
# task.
async def eval() -> Any:
nonlocal system_exit
try:
return await self.eval_async(text)
except SystemExit as e:
# Don't propagate SystemExit in `create_task()`. That
# will kill the event loop. We want to handle it
# gracefully.
system_exit = e
task = asyncio.create_task(eval())
loop.add_signal_handler(signal.SIGINT, lambda *_: task.cancel())
# ^^^^^ here!
...
...as adding singal handler won't work on windows, potential hindsight of ptpython devs.
import signal
import asyncio
async def main():
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, lambda: print("SIGINT"))
await asyncio.sleep(10)
asyncio.run(main())
Exception has occurred: NotImplementedError
exception: no description
File "...\asyncio_task_count.py", line 7, in main
loop.add_signal_handler(signal.SIGINT, lambda: print("SIGINT"))
File ...\asyncio_task_count.py", line 12, in <module>
asyncio.run(main())
NotImplementedError:
Yeah I think speculation was right, so this section tries to handle ctrl+c but since it's not implemented in ProactorEventLoop
it's failing. Windows triggers on KeyboardInterrupt
instead as there's no signal.
(Line 183, 206)
However as linked on TL;DR section of this SO answer it's almost impossibile to catch KeyboardInterrupt
without singal handler by asyncio.Task side.
Looking at how asyncio/__main__.py
handles the same work flawlessly - depending on structure of ptpython this might be plain impossible without overhaul, will need to look into it.
TL;DR: asyncio's REPL runs loop from non-async context, thus can resume loop after handling. Input handling is done by other thread, so this can be done.
But in ptpython seems like input handling is tied within async context. Would put some guard here and test if it help
I have Windows 11 and Python 3.12.3 and wish to experiment with the asyncio REPL. It works fine with the built-in REPL:
However, I cannot get it to work with ptpython:
Note it also doesn't work with Python 3.8.10:
Is it possible to get this working on Windows? I see on Stack Overflow, in some cases the event loop can be changed to support various use cases. Is there a configurable option to get this to work?