summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test.yaml2
-rw-r--r--CHANGELOG20
-rw-r--r--ptpython/completer.py30
-rw-r--r--ptpython/python_input.py1
-rw-r--r--ptpython/repl.py48
-rw-r--r--setup.py2
6 files changed, 90 insertions, 13 deletions
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 9a50f3b..c62bdc3 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -27,7 +27,7 @@ jobs:
- name: Type Checker
run: |
mypy ptpython
- ruff .
+ ruff check .
ruff format --check .
- name: Run Tests
run: |
diff --git a/CHANGELOG b/CHANGELOG
index 6f2bbb9..bef7d07 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,26 @@
CHANGELOG
=========
+3.0.29: 2024-07-22
+------------------
+
+Fixes:
+- Further improve performance of dictionary completions.
+
+
+3.0.28: 2024-07-22
+------------------
+
+New features:
+- Custom 'exit' function to return from REPL that
+ * doesn't terminate `sys.stdin` when `exit` is called (important for
+ `embed()`).
+ * doesn't require to be called with parentheses.
+
+Fixes:
+- Clean up signatures on control-c.
+
+
3.0.27: 2024-05-27
------------------
diff --git a/ptpython/completer.py b/ptpython/completer.py
index 264918e..e8bab28 100644
--- a/ptpython/completer.py
+++ b/ptpython/completer.py
@@ -476,20 +476,34 @@ class DictionaryCompleter(Completer):
Complete dictionary keys.
"""
- def meta_repr(value: object) -> Callable[[], str]:
+ def meta_repr(obj: object, key: object) -> Callable[[], str]:
"Abbreviate meta text, make sure it fits on one line."
+ cached_result: str | None = None
# We return a function, so that it gets computed when it's needed.
# When there are many completions, that improves the performance
# quite a bit (for the multi-column completion menu, we only need
# to display one meta text).
+ # Note that we also do the lookup itself in here (`obj[key]`),
+ # because this part can also be slow for some mapping
+ # implementations.
def get_value_repr() -> str:
- text = self._do_repr(value)
+ nonlocal cached_result
+ if cached_result is not None:
+ return cached_result
+
+ try:
+ value = obj[key] # type: ignore
+
+ text = self._do_repr(value)
+ except BaseException:
+ return "-"
# Take first line, if multiple lines.
if "\n" in text:
text = text.split("\n", 1)[0] + "..."
+ cached_result = text
return text
return get_value_repr
@@ -504,24 +518,24 @@ class DictionaryCompleter(Completer):
# If this object is a dictionary, complete the keys.
if isinstance(result, (dict, collections_abc.Mapping)):
# Try to evaluate the key.
- key_obj = key
+ key_obj_str = str(key)
for k in [key, key + '"', key + "'"]:
try:
- key_obj = ast.literal_eval(k)
+ key_obj_str = str(ast.literal_eval(k))
except (SyntaxError, ValueError):
continue
else:
break
- for k, v in result.items():
- if str(k).startswith(str(key_obj)):
+ for k in result:
+ if str(k).startswith(key_obj_str):
try:
k_repr = self._do_repr(k)
yield Completion(
k_repr + "]",
-len(key),
display=f"[{k_repr}]",
- display_meta=meta_repr(v),
+ display_meta=meta_repr(result, k),
)
except ReprFailedError:
pass
@@ -537,7 +551,7 @@ class DictionaryCompleter(Completer):
k_repr + "]",
-len(key),
display=f"[{k_repr}]",
- display_meta=meta_repr(result[k]),
+ display_meta=meta_repr(result, k),
)
except KeyError:
# `result[k]` lookup failed. Trying to complete
diff --git a/ptpython/python_input.py b/ptpython/python_input.py
index 975d3d9..b177364 100644
--- a/ptpython/python_input.py
+++ b/ptpython/python_input.py
@@ -1116,4 +1116,5 @@ class PythonInput:
return result
except KeyboardInterrupt:
# Abort - try again.
+ self.signatures = []
self.default_buffer.document = Document()
diff --git a/ptpython/repl.py b/ptpython/repl.py
index ea2d84f..6b60018 100644
--- a/ptpython/repl.py
+++ b/ptpython/repl.py
@@ -20,7 +20,7 @@ import types
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
@@ -40,7 +40,15 @@ try:
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:
@@ -91,9 +99,16 @@ class PythonRepl(PythonInput):
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)
@@ -155,7 +170,10 @@ class PythonRepl(PythonInput):
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()
@@ -383,6 +401,7 @@ class PythonRepl(PythonInput):
return self
globals["get_ptpython"] = get_ptpython
+ globals["exit"] = exit()
def _remove_from_namespace(self) -> None:
"""
@@ -459,6 +478,29 @@ def run_config(repl: PythonInput, config_file: str | None = None) -> 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,
diff --git a/setup.py b/setup.py
index 84f18be..aa10176 100644
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@ with open(os.path.join(os.path.dirname(__file__), "README.rst")) as f:
setup(
name="ptpython",
author="Jonathan Slenders",
- version="3.0.27",
+ version="3.0.29",
url="https://github.com/prompt-toolkit/ptpython",
description="Python REPL build on top of prompt_toolkit",
long_description=long_description,