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

Custom exit function #590

Merged
merged 2 commits into from
Jul 22, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Type Checker
run: |
mypy ptpython
ruff .
ruff check .
ruff format --check .
- name: Run Tests
run: |
Expand Down
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
Loading