From 635a85607c8e4541250faada40ebf343e9f18d12 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 16 Apr 2025 08:48:42 +0200 Subject: [PATCH] Adding upstream version 3.0.30. Signed-off-by: Daniel Baumann --- .github/workflows/test.yaml | 33 +++++---- CHANGELOG | 11 +++ examples/asyncio-python-embed.py | 2 +- examples/asyncio-ssh-python-embed.py | 4 +- examples/ssh-and-telnet-embed.py | 6 +- mypy.ini | 6 -- pyproject.toml | 72 +++++++++++++++++-- setup.cfg | 41 ----------- setup.py | 67 ----------------- {ptpython => src/ptpython}/__init__.py | 0 {ptpython => src/ptpython}/__main__.py | 0 {ptpython => src/ptpython}/completer.py | 0 .../ptpython}/contrib/__init__.py | 0 .../ptpython}/contrib/asyncssh_repl.py | 0 .../ptpython}/entry_points/__init__.py | 0 .../ptpython}/entry_points/run_ptipython.py | 0 .../ptpython}/entry_points/run_ptpython.py | 15 ++-- {ptpython => src/ptpython}/eventloop.py | 0 {ptpython => src/ptpython}/filters.py | 0 {ptpython => src/ptpython}/history_browser.py | 4 +- {ptpython => src/ptpython}/ipython.py | 0 {ptpython => src/ptpython}/key_bindings.py | 4 +- {ptpython => src/ptpython}/layout.py | 12 ++-- {ptpython => src/ptpython}/lexer.py | 0 {ptpython => src/ptpython}/printer.py | 26 ++++--- {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 | 49 ++++++++++++- {ptpython => src/ptpython}/signatures.py | 0 {ptpython => src/ptpython}/style.py | 0 {ptpython => src/ptpython}/utils.py | 0 {ptpython => src/ptpython}/validator.py | 0 tests/run_tests.py | 24 ------- tests/test_dummy.py | 31 ++++++++ 35 files changed, 212 insertions(+), 195 deletions(-) delete mode 100644 mypy.ini delete mode 100644 setup.cfg delete mode 100644 setup.py 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 (97%) rename {ptpython => src/ptpython}/eventloop.py (100%) rename {ptpython => src/ptpython}/filters.py (100%) rename {ptpython => src/ptpython}/history_browser.py (99%) rename {ptpython => src/ptpython}/ipython.py (100%) rename {ptpython => src/ptpython}/key_bindings.py (99%) rename {ptpython => src/ptpython}/layout.py (98%) rename {ptpython => src/ptpython}/lexer.py (100%) rename {ptpython => src/ptpython}/printer.py (95%) 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 (93%) 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%) 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 c62bdc3..d53bfcc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,29 +10,32 @@ 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 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} - - name: Install Dependencies + - name: Type Checking run: | - sudo apt remove python3-pip - python -m pip install --upgrade pip - python -m pip install . ruff mypy pytest readme_renderer - pip list - - name: Type Checker + uvx --with . mypy src/ptpython/ + uvx --with . mypy examples/ + - name: Code formatting + if: ${{ matrix.python-version == '3.13' }} run: | - mypy ptpython - ruff check . - ruff format --check . - - name: Run Tests + uvx ruff check . + uvx ruff format --check . + - name: Typos + if: ${{ matrix.python-version == '3.13' }} run: | - ./tests/run_tests.py + 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 python -m readme_renderer README.rst > /dev/null diff --git a/CHANGELOG b/CHANGELOG index bef7d07..7706260 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/examples/asyncio-python-embed.py b/examples/asyncio-python-embed.py index 38cc1c2..cb90973 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 9bbad86..bf79df7 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/examples/ssh-and-telnet-embed.py b/examples/ssh-and-telnet-embed.py index 62fa76d..2b293e6 100755 --- a/examples/ssh-and-telnet-embed.py +++ b/examples/ssh-and-telnet-embed.py @@ -6,6 +6,8 @@ Thanks to Vincent Michel for this! https://gist.github.com/vxgmichel/7685685b3e5ead04ada4a3ba75a48eef """ +from __future__ import annotations + import asyncio import pathlib @@ -15,7 +17,7 @@ from prompt_toolkit.contrib.ssh.server import ( 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/mypy.ini b/mypy.ini deleted file mode 100644 index 5a7ef2e..0000000 --- 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 diff --git a/pyproject.toml b/pyproject.toml index ce42037..00e2d5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,55 @@ +[project] +name = "ptpython" +version = "3.0.30" +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 = [ @@ -22,14 +74,22 @@ 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. -"tests/run_tests.py" = ["F401"] # Unused imports. +"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] 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"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 80dfec6..0000000 --- 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 aa10176..0000000 --- a/setup.py +++ /dev/null @@ -1,67 +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", - "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", - 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 :: 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 - }, -) 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 97% rename from ptpython/entry_points/run_ptpython.py rename to src/ptpython/entry_points/run_ptpython.py index 05df971..d083858 100644 --- a/ptpython/entry_points/run_ptpython.py +++ b/src/ptpython/entry_points/run_ptpython.py @@ -30,8 +30,9 @@ import asyncio 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 prompt_toolkit.shortcuts import print_formatted_text 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"] +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/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 99% rename from ptpython/history_browser.py rename to src/ptpython/history_browser.py index ae0ac03..72bc576 100644 --- a/ptpython/history_browser.py +++ b/src/ptpython/history_browser.py @@ -58,13 +58,15 @@ from ptpython.layout import get_inputmode_fragments 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/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 99% rename from ptpython/key_bindings.py rename to src/ptpython/key_bindings.py index d7bb575..48c5f5a 100644 --- a/ptpython/key_bindings.py +++ b/src/ptpython/key_bindings.py @@ -22,6 +22,8 @@ from prompt_toolkit.keys import Keys 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 @@ __all__ = [ "load_confirm_exit_bindings", ] -E = KeyPressEvent +E: TypeAlias = KeyPressEvent @Condition diff --git a/ptpython/layout.py b/src/ptpython/layout.py similarity index 98% rename from ptpython/layout.py rename to src/ptpython/layout.py index 622df59..9768598 100644 --- a/ptpython/layout.py +++ b/src/ptpython/layout.py @@ -108,7 +108,7 @@ def python_sidebar(python_input: PythonInput) -> Window: 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 python_sidebar(python_input: PythonInput) -> Window: 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 @@ class PythonPromptMargin(PromptMargin): 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 status_bar(python_input: PythonInput) -> Container: 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 show_sidebar_button_info(python_input: PythonInput) -> Container: ("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/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 95% rename from ptpython/printer.py rename to src/ptpython/printer.py index 85bd9c8..a3578de 100644 --- a/ptpython/printer.py +++ b/src/ptpython/printer.py @@ -1,6 +1,5 @@ from __future__ import annotations -import sys import traceback from dataclasses import dataclass from enum import Enum @@ -254,8 +253,7 @@ class OutputPrinter: 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 +321,20 @@ class OutputPrinter: 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() + 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", + ) - # 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)) + tblist = list(traceback.extract_tb(e.__traceback__)) for line_nr, tb_tuple in enumerate(tblist): if tb_tuple[0] == "": @@ -340,7 +344,7 @@ class OutputPrinter: 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/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 93% rename from ptpython/repl.py rename to src/ptpython/repl.py index 6b60018..469ed69 100644 --- a/ptpython/repl.py +++ b/src/ptpython/repl.py @@ -20,7 +20,17 @@ import types 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 @@ -362,7 +372,7 @@ class PythonRepl(PythonInput): 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 @@ -378,6 +388,10 @@ class PythonRepl(PythonInput): ) 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, @@ -501,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, @@ -511,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`. :: @@ -573,3 +615,4 @@ def embed( else: with patch_context: repl.run() + return None 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 diff --git a/tests/run_tests.py b/tests/run_tests.py deleted file mode 100755 index 0de3743..0000000 --- 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 0000000..922c6a3 --- /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