diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c62bdc39..d6c83013 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,19 +10,19 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] + python-version: ["3.7", "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@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 + python -m pip install .[dev] pip list - name: Type Checker run: | @@ -31,7 +31,7 @@ jobs: ruff format --check . - name: Run Tests run: | - ./tests/run_tests.py + pytest - name: Validate README.md # Ensure that the README renders correctly (required for uploading to PyPI). run: | 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..d0f2d483 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/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 diff --git a/ptpython/layout.py b/ptpython/layout.py index 622df594..8f08aa38 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)) @@ -365,13 +365,9 @@ def get_text_fragments() -> StyleAndTextTuples: append((TB, " ")) # Position in history. - append( - ( - TB, - "%i/%i " - % (python_buffer.working_index + 1, len(python_buffer._working_lines)), - ) - ) + index = python_buffer.working_index + 1 + lines = len(python_buffer._working_lines) + append((TB, f"{index}/{lines} ")) # Shortcuts. app = get_app() @@ -492,8 +488,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/repl.py b/ptpython/repl.py index 6b60018e..aba23f51 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 diff --git a/pyproject.toml b/pyproject.toml index ce420372..4efffc20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,51 @@ +[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.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", +] +requires-python = ">=3.7" +dependencies = [ + "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", +] +dynamic = ["scripts"] + +[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.optional-dependencies] +ptipython = ["ipython"] # For ptipython, we need to have IPython +all = ["black"] # Black not always possible on PyPy +dev = ["mypy", "readme_renderer", "ruff", "pytest"] + +[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 +75,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. +"tests/test_todo.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 index aa101764..5ec91484 100644 --- a/setup.py +++ b/setup.py @@ -1,51 +1,8 @@ -#!/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() - +from setuptools import setup 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", @@ -59,9 +16,5 @@ *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/tests/run_tests.py b/tests/test_todo.py similarity index 72% rename from tests/run_tests.py rename to tests/test_todo.py index 0de37430..4e8ab37c 100755 --- a/tests/run_tests.py +++ b/tests/test_todo.py @@ -1,8 +1,5 @@ -#!/usr/bin/env python from __future__ import annotations -import unittest - import ptpython.completer import ptpython.eventloop import ptpython.filters @@ -16,9 +13,9 @@ import ptpython.validator # For now there are no tests here. -# However this is sufficient for Travis to do at least a syntax check. +# However this is sufficient 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() +def test_todo(): + pass