Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,26 @@ 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
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
ruff check .
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: |
Expand Down
2 changes: 1 addition & 1 deletion examples/asyncio-python-embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions examples/asyncio-ssh-python-embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
13 changes: 6 additions & 7 deletions ptpython/entry_points/run_ptpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,25 @@
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
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"]


__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(
Expand Down
12 changes: 5 additions & 7 deletions ptpython/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
]
)
Expand All @@ -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))

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)} ",
)
)

Expand Down Expand Up @@ -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", " "),
]
Expand Down
28 changes: 16 additions & 12 deletions ptpython/printer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import sys
import traceback
from dataclasses import dataclass
from enum import Enum
Expand Down Expand Up @@ -254,8 +253,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]
Expand Down Expand Up @@ -323,14 +321,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] == "<stdin>":
Expand All @@ -340,7 +344,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)

Expand Down
6 changes: 5 additions & 1 deletion ptpython/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
Expand Down
24 changes: 0 additions & 24 deletions tests/run_tests.py

This file was deleted.

31 changes: 31 additions & 0 deletions tests/test_dummy.py
Original file line number Diff line number Diff line change
@@ -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
Loading