Skip to content

Commit

Permalink
Add custom 'exit' function to return from REPL.
Browse files Browse the repository at this point in the history
- This doesn't doesn't terminate sys.stdin when `exit` is called, whis is important for `embed()`.
- Don't require 'exit' to be called with parentheses.
  • Loading branch information
jonathanslenders committed Jul 22, 2024
1 parent f66a289 commit 0873191
Showing 1 changed file with 45 additions and 3 deletions.
48 changes: 45 additions & 3 deletions ptpython/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import warnings
from dis import COMPILER_FLAG_NAMES
from pathlib import Path
from typing import Any, Callable, ContextManager, Iterable, Sequence
from typing import Any, Callable, ContextManager, Iterable, NoReturn, Sequence

from prompt_toolkit.formatted_text import OneStyleAndTextTuple
from prompt_toolkit.patch_stdout import patch_stdout as patch_stdout_context
Expand All @@ -40,7 +40,15 @@
except ImportError:
PyCF_ALLOW_TOP_LEVEL_AWAIT = 0

__all__ = ["PythonRepl", "enable_deprecation_warnings", "run_config", "embed"]

__all__ = [
"PythonRepl",
"enable_deprecation_warnings",
"run_config",
"embed",
"exit",
"ReplExit",
]


def _get_coroutine_flag() -> int | None:
Expand Down Expand Up @@ -91,9 +99,16 @@ def run_and_show_expression(self, expression: str) -> None:
raise
except SystemExit:
raise
except ReplExit:
raise
except BaseException as e:
self._handle_exception(e)
else:
if isinstance(result, exit):
# When `exit` is evaluated without parentheses.
# Automatically trigger the `ReplExit` exception.
raise ReplExit

# Print.
if result is not None:
self._show_result(result)
Expand Down Expand Up @@ -155,7 +170,10 @@ def run(self) -> None:
continue

# Run it; display the result (or errors if applicable).
self.run_and_show_expression(text)
try:
self.run_and_show_expression(text)
except ReplExit:
return
finally:
if self.terminal_title:
clear_title()
Expand Down Expand Up @@ -383,6 +401,7 @@ def get_ptpython() -> PythonInput:
return self

globals["get_ptpython"] = get_ptpython
globals["exit"] = exit()

def _remove_from_namespace(self) -> None:
"""
Expand Down Expand Up @@ -459,6 +478,29 @@ def enter_to_continue() -> None:
enter_to_continue()


class exit:
"""
Exit the ptpython REPL.
"""

# This custom exit function ensures that the `embed` function returns from
# where we are embedded, and Python doesn't close `sys.stdin` like
# the default `exit` from `_sitebuiltins.Quitter` does.

def __call__(self) -> NoReturn:
raise ReplExit

def __repr__(self) -> str:
# (Same message as the built-in Python REPL.)
return "Use exit() or Ctrl-D (i.e. EOF) to exit"


class ReplExit(Exception):
"""
Exception raised by ptpython's exit function.
"""


def embed(
globals: dict[str, Any] | None = None,
locals: dict[str, Any] | None = None,
Expand Down

0 comments on commit 0873191

Please sign in to comment.