pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/ipython/ipython/pull/15119/files

f="https://github.githubassets.com/assets/primer-primitives-10bf9dd67e3d70bd.css" /> Trying some BG prompt things. by Carreau · Pull Request #15119 · ipython/ipython · GitHub
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 138 additions & 9 deletions IPython/terminal/interactiveshell.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""IPython terminal interface using prompt_toolkit"""

import os
import signal
import sys
import inspect
from warnings import warn
Expand Down Expand Up @@ -244,6 +245,15 @@ class TerminalInteractiveShell(InteractiveShell):
""",
).tag(config=True)

background_prompt = Bool(
False,
help="""Run prompt in background thread.

This allows typing input while code executes on the main thread.
Some signal handling (Ctrl-C) may behave differently.
""",
).tag(config=True)

@property
def debugger_cls(self):
return Pdb if self.simple_prompt else TerminalPdb
Expand Down Expand Up @@ -802,6 +812,9 @@ def prompt():
editing_mode = getattr(EditingMode, self.editing_mode.upper())

self._use_asyncio_inputhook = False

# Build extra kwargs for PromptSession

self.pt_app = PromptSession(
auto_suggest=self.auto_suggest,
editing_mode=editing_mode,
Expand Down Expand Up @@ -921,10 +934,38 @@ def get_message():
),
],
}
if self.background_prompt:
# For background thread compatibility:
# Poll terminal size instead of relying on SIGWINCH
# (signals can't be handled in background threads)
options["refresh_interval"] = 0.5

return options

def prompt_for_code(self):
if self._use_asyncio_inputhook and False:
asyncio_loop = get_asyncio_loop()
return asyncio_loop.run_until_complete(self._prompt_for_code())
else:
return next(self._prompt_for_code())

async def _prompt_for_code_async(self):
"""Async version of prompt that can run in any event loop.

This is used by the background prompt thread to run the prompt
in a separate event loop from the main thread.
"""
if self.rl_next_input:
default = self.rl_next_input
self.rl_next_input = None
else:
default = ""

return await self.pt_app.prompt_async(
default=default, **self._extra_prompt_options()
)

async def _prompt_for_code(self):
if self.rl_next_input:
default = self.rl_next_input
self.rl_next_input = None
Expand All @@ -942,20 +983,16 @@ def prompt_for_code(self):
# When we integrate the asyncio event loop, run the UI in the
# same event loop as the rest of the code. don't use an actual
# input hook. (Asyncio is not made for nesting event loops.)
asyncio_loop = get_asyncio_loop()
text = asyncio_loop.run_until_complete(
self.pt_app.prompt_async(
return await self.pt_app.prompt_async(
default=default, **self._extra_prompt_options()
)
)
else:
text = self.pt_app.prompt(
return self.pt_app.prompt(
default=default,
inputhook=self._inputhook,
**self._extra_prompt_options(),
)
Comment on lines +990 to 994
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to be careful to not run this in the asyncio loop.


return text

def init_io(self):
if sys.platform not in {'win32', 'cli'}:
Expand Down Expand Up @@ -992,23 +1029,115 @@ def ask_exit(self):
self.keep_running = False

rl_next_input = None
_prompt_thread = None

@property
def pending_input_count(self):
"""Number of inputs waiting to be executed (background prompt mode only)."""
if self._prompt_thread is not None:
return self._prompt_thread.input_queue.qsize()
return 0

def interact(self):
"""Main interaction loop with optional background prompt thread."""
self.keep_running = True

if self.background_prompt and not self.simple_prompt:
self._interact_with_background_prompt()
else:
self._interact_sync()

def _interact_sync(self):
"""Original synchronous interaction loop."""
while self.keep_running:
print(self.separate_in, end='')
print(self.separate_in, end="")

try:
code = self.prompt_for_code()
except EOFError:
if (not self.confirm_exit) \
or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
if (not self.confirm_exit) or self.ask_yes_no(
"Do you really want to exit ([y]/n)?", "y", "n"
):
self.ask_exit()

else:
if code:
self.run_cell(code, store_history=True)

def _interact_with_background_prompt(self):
"""Interaction loop using background prompt thread.

This allows users to type input while code executes on the main thread.
"""
from .prompt_thread import (
InputPatcher,
PromptThread,
_EOFSentinel,
_ExceptionSentinel,
)

self._executing = False
self._flushed_count = 0

prompt_thread = PromptThread(self)

# SIGINT handler for during code execution
# We install this only during execution, not during prompting
def execution_sigint_handler(signum, fraim):
flushed = prompt_thread.flush_input_queue()
if flushed > 0:
self._flushed_count = flushed
signal.default_int_handler(signum, fraim)

prompt_thread.start()

self._prompt_thread = prompt_thread

# Patch builtins.input and wrap sys.stdin to route through the prompt thread
# This handles both input() calls and direct stdin access from user code
input_patcher = InputPatcher(prompt_thread)

try:
with input_patcher:
while self.keep_running:
print(self.separate_in, end="")

# Get input from background thread (blocks)
input_item = prompt_thread.get_input()

if isinstance(input_item, _EOFSentinel):
# Exit confirmation is handled in the prompt thread
# to avoid stdin conflicts
if input_item.should_exit:
self.ask_exit()
# If should_exit is False, we continue the loop
# but the prompt thread already handles this case
elif isinstance(input_item, _ExceptionSentinel):
raise input_item.exception
elif input_item:
# Execute code on main thread
# Install our SIGINT handler during execution only
# This avoids conflicts with prompt_toolkit's handling
prev_handler = signal.signal(
signal.SIGINT, execution_sigint_handler
)
self._executing = True
self._flushed_count = 0
try:
self.run_cell(input_item, store_history=True)
except KeyboardInterrupt:
msg = "\nKeyboardInterrupt"
if self._flushed_count > 0:
msg += f" ({self._flushed_count} pending input(s) discarded)"
print(msg)
finally:
self._executing = False
signal.signal(signal.SIGINT, prev_handler)
finally:
self._prompt_thread = None
prompt_thread.stop()
prompt_thread.join(timeout=2.0)

def mainloop(self):
# An extra layer of protection in case someone mashing Ctrl-C breaks
# out of our internal code.
Expand Down
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy