diff options
-rw-r--r-- | .github/workflows/test.yaml | 2 | ||||
-rw-r--r-- | CHANGELOG | 20 | ||||
-rw-r--r-- | ptpython/completer.py | 30 | ||||
-rw-r--r-- | ptpython/python_input.py | 1 | ||||
-rw-r--r-- | ptpython/repl.py | 48 | ||||
-rw-r--r-- | setup.py | 2 |
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: | @@ -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, @@ -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, |