From b5d8c28535578eca504572c11a6ff893728ecac0 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 12:53:50 +0000 Subject: [PATCH 01/25] Show exception cause/context when printing an exception. --- ptpython/printer.py | 27 ++++++++++++++++----------- ptpython/repl.py | 4 ++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ptpython/printer.py b/ptpython/printer.py index 85bd9c88..81ea16f3 100644 --- a/ptpython/printer.py +++ b/ptpython/printer.py @@ -254,8 +254,7 @@ def _apply_soft_wrapping( columns_in_buffer += width current_line.append((style, c)) - if len(current_line) > 0: - yield current_line + yield current_line def _print_paginated_formatted_text( self, lines: Iterable[StyleAndTextTuples] @@ -323,14 +322,20 @@ def show_pager() -> None: def _format_exception_output( self, e: BaseException, highlight: bool ) -> Generator[OneStyleAndTextTuple, None, None]: - # Instead of just calling ``traceback.format_exc``, we take the - # traceback and skip the bottom calls of this framework. - t, v, tb = sys.exc_info() - - # Required for pdb.post_mortem() to work. - sys.last_type, sys.last_value, sys.last_traceback = t, v, tb - - tblist = list(traceback.extract_tb(tb)) + if e.__cause__: + yield from self._format_exception_output(e.__cause__, highlight=highlight) + yield ( + "", + "\nThe above exception was the direct cause of the following exception:\n\n", + ) + elif e.__context__: + yield from self._format_exception_output(e.__context__, highlight=highlight) + yield ( + "", + "\nDuring handling of the above exception, another exception occurred:\n\n", + ) + + tblist = list(traceback.extract_tb(e.__traceback__)) for line_nr, tb_tuple in enumerate(tblist): if tb_tuple[0] == "": @@ -340,7 +345,7 @@ def _format_exception_output( tb_list = traceback.format_list(tblist) if tb_list: tb_list.insert(0, "Traceback (most recent call last):\n") - tb_list.extend(traceback.format_exception_only(t, v)) + tb_list.extend(traceback.format_exception_only(type(e), e)) tb_str = "".join(tb_list) diff --git a/ptpython/repl.py b/ptpython/repl.py index 6b60018e..9142d909 100644 --- a/ptpython/repl.py +++ b/ptpython/repl.py @@ -378,6 +378,10 @@ def _compile_with_flags(self, code: str, mode: str) -> Any: ) def _handle_exception(self, e: BaseException) -> None: + # Required for pdb.post_mortem() to work. + t, v, tb = sys.exc_info() + sys.last_type, sys.last_value, sys.last_traceback = t, v, tb + self._get_output_printer().display_exception( e, highlight=self.enable_syntax_highlighting, From 37763164fd444771c9232ed10e1021d34b7a5d20 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 12:58:18 +0000 Subject: [PATCH 02/25] Drop Python 3.8, given it's end of life and no longer supported on GitHub CI. Also some typing fixes. --- .github/workflows/test.yaml | 6 +++--- ptpython/entry_points/run_ptpython.py | 13 ++++++------- setup.py | 5 ++--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c62bdc39..2311e02a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,12 +10,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install Dependencies diff --git a/ptpython/entry_points/run_ptpython.py b/ptpython/entry_points/run_ptpython.py index 05df9714..d083858d 100644 --- a/ptpython/entry_points/run_ptpython.py +++ b/ptpython/entry_points/run_ptpython.py @@ -30,8 +30,9 @@ import os import pathlib import sys +from importlib import metadata from textwrap import dedent -from typing import IO +from typing import Protocol import appdirs from prompt_toolkit.formatted_text import HTML @@ -39,17 +40,15 @@ from ptpython.repl import PythonRepl, embed, enable_deprecation_warnings, run_config -try: - from importlib import metadata # type: ignore -except ImportError: - import importlib_metadata as metadata # type: ignore +__all__ = ["create_parser", "get_config_and_history_file", "run"] -__all__ = ["create_parser", "get_config_and_history_file", "run"] +class _SupportsWrite(Protocol): + def write(self, s: str, /) -> object: ... class _Parser(argparse.ArgumentParser): - def print_help(self, file: IO[str] | None = None) -> None: + def print_help(self, file: _SupportsWrite | None = None) -> None: super().print_help() print( dedent( diff --git a/setup.py b/setup.py index aa101764..bd2f962a 100644 --- a/setup.py +++ b/setup.py @@ -27,22 +27,21 @@ package_data={"ptpython": ["py.typed"]}, install_requires=[ "appdirs", - "importlib_metadata;python_version<'3.8'", "jedi>=0.16.0", # Use prompt_toolkit 3.0.43, because of `OneStyleAndTextTuple` import. "prompt_toolkit>=3.0.43,<3.1.0", "pygments", ], - python_requires=">=3.7", + python_requires=">=3.8", classifiers=[ "License :: OSI Approved :: BSD License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python", ], From 04235d791b483af0ad36f578608d06bf4331f825 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 13:36:23 +0000 Subject: [PATCH 03/25] Use f-strings instead of %-style formatting. --- examples/asyncio-python-embed.py | 2 +- examples/asyncio-ssh-python-embed.py | 4 ++-- ptpython/layout.py | 12 +++++------- ptpython/printer.py | 1 - ptpython/repl.py | 2 +- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/examples/asyncio-python-embed.py b/examples/asyncio-python-embed.py index 38cc1c20..cb909731 100755 --- a/examples/asyncio-python-embed.py +++ b/examples/asyncio-python-embed.py @@ -25,7 +25,7 @@ async def print_counter() -> None: Coroutine that prints counters and saves it in a global variable. """ while True: - print("Counter: %i" % counter[0]) + print(f"Counter: {counter[0]}") counter[0] += 1 await asyncio.sleep(3) diff --git a/examples/asyncio-ssh-python-embed.py b/examples/asyncio-ssh-python-embed.py index 9bbad86f..bf79df78 100755 --- a/examples/asyncio-ssh-python-embed.py +++ b/examples/asyncio-ssh-python-embed.py @@ -44,8 +44,8 @@ async def main(port: int = 8222) -> None: def create_server() -> MySSHServer: return MySSHServer(lambda: environ) - print("Listening on :%i" % port) - print('To connect, do "ssh localhost -p %i"' % port) + print(f"Listening on: {port}") + print(f'To connect, do "ssh localhost -p {port}"') await asyncssh.create_server( create_server, "", port, server_host_keys=["/etc/ssh/ssh_host_dsa_key"] diff --git a/ptpython/layout.py b/ptpython/layout.py index 622df594..9768598e 100644 --- a/ptpython/layout.py +++ b/ptpython/layout.py @@ -108,7 +108,7 @@ def append_category(category: OptionCategory[Any]) -> None: tokens.extend( [ ("class:sidebar", " "), - ("class:sidebar.title", " %-36s" % category.title), + ("class:sidebar.title", f" {category.title:36}"), ("class:sidebar", "\n"), ] ) @@ -130,7 +130,7 @@ def goto_next(mouse_event: MouseEvent) -> None: sel = ",selected" if selected else "" tokens.append(("class:sidebar" + sel, " >" if selected else " ")) - tokens.append(("class:sidebar.label" + sel, "%-24s" % label, select_item)) + tokens.append(("class:sidebar.label" + sel, f"{label:24}", select_item)) tokens.append(("class:sidebar.status" + sel, " ", select_item)) tokens.append(("class:sidebar.status" + sel, f"{status}", goto_next)) @@ -332,7 +332,7 @@ def get_continuation( width: int, line_number: int, is_soft_wrap: bool ) -> StyleAndTextTuples: if python_input.show_line_numbers and not is_soft_wrap: - text = ("%i " % (line_number + 1)).rjust(width) + text = f"{line_number + 1} ".rjust(width) return [("class:line-number", text)] else: return to_formatted_text(get_prompt_style().in2_prompt(width)) @@ -368,8 +368,7 @@ def get_text_fragments() -> StyleAndTextTuples: append( ( TB, - "%i/%i " - % (python_buffer.working_index + 1, len(python_buffer._working_lines)), + f"{python_buffer.working_index + 1}/{len(python_buffer._working_lines)} ", ) ) @@ -492,8 +491,7 @@ def toggle_sidebar(mouse_event: MouseEvent) -> None: ("class:status-toolbar", " - "), ( "class:status-toolbar.python-version", - "%s %i.%i.%i" - % (platform.python_implementation(), version[0], version[1], version[2]), + f"{platform.python_implementation()} {version[0]}.{version[1]}.{version[2]}", ), ("class:status-toolbar", " "), ] diff --git a/ptpython/printer.py b/ptpython/printer.py index 81ea16f3..a3578de7 100644 --- a/ptpython/printer.py +++ b/ptpython/printer.py @@ -1,6 +1,5 @@ from __future__ import annotations -import sys import traceback from dataclasses import dataclass from enum import Enum diff --git a/ptpython/repl.py b/ptpython/repl.py index 9142d909..ba6717fb 100644 --- a/ptpython/repl.py +++ b/ptpython/repl.py @@ -362,7 +362,7 @@ async def eval_async(self, line: str) -> object: def _store_eval_result(self, result: object) -> None: locals: dict[str, Any] = self.get_locals() - locals["_"] = locals["_%i" % self.current_statement_index] = result + locals["_"] = locals[f"_{self.current_statement_index}"] = result def get_compiler_flags(self) -> int: return super().get_compiler_flags() | PyCF_ALLOW_TOP_LEVEL_AWAIT From ce3a9e2f5495a7ae5146942e468e3565cbe3a87c Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 13:51:45 +0000 Subject: [PATCH 04/25] Use uv in github actions. --- .github/workflows/test.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2311e02a..c9fb0ae8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,20 +10,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + + - uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} - name: Install Dependencies run: | - sudo apt remove python3-pip - python -m pip install --upgrade pip - python -m pip install . ruff mypy pytest readme_renderer - pip list + uv pip install . ruff mypy pytest readme_renderer + uv pip list - name: Type Checker run: | mypy ptpython From 1f1eb1796a67699bbc2bba21129aaf1e6dab978b Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 14:00:42 +0000 Subject: [PATCH 05/25] Reworked dummy test directory. --- .github/workflows/test.yaml | 2 +- tests/run_tests.py | 24 ------------------------ tests/test_dummy.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 25 deletions(-) delete mode 100755 tests/run_tests.py create mode 100755 tests/test_dummy.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c9fb0ae8..3f527abe 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -29,7 +29,7 @@ jobs: ruff format --check . - name: Run Tests run: | - ./tests/run_tests.py + pytest tests/ - name: Validate README.md # Ensure that the README renders correctly (required for uploading to PyPI). run: | diff --git a/tests/run_tests.py b/tests/run_tests.py deleted file mode 100755 index 0de37430..00000000 --- a/tests/run_tests.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -from __future__ import annotations - -import unittest - -import ptpython.completer -import ptpython.eventloop -import ptpython.filters -import ptpython.history_browser -import ptpython.key_bindings -import ptpython.layout -import ptpython.python_input -import ptpython.repl -import ptpython.style -import ptpython.utils -import ptpython.validator - -# For now there are no tests here. -# However this is sufficient for Travis to do at least a syntax check. -# That way we are at least sure to restrict to the Python 2.6 syntax. - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_dummy.py b/tests/test_dummy.py new file mode 100755 index 00000000..922c6a39 --- /dev/null +++ b/tests/test_dummy.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +from __future__ import annotations + +import ptpython.completer +import ptpython.eventloop +import ptpython.filters +import ptpython.history_browser +import ptpython.key_bindings +import ptpython.layout +import ptpython.python_input +import ptpython.repl +import ptpython.style +import ptpython.utils +import ptpython.validator + +# For now there are no tests here. +# However this is sufficient to do at least a syntax check. + + +def test_dummy() -> None: + assert ptpython.completer + assert ptpython.eventloop + assert ptpython.filters + assert ptpython.history_browser + assert ptpython.key_bindings + assert ptpython.layout + assert ptpython.python_input + assert ptpython.repl + assert ptpython.style + assert ptpython.utils + assert ptpython.validator From f1dea7efe97426eec9e7218a0fdc0e17bc47aca8 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 20:35:59 +0000 Subject: [PATCH 06/25] Use pyproject.toml instead of setup.py Cherry-picked from: https://github.com/prompt-toolkit/ptpython/pull/599 Thanks to: Branch Vincent --- pyproject.toml | 58 +++++++++++++++++++++++++++++++++++++++++++- setup.cfg | 41 ------------------------------- setup.py | 66 -------------------------------------------------- 3 files changed, 57 insertions(+), 108 deletions(-) delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml index ce420372..3780f9d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,55 @@ +[project] +name = "ptpython" +version = "3.0.29" +description = "Python REPL build on top of prompt_toolkit" +readme = "README.rst" +authors = [{ name = "Jonathan Slenders" }] +classifiers = [ + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python", +] +requires-python = ">=3.8" +dependencies = [ + "appdirs", + "jedi>=0.16.0", + # Use prompt_toolkit 3.0.43, because of `OneStyleAndTextTuple` import. + "prompt_toolkit>=3.0.43,<3.1.0", + "pygments", +] + + +[project.urls] +Homepage = "https://github.com/prompt-toolkit/ptpython" +Changelog = "https://github.com/prompt-toolkit/ptpython/blob/master/CHANGELOG" +"Bug Tracker" = "https://github.com/prompt-toolkit/ptpython/issues" +"Source Code" = "https://github.com/prompt-toolkit/ptpython" + + +[project.scripts] +ptpython = "ptpython.entry_points.run_ptpython:run" +ptipython = "ptpython.entry_points.run_ptipython:run" + + +[project.optional-dependencies] +ptipython = ["ipython"] # For ptipython, we need to have IPython + + +[tool.mypy] +ignore_missing_imports = true +no_implicit_optional = true +platform = "win32" +strict_equality = true +strict_optional = true + + [tool.ruff] target-version = "py37" lint.select = [ @@ -27,9 +79,13 @@ lint.ignore = [ "ptpython/ipython.py" = ["T100"] # Import usage. "ptpython/repl.py" = ["T201"] # Print usage. "ptpython/printer.py" = ["T201"] # Print usage. -"tests/run_tests.py" = ["F401"] # Unused imports. [tool.ruff.lint.isort] known-first-party = ["ptpython"] known-third-party = ["prompt_toolkit", "pygments", "asyncssh"] + + +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 80dfec6a..00000000 --- a/setup.cfg +++ /dev/null @@ -1,41 +0,0 @@ -[bdist_wheel] -universal=1 - -[flake8] -exclude=__init__.py -max_line_length=150 -ignore= - E114, - E116, - E117, - E121, - E122, - E123, - E125, - E126, - E127, - E128, - E131, - E171, - E203, - E211, - E221, - E227, - E231, - E241, - E251, - E301, - E402, - E501, - E701, - E702, - E704, - E731, - E741, - F401, - F403, - F405, - F811, - W503, - W504, - E722 diff --git a/setup.py b/setup.py deleted file mode 100644 index bd2f962a..00000000 --- a/setup.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -from setuptools import find_packages, setup - -with open(os.path.join(os.path.dirname(__file__), "README.rst")) as f: - long_description = f.read() - - -setup( - name="ptpython", - author="Jonathan Slenders", - version="3.0.29", - url="https://github.com/prompt-toolkit/ptpython", - description="Python REPL build on top of prompt_toolkit", - long_description=long_description, - package_urls={ - "Changelog": "https://github.com/prompt-toolkit/ptpython/blob/master/CHANGELOG", - }, - project_urls={ - "Bug Tracker": "https://github.com/prompt-toolkit/ptpython/issues", - "Source Code": "https://github.com/prompt-toolkit/ptpython", - "Changelog": "https://github.com/prompt-toolkit/ptpython/blob/master/CHANGELOG", - }, - packages=find_packages("."), - package_data={"ptpython": ["py.typed"]}, - install_requires=[ - "appdirs", - "jedi>=0.16.0", - # Use prompt_toolkit 3.0.43, because of `OneStyleAndTextTuple` import. - "prompt_toolkit>=3.0.43,<3.1.0", - "pygments", - ], - python_requires=">=3.8", - classifiers=[ - "License :: OSI Approved :: BSD License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python", - ], - entry_points={ - "console_scripts": [ - "ptpython = ptpython.entry_points.run_ptpython:run", - "ptipython = ptpython.entry_points.run_ptipython:run", - f"ptpython{sys.version_info[0]} = ptpython.entry_points.run_ptpython:run", - "ptpython{}.{} = ptpython.entry_points.run_ptpython:run".format( - *sys.version_info[:2] - ), - f"ptipython{sys.version_info[0]} = ptpython.entry_points.run_ptipython:run", - "ptipython{}.{} = ptpython.entry_points.run_ptipython:run".format( - *sys.version_info[:2] - ), - ] - }, - extras_require={ - "ptipython": ["ipython"], # For ptipython, we need to have IPython - "all": ["black"], # Black not always possible on PyPy - }, -) From acf61459a7b203815a738cf6dc5ec20288e3ce19 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 20:48:11 +0000 Subject: [PATCH 07/25] Use uvx in GitHub workflows. --- .github/workflows/test.yaml | 18 ++++++++---------- ptpython/history_browser.py | 4 +++- ptpython/key_bindings.py | 4 +++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3f527abe..74c3c7b8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -14,23 +14,21 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} - - name: Install Dependencies + - name: Type Checking run: | - uv pip install . ruff mypy pytest readme_renderer - uv pip list - - name: Type Checker + uvx --with . mypy ptpython + - name: Code formatting run: | - mypy ptpython - ruff check . - ruff format --check . - - name: Run Tests + uvx ruff check . + uvx ruff format --check . + - name: Unit test run: | - pytest tests/ + uvx --with . pytest tests/ - name: Validate README.md # Ensure that the README renders correctly (required for uploading to PyPI). run: | + uv pip install readme_renderer python -m readme_renderer README.rst > /dev/null diff --git a/ptpython/history_browser.py b/ptpython/history_browser.py index ae0ac03e..72bc576d 100644 --- a/ptpython/history_browser.py +++ b/ptpython/history_browser.py @@ -58,13 +58,15 @@ from .utils import if_mousedown if TYPE_CHECKING: + from typing_extensions import TypeAlias + from .python_input import PythonInput HISTORY_COUNT = 2000 __all__ = ["HistoryLayout", "PythonHistory"] -E = KeyPressEvent +E: TypeAlias = KeyPressEvent HELP_TEXT = """ This interface is meant to select multiple lines from the diff --git a/ptpython/key_bindings.py b/ptpython/key_bindings.py index d7bb575e..48c5f5ae 100644 --- a/ptpython/key_bindings.py +++ b/ptpython/key_bindings.py @@ -22,6 +22,8 @@ from .utils import document_is_multiline_python if TYPE_CHECKING: + from typing_extensions import TypeAlias + from .python_input import PythonInput __all__ = [ @@ -30,7 +32,7 @@ "load_confirm_exit_bindings", ] -E = KeyPressEvent +E: TypeAlias = KeyPressEvent @Condition From 39b1cbda27e7b579e7b470311d409924457e072b Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 20:59:43 +0000 Subject: [PATCH 08/25] Remove mypy.ini --- mypy.ini | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 mypy.ini diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 5a7ef2eb..00000000 --- a/mypy.ini +++ /dev/null @@ -1,6 +0,0 @@ -[mypy] -ignore_missing_imports = True -no_implicit_optional = True -platform = win32 -strict_equality = True -strict_optional = True From 1527d0527625a2c72b154a6cb937f0e4dec9a87a Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 21:07:52 +0000 Subject: [PATCH 09/25] use src/ directory for source code. --- .github/workflows/test.yaml | 2 +- pyproject.toml | 10 +++++----- {ptpython => src/ptpython}/__init__.py | 0 {ptpython => src/ptpython}/__main__.py | 0 {ptpython => src/ptpython}/completer.py | 0 {ptpython => src/ptpython}/contrib/__init__.py | 0 {ptpython => src/ptpython}/contrib/asyncssh_repl.py | 0 {ptpython => src/ptpython}/entry_points/__init__.py | 0 .../ptpython}/entry_points/run_ptipython.py | 0 .../ptpython}/entry_points/run_ptpython.py | 0 {ptpython => src/ptpython}/eventloop.py | 0 {ptpython => src/ptpython}/filters.py | 0 {ptpython => src/ptpython}/history_browser.py | 0 {ptpython => src/ptpython}/ipython.py | 0 {ptpython => src/ptpython}/key_bindings.py | 0 {ptpython => src/ptpython}/layout.py | 0 {ptpython => src/ptpython}/lexer.py | 0 {ptpython => src/ptpython}/printer.py | 0 {ptpython => src/ptpython}/prompt_style.py | 0 {ptpython => src/ptpython}/py.typed | 0 {ptpython => src/ptpython}/python_input.py | 0 {ptpython => src/ptpython}/repl.py | 0 {ptpython => src/ptpython}/signatures.py | 0 {ptpython => src/ptpython}/style.py | 0 {ptpython => src/ptpython}/utils.py | 0 {ptpython => src/ptpython}/validator.py | 0 26 files changed, 6 insertions(+), 6 deletions(-) rename {ptpython => src/ptpython}/__init__.py (100%) rename {ptpython => src/ptpython}/__main__.py (100%) rename {ptpython => src/ptpython}/completer.py (100%) rename {ptpython => src/ptpython}/contrib/__init__.py (100%) rename {ptpython => src/ptpython}/contrib/asyncssh_repl.py (100%) rename {ptpython => src/ptpython}/entry_points/__init__.py (100%) rename {ptpython => src/ptpython}/entry_points/run_ptipython.py (100%) rename {ptpython => src/ptpython}/entry_points/run_ptpython.py (100%) rename {ptpython => src/ptpython}/eventloop.py (100%) rename {ptpython => src/ptpython}/filters.py (100%) rename {ptpython => src/ptpython}/history_browser.py (100%) rename {ptpython => src/ptpython}/ipython.py (100%) rename {ptpython => src/ptpython}/key_bindings.py (100%) rename {ptpython => src/ptpython}/layout.py (100%) rename {ptpython => src/ptpython}/lexer.py (100%) rename {ptpython => src/ptpython}/printer.py (100%) rename {ptpython => src/ptpython}/prompt_style.py (100%) rename {ptpython => src/ptpython}/py.typed (100%) rename {ptpython => src/ptpython}/python_input.py (100%) rename {ptpython => src/ptpython}/repl.py (100%) rename {ptpython => src/ptpython}/signatures.py (100%) rename {ptpython => src/ptpython}/style.py (100%) rename {ptpython => src/ptpython}/utils.py (100%) rename {ptpython => src/ptpython}/validator.py (100%) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 74c3c7b8..457a4e48 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,7 +19,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Type Checking run: | - uvx --with . mypy ptpython + uvx --with . mypy src/ptpython - name: Code formatting run: | uvx ruff check . diff --git a/pyproject.toml b/pyproject.toml index 3780f9d6..680d7087 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,11 +74,11 @@ lint.ignore = [ [tool.ruff.lint.per-file-ignores] "examples/*" = ["T201"] # Print allowed in examples. "examples/ptpython_config/config.py" = ["F401"] # Unused imports in config. -"ptpython/entry_points/run_ptipython.py" = ["T201", "F401"] # Print, import usage. -"ptpython/entry_points/run_ptpython.py" = ["T201"] # Print usage. -"ptpython/ipython.py" = ["T100"] # Import usage. -"ptpython/repl.py" = ["T201"] # Print usage. -"ptpython/printer.py" = ["T201"] # Print usage. +"src/ptpython/entry_points/run_ptipython.py" = ["T201", "F401"] # Print, import usage. +"src/ptpython/entry_points/run_ptpython.py" = ["T201"] # Print usage. +"src/ptpython/ipython.py" = ["T100"] # Import usage. +"src/ptpython/repl.py" = ["T201"] # Print usage. +"src/ptpython/printer.py" = ["T201"] # Print usage. [tool.ruff.lint.isort] diff --git a/ptpython/__init__.py b/src/ptpython/__init__.py similarity index 100% rename from ptpython/__init__.py rename to src/ptpython/__init__.py diff --git a/ptpython/__main__.py b/src/ptpython/__main__.py similarity index 100% rename from ptpython/__main__.py rename to src/ptpython/__main__.py diff --git a/ptpython/completer.py b/src/ptpython/completer.py similarity index 100% rename from ptpython/completer.py rename to src/ptpython/completer.py diff --git a/ptpython/contrib/__init__.py b/src/ptpython/contrib/__init__.py similarity index 100% rename from ptpython/contrib/__init__.py rename to src/ptpython/contrib/__init__.py diff --git a/ptpython/contrib/asyncssh_repl.py b/src/ptpython/contrib/asyncssh_repl.py similarity index 100% rename from ptpython/contrib/asyncssh_repl.py rename to src/ptpython/contrib/asyncssh_repl.py diff --git a/ptpython/entry_points/__init__.py b/src/ptpython/entry_points/__init__.py similarity index 100% rename from ptpython/entry_points/__init__.py rename to src/ptpython/entry_points/__init__.py diff --git a/ptpython/entry_points/run_ptipython.py b/src/ptpython/entry_points/run_ptipython.py similarity index 100% rename from ptpython/entry_points/run_ptipython.py rename to src/ptpython/entry_points/run_ptipython.py diff --git a/ptpython/entry_points/run_ptpython.py b/src/ptpython/entry_points/run_ptpython.py similarity index 100% rename from ptpython/entry_points/run_ptpython.py rename to src/ptpython/entry_points/run_ptpython.py diff --git a/ptpython/eventloop.py b/src/ptpython/eventloop.py similarity index 100% rename from ptpython/eventloop.py rename to src/ptpython/eventloop.py diff --git a/ptpython/filters.py b/src/ptpython/filters.py similarity index 100% rename from ptpython/filters.py rename to src/ptpython/filters.py diff --git a/ptpython/history_browser.py b/src/ptpython/history_browser.py similarity index 100% rename from ptpython/history_browser.py rename to src/ptpython/history_browser.py diff --git a/ptpython/ipython.py b/src/ptpython/ipython.py similarity index 100% rename from ptpython/ipython.py rename to src/ptpython/ipython.py diff --git a/ptpython/key_bindings.py b/src/ptpython/key_bindings.py similarity index 100% rename from ptpython/key_bindings.py rename to src/ptpython/key_bindings.py diff --git a/ptpython/layout.py b/src/ptpython/layout.py similarity index 100% rename from ptpython/layout.py rename to src/ptpython/layout.py diff --git a/ptpython/lexer.py b/src/ptpython/lexer.py similarity index 100% rename from ptpython/lexer.py rename to src/ptpython/lexer.py diff --git a/ptpython/printer.py b/src/ptpython/printer.py similarity index 100% rename from ptpython/printer.py rename to src/ptpython/printer.py diff --git a/ptpython/prompt_style.py b/src/ptpython/prompt_style.py similarity index 100% rename from ptpython/prompt_style.py rename to src/ptpython/prompt_style.py diff --git a/ptpython/py.typed b/src/ptpython/py.typed similarity index 100% rename from ptpython/py.typed rename to src/ptpython/py.typed diff --git a/ptpython/python_input.py b/src/ptpython/python_input.py similarity index 100% rename from ptpython/python_input.py rename to src/ptpython/python_input.py diff --git a/ptpython/repl.py b/src/ptpython/repl.py similarity index 100% rename from ptpython/repl.py rename to src/ptpython/repl.py diff --git a/ptpython/signatures.py b/src/ptpython/signatures.py similarity index 100% rename from ptpython/signatures.py rename to src/ptpython/signatures.py diff --git a/ptpython/style.py b/src/ptpython/style.py similarity index 100% rename from ptpython/style.py rename to src/ptpython/style.py diff --git a/ptpython/utils.py b/src/ptpython/utils.py similarity index 100% rename from ptpython/utils.py rename to src/ptpython/utils.py diff --git a/ptpython/validator.py b/src/ptpython/validator.py similarity index 100% rename from ptpython/validator.py rename to src/ptpython/validator.py From 030790f8fb8da7736cc91a76712c99f230d1ebe1 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 22:02:03 +0000 Subject: [PATCH 10/25] Add typos to workflow. --- .github/workflows/test.yaml | 6 ++++++ pyproject.toml | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 457a4e48..6d2877b3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,13 +21,19 @@ jobs: run: | uvx --with . mypy src/ptpython - name: Code formatting + if: ${{ matrix.python-version == '3.13' }} run: | uvx ruff check . uvx ruff format --check . + - name: Typos + if: ${{ matrix.python-version == '3.13' }} + run: | + uvx typos . - name: Unit test run: | uvx --with . pytest tests/ - name: Validate README.md + if: ${{ matrix.python-version == '3.13' }} # Ensure that the README renders correctly (required for uploading to PyPI). run: | uv pip install readme_renderer diff --git a/pyproject.toml b/pyproject.toml index 680d7087..72259863 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,10 @@ lint.ignore = [ known-first-party = ["ptpython"] known-third-party = ["prompt_toolkit", "pygments", "asyncssh"] +[tool.typos.default] +extend-ignore-re = [ + "impotr" # Intentional typo in: ./examples/ptpython_config/config.py +] [build-system] requires = ["setuptools>=68"] From fb4949ad52ce7d603ab5bb52fba572c6dfdaad0b Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 10 Apr 2025 22:21:44 +0000 Subject: [PATCH 11/25] Typecheck examples. --- .github/workflows/test.yaml | 3 ++- examples/ssh-and-telnet-embed.py | 6 +++-- src/ptpython/repl.py | 43 ++++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6d2877b3..d53bfcc1 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,7 +19,8 @@ jobs: python-version: ${{ matrix.python-version }} - name: Type Checking run: | - uvx --with . mypy src/ptpython + uvx --with . mypy src/ptpython/ + uvx --with . mypy examples/ - name: Code formatting if: ${{ matrix.python-version == '3.13' }} run: | diff --git a/examples/ssh-and-telnet-embed.py b/examples/ssh-and-telnet-embed.py index 62fa76d9..2b293e6f 100755 --- a/examples/ssh-and-telnet-embed.py +++ b/examples/ssh-and-telnet-embed.py @@ -6,6 +6,8 @@ https://gist.github.com/vxgmichel/7685685b3e5ead04ada4a3ba75a48eef """ +from __future__ import annotations + import asyncio import pathlib @@ -15,7 +17,7 @@ PromptToolkitSSHServer, PromptToolkitSSHSession, ) -from prompt_toolkit.contrib.telnet.server import TelnetServer +from prompt_toolkit.contrib.telnet.server import TelnetConnection, TelnetServer from ptpython.repl import embed @@ -28,7 +30,7 @@ def ensure_key(filename: str = "ssh_host_key") -> str: return str(path) -async def interact(connection: PromptToolkitSSHSession) -> None: +async def interact(connection: PromptToolkitSSHSession | TelnetConnection) -> None: global_dict = {**globals(), "print": print_formatted_text} await embed(return_asyncio_coroutine=True, globals=global_dict) diff --git a/src/ptpython/repl.py b/src/ptpython/repl.py index ba6717fb..469ed694 100644 --- a/src/ptpython/repl.py +++ b/src/ptpython/repl.py @@ -20,7 +20,17 @@ import warnings from dis import COMPILER_FLAG_NAMES from pathlib import Path -from typing import Any, Callable, ContextManager, Iterable, NoReturn, Sequence +from typing import ( + Any, + Callable, + ContextManager, + Coroutine, + Iterable, + Literal, + NoReturn, + Sequence, + overload, +) from prompt_toolkit.formatted_text import OneStyleAndTextTuple from prompt_toolkit.patch_stdout import patch_stdout as patch_stdout_context @@ -505,6 +515,34 @@ class ReplExit(Exception): """ +@overload +def embed( + globals: dict[str, Any] | None = ..., + locals: dict[str, Any] | None = ..., + configure: Callable[[PythonRepl], None] | None = ..., + vi_mode: bool = ..., + history_filename: str | None = ..., + title: str | None = ..., + startup_paths: Sequence[str | Path] | None = ..., + patch_stdout: bool = ..., + return_asyncio_coroutine: Literal[False] = ..., +) -> None: ... + + +@overload +def embed( + globals: dict[str, Any] | None = ..., + locals: dict[str, Any] | None = ..., + configure: Callable[[PythonRepl], None] | None = ..., + vi_mode: bool = ..., + history_filename: str | None = ..., + title: str | None = ..., + startup_paths: Sequence[str | Path] | None = ..., + patch_stdout: bool = ..., + return_asyncio_coroutine: Literal[True] = ..., +) -> Coroutine[Any, Any, None]: ... + + def embed( globals: dict[str, Any] | None = None, locals: dict[str, Any] | None = None, @@ -515,7 +553,7 @@ def embed( startup_paths: Sequence[str | Path] | None = None, patch_stdout: bool = False, return_asyncio_coroutine: bool = False, -) -> None: +) -> None | Coroutine[Any, Any, None]: """ Call this to embed Python shell at the current point in your program. It's similar to `IPython.embed` and `bpython.embed`. :: @@ -577,3 +615,4 @@ async def coroutine() -> None: else: with patch_context: repl.run() + return None From 836431ff6775aac2c2e3aafa3295b259ebe99d0a Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Tue, 15 Apr 2025 09:24:02 +0000 Subject: [PATCH 12/25] Release 3.0.30 --- CHANGELOG | 11 +++++++++++ pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index bef7d07f..7706260d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,17 @@ CHANGELOG ========= +3.0.30: 2025-04-15 +------------------ + +New features: +- Show exception cause/context when printing chained exceptions. +- Reworked project layout and use pyproject.toml instead of setup.py. + +Breaking changes: +- Drop Python 3.7 support. + + 3.0.29: 2024-07-22 ------------------ diff --git a/pyproject.toml b/pyproject.toml index 72259863..00e2d5f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ptpython" -version = "3.0.29" +version = "3.0.30" description = "Python REPL build on top of prompt_toolkit" readme = "README.rst" authors = [{ name = "Jonathan Slenders" }] From c22b59d21147d36560a2ff802c8ba1876797e208 Mon Sep 17 00:00:00 2001 From: PEMessage <1165739182@qq.com> Date: Thu, 26 Jun 2025 00:20:27 +0800 Subject: [PATCH 13/25] Fix repl.min_brightness and repl.max_brightness config not work in history --- src/ptpython/history_browser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ptpython/history_browser.py b/src/ptpython/history_browser.py index 72bc576d..101a6c5c 100644 --- a/src/ptpython/history_browser.py +++ b/src/ptpython/history_browser.py @@ -643,6 +643,7 @@ def accept_handler(buffer: Buffer) -> bool: layout=self.history_layout.layout, full_screen=True, style=python_input._current_style, + style_transformation=python_input.style_transformation, mouse_support=Condition(lambda: python_input.enable_mouse_support), key_bindings=create_key_bindings(self, python_input, history_mapping), ) From 3767d50ff330f7847cc1695ab41de6b80c444fff Mon Sep 17 00:00:00 2001 From: Shengchen Zhang Date: Fri, 27 Jun 2025 20:47:18 +0800 Subject: [PATCH 14/25] Expose `raw` parameter from prompt-toolkit to allow escape sequences to print normally. --- src/ptpython/repl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ptpython/repl.py b/src/ptpython/repl.py index 469ed694..90772689 100644 --- a/src/ptpython/repl.py +++ b/src/ptpython/repl.py @@ -525,6 +525,7 @@ def embed( title: str | None = ..., startup_paths: Sequence[str | Path] | None = ..., patch_stdout: bool = ..., + patch_stdout_raw: bool = ..., return_asyncio_coroutine: Literal[False] = ..., ) -> None: ... @@ -539,6 +540,7 @@ def embed( title: str | None = ..., startup_paths: Sequence[str | Path] | None = ..., patch_stdout: bool = ..., + patch_stdout_raw: bool = ..., return_asyncio_coroutine: Literal[True] = ..., ) -> Coroutine[Any, Any, None]: ... @@ -552,6 +554,7 @@ def embed( title: str | None = None, startup_paths: Sequence[str | Path] | None = None, patch_stdout: bool = False, + patch_stdout_raw: bool = False, return_asyncio_coroutine: bool = False, ) -> None | Coroutine[Any, Any, None]: """ @@ -567,6 +570,7 @@ def embed( :param title: Title to be displayed in the terminal titlebar. (None or string.) :param patch_stdout: When true, patch `sys.stdout` so that background threads that are printing will print nicely above the prompt. + :param patch_stdout_raw: When true, patch_stdout will not escape/remove vt100 terminal escape sequences. """ # Default globals/locals if globals is None: @@ -602,7 +606,7 @@ def get_locals() -> dict[str, Any]: # Start repl. patch_context: ContextManager[None] = ( - patch_stdout_context() if patch_stdout else DummyContext() + patch_stdout_context(raw=patch_stdout_raw) if patch_stdout else DummyContext() ) if return_asyncio_coroutine: From a14c329147f498fd0ce28240a16de8620d454180 Mon Sep 17 00:00:00 2001 From: Shengchen Zhang Date: Tue, 8 Jul 2025 16:44:27 +0100 Subject: [PATCH 15/25] Update dead image links in README.rst --- README.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 06c1e02b..2559a3c4 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ ptpython pip install ptpython -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/example1.png +.. image :: ./docs/images/example1.png Ptpython is an advanced Python REPL. It should work on all Python versions from 2.6 up to 3.11 and work cross platform (Linux, @@ -109,15 +109,15 @@ More screenshots The configuration menu: -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ptpython-menu.png +.. image :: ./docs/images/ptpython-menu.png The history page and its help: -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ptpython-history-help.png +.. image :: ./docs/images/ptpython-history-help.png Autocompletion: -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/file-completion.png +.. image :: ./docs/images/file-completion.png Embedding the REPL @@ -159,7 +159,7 @@ terminal, you have to check the "Use option as meta key" checkbox in your terminal settings. For iTerm2, you have to check "Left option acts as +Esc" in the options.) -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/multiline.png +.. image :: ./docs/images/multiline.png Syntax validation @@ -169,7 +169,7 @@ Before execution, ``ptpython`` will see whether the input is syntactically correct Python code. If not, it will show a warning, and move the cursor to the error. -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/validation.png +.. image :: ./docs/images/validation.png Asyncio REPL and top level await @@ -208,7 +208,7 @@ variable, if set, can also be used to explicitly override where configuration is looked for. Have a look at this example to see what is possible: -`config.py `_ +`config.py `_ Note config file support currently only works when invoking `ptpython` directly. That it, the config file will be ignored when embedding ptpython in an application. @@ -222,7 +222,7 @@ with all the power that IPython has to offer, like magic functions and shell integration. Make sure that IPython has been installed. (``pip install ipython``) -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ipython.png +.. image :: ./docs/images/ipython.png This is also available for embedding: @@ -253,7 +253,7 @@ Windows support ``prompt_toolkit`` and ``ptpython`` works better on Linux and OS X than on Windows. Some things might not work, but it is usable: -.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/windows.png +.. image :: ./docs/images/windows.png Windows terminal integration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From c49bf9b05a204fd8ed296698c5a6fe60b712dc1f Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Wed, 27 Aug 2025 11:30:30 +0000 Subject: [PATCH 16/25] Fix positioning of exit confirmation (compatibilitiy with latest prompt_toolkit). --- src/ptpython/layout.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ptpython/layout.py b/src/ptpython/layout.py index 9768598e..8d030dd1 100644 --- a/src/ptpython/layout.py +++ b/src/ptpython/layout.py @@ -694,7 +694,8 @@ def menu_position() -> int | None: ), Float( left=2, - bottom=1, + top=2, + height=3, content=self.exit_confirmation, ), Float( From b3959fe513d8b3fa0306ce0599347b779d736c07 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Wed, 27 Aug 2025 11:36:57 +0000 Subject: [PATCH 17/25] Upgrade ruff check to Python 3.8. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 00e2d5f8..9912c736 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ strict_optional = true [tool.ruff] -target-version = "py37" +target-version = "py38" lint.select = [ "E", # pycodestyle errors "W", # pycodestyle warnings From 0066c7ee392d46103b8bd2968c7a645a4c04f02d Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Wed, 27 Aug 2025 15:29:12 +0000 Subject: [PATCH 18/25] Release 3.0.31 --- CHANGELOG | 9 +++++++++ pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 7706260d..8a07ede2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,15 @@ CHANGELOG ========= +3.0.31: 2025-08-27 +------------------ + +Fixes: +- Add `patch_stdout_raw` parameter to `embed()`. +- Fix repl.min_brightness and repl.max_brightness config in history. +- Fix positioning of exit confirmation (compatibilitiy with latest prompt_toolkit). + + 3.0.30: 2025-04-15 ------------------ diff --git a/pyproject.toml b/pyproject.toml index 9912c736..f1f3a853 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ptpython" -version = "3.0.30" +version = "3.0.31" description = "Python REPL build on top of prompt_toolkit" readme = "README.rst" authors = [{ name = "Jonathan Slenders" }] From d29f20d996e301db1ce12cfd77cfa50a9c128155 Mon Sep 17 00:00:00 2001 From: tapple-cisco <167885465+tapple-cisco@users.noreply.github.com> Date: Tue, 16 Sep 2025 07:01:39 -0700 Subject: [PATCH 19/25] Note in config example where the file goes on Windows --- examples/ptpython_config/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/ptpython_config/config.py b/examples/ptpython_config/config.py index bfd3914e..4efd5d97 100644 --- a/examples/ptpython_config/config.py +++ b/examples/ptpython_config/config.py @@ -4,6 +4,7 @@ Copy this file to $XDG_CONFIG_HOME/ptpython/config.py On Linux, this is: ~/.config/ptpython/config.py On macOS, this is: ~/Library/Application Support/ptpython/config.py +On Windows, this is: ~\AppData\Local\prompt_toolkit\ptpython\config.py """ from prompt_toolkit.filters import ViInsertMode From 86c6d1168a8f020f442309cd5bc3023ed6f0764d Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Sat, 8 Nov 2025 22:09:54 +0000 Subject: [PATCH 20/25] Use ANSI colors for the default theme. --- src/ptpython/python_input.py | 2 +- src/ptpython/style.py | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/ptpython/python_input.py b/src/ptpython/python_input.py index b1773643..7e218eb9 100644 --- a/src/ptpython/python_input.py +++ b/src/ptpython/python_input.py @@ -350,7 +350,7 @@ def __init__( #: Load styles. self.code_styles: dict[str, BaseStyle] = get_all_code_styles() self.ui_styles = get_all_ui_styles() - self._current_code_style_name: str = "default" + self._current_code_style_name: str = "default-ansi" self._current_ui_style_name: str = "default" if is_windows(): diff --git a/src/ptpython/style.py b/src/ptpython/style.py index c5a04e58..85219717 100644 --- a/src/ptpython/style.py +++ b/src/ptpython/style.py @@ -17,6 +17,7 @@ def get_all_code_styles() -> dict[str, BaseStyle]: for name in get_all_styles() } result["win32"] = Style.from_dict(win32_code_style) + result["default-ansi"] = Style.from_dict(default_ansi_code_style) return result @@ -38,6 +39,63 @@ def generate_style(python_style: BaseStyle, ui_style: BaseStyle) -> BaseStyle: return merge_styles([python_style, ui_style]) +# Use ANSI colors for the default theme. +# This is `DefaultStyle` from Pygments, modified to use ANSI colors instead of +# RGB. This adapts better to light/dark mode, because the built-in themes from +# a terminal are typically designed for whatever background is used. All the +# other Pygments themes use RGB, which is fine, because the user consciously +# chooses what works for them. + +# To convert, do: +# from prompt_toolkit.output import ColorDepth +# from prompt_toolkit.output.vt100 import _EscapeCodeCache, _get_closest_ansi_color +# print(_get_closest_ansi_color( +# *_EscapeCodeCache(ColorDepth.DEPTH_8_BIT)._color_name_to_rgb('bbbbbb')) +# ) + +default_ansi_code_style = { + "pygments.whitespace": "ansigray", # "#bbbbbb", + "pygments.comment": "italic ansibrightblack", # "italic #3d7b7b", + "pygments.comment.preproc": "noitalic ansired", # "noitalic #9c6500", + "pygments.keyword": "bold ansigreen", # "bold #008000", + "pygments.keyword.pseudo": "nobold", + "pygments.keyword.type": "nobold ansired", # "nobold #b00040", + "pygments.operator": "ansibrightblack", # "#666666", + "pygments.operator.word": "bold ansimagenta", # "bold #aa22ff", + "pygments.name.builtin": "ansigreen", # "#008000", + "pygments.name.function": "ansibrightblue", # "#0000ff", + "pygments.name.class": "bold ansibrightblue", # "bold #0000ff", + "pygments.name.namespace": "bold ansibrightblack", # "bold #0000ff", + "pygments.name.exception": "bold ansired", # "bold #cb3f38", + "pygments.name.variable": "ansiblue", # "#19177c", + "pygments.name.constant": "ansired", # "#880000", + "pygments.name.label": "ansiyellow", # "#767600", + "pygments.name.entity": "bold ansibrightblack", # "bold #717171", + "pygments.name.attribute": "ansibrightblack", # "#687822", + "pygments.name.tag": "bold ansigreen", # "bold #008000", + "pygments.name.decorator": "ansimagenta", # "#aa22ff", + "pygments.literal.string": "ansired", # "#ba2121", + "pygments.literal.string.doc": "italic", + "pygments.literal.string.interpol": "bold ansibrightblack", # "bold #a45a77", + "pygments.literal.string.escape": "bold ansired", # "bold #aa5d1f", + "pygments.literal.string.regex": "ansibrightblack", # "#a45a77", + "pygments.literal.string.symbol": "ansiblue", # "#19177c", + "pygments.literal.string.other": "ansigreen", # "#008000", + "pygments.literal.number": "ansibrightblack", # "#666666", + "pygments.generic.heading": "bold ansiblue", # "bold #000080", + "pygments.generic.subheading": "bold ansimagenta", # "bold #800080", + "pygments.generic.deleted": "ansired", # "#a00000", + "pygments.generic.inserted": "ansigreen", # "#008400", + "pygments.generic.error": "ansigreen", # "#e40000", + "pygments.generic.emph": "italic", + "pygments.generic.strong": "bold", + "pygments.generic.emphstrong": "bold italic", + "pygments.generic.prompt": "bold ansiblue", # "bold #000080", + "pygments.generic.output": "ansibrightblack", # "#717171", + "pygments.generic.traceback": "ansiblue", # "#04d", + "pygments.error": "", # "border:#ff0000", +} + # Code style for Windows consoles. They support only 16 colors, # so we choose a combination that displays nicely. win32_code_style = { From ed597e1945535e4d84a2c628ba6e470917e883dc Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Mon, 17 Nov 2025 13:59:26 +0000 Subject: [PATCH 21/25] Fix string escaping error in config example. --- examples/ptpython_config/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ptpython_config/config.py b/examples/ptpython_config/config.py index 4efd5d97..fe8a9ae8 100644 --- a/examples/ptpython_config/config.py +++ b/examples/ptpython_config/config.py @@ -1,4 +1,4 @@ -""" +r""" Configuration example for ``ptpython``. Copy this file to $XDG_CONFIG_HOME/ptpython/config.py From 73a12553640280d01d576c080c589ec0fd099f05 Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Thu, 20 Nov 2025 21:03:24 +0000 Subject: [PATCH 22/25] Release 3.0.32. --- CHANGELOG | 12 +++++++++++- pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8a07ede2..838303f6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,13 +1,23 @@ CHANGELOG ========= +3.0.32: 2025-11-20 +------------------ + +Fixes: +- Use ANSI colors (instead of RGB colors) for the default code theme. This + improves the chances of having a better contrast with the background color, + because we now use what is configured in the terminal emulator. The theme is + called 'default-ansi' and exists alongside 'default'. + + 3.0.31: 2025-08-27 ------------------ Fixes: - Add `patch_stdout_raw` parameter to `embed()`. - Fix repl.min_brightness and repl.max_brightness config in history. -- Fix positioning of exit confirmation (compatibilitiy with latest prompt_toolkit). +- Fix positioning of exit confirmation (compatibility with latest prompt_toolkit). 3.0.30: 2025-04-15 diff --git a/pyproject.toml b/pyproject.toml index f1f3a853..cb6d1ed1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ptpython" -version = "3.0.31" +version = "3.0.32" description = "Python REPL build on top of prompt_toolkit" readme = "README.rst" authors = [{ name = "Jonathan Slenders" }] From 8fa5ddb3eb7842fd54484bac74b091c00fb804d5 Mon Sep 17 00:00:00 2001 From: Prescott Murphy Date: Mon, 22 Jul 2024 18:59:29 -0400 Subject: [PATCH 23/25] Pass style with meta completion text --- src/ptpython/completer.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ptpython/completer.py b/src/ptpython/completer.py index e8bab285..736756ab 100644 --- a/src/ptpython/completer.py +++ b/src/ptpython/completer.py @@ -285,12 +285,15 @@ def get_completions( if jc.type == "param": suffix = "..." + style = _get_style_for_jedi_completion(jc) + display_meta = jc.type if style == "" else [(style, jc.type)] + yield Completion( jc.name_with_symbols, len(jc.complete) - len(jc.name_with_symbols), display=jc.name_with_symbols + suffix, - display_meta=jc.type, - style=_get_style_for_jedi_completion(jc), + display_meta=display_meta, + style=style, ) From bed05014c34a01c41233481dea07213ac9dca80d Mon Sep 17 00:00:00 2001 From: Prescott Murphy Date: Mon, 22 Jul 2024 19:29:59 -0400 Subject: [PATCH 24/25] Use distinct style to make feature completely opt-in --- src/ptpython/completer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ptpython/completer.py b/src/ptpython/completer.py index 736756ab..40701cab 100644 --- a/src/ptpython/completer.py +++ b/src/ptpython/completer.py @@ -286,7 +286,9 @@ def get_completions( suffix = "..." style = _get_style_for_jedi_completion(jc) - display_meta = jc.type if style == "" else [(style, jc.type)] + display_meta = ( + jc.type if style == "" else [(f"{style}-meta", jc.type)] + ) yield Completion( jc.name_with_symbols, From dfe9dae32df17f6695aad6168ec271d02eb1232e Mon Sep 17 00:00:00 2001 From: Prescott Murphy Date: Thu, 20 Nov 2025 22:08:27 -0500 Subject: [PATCH 25/25] Add display meta styling classes to default style --- src/ptpython/style.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ptpython/style.py b/src/ptpython/style.py index 85219717..8242df31 100644 --- a/src/ptpython/style.py +++ b/src/ptpython/style.py @@ -137,6 +137,14 @@ def generate_style(python_style: BaseStyle, ui_style: BaseStyle) -> BaseStyle: "completion.keyword": "fg:#008800", "completion.keyword fuzzymatch.inside": "fg:#008800", "completion.keyword fuzzymatch.outside": "fg:#44aa44", + # Styling for the meta completion menu that displays the type of each + # completion, e.g. param, builtin, keyword to the right of the item. + "completion.param-meta": "fg:ansiblue", + "completion.param-meta fuzzymatch.inside.character": "fg:ansiblue", + "completion.builtin-meta": "fg:ansigreen", + "completion.builtin-meta fuzzymatch.inside.character": "fg:ansigreen", + "completion.keyword-meta": "fg:ansired", + "completion.keyword-meta fuzzymatch.inside.character": "fg:ansired", # Separator between windows. (Used above docstring.) "separator": "#bbbbbb", # System toolbar