summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CHANGELOG7
-rw-r--r--README.rst26
-rwxr-xr-xexamples/asyncio-python-embed.py1
-rwxr-xr-xexamples/asyncio-ssh-python-embed.py1
-rw-r--r--examples/ptpython_config/config.py1
-rwxr-xr-xexamples/python-embed-with-custom-prompt.py1
-rwxr-xr-xexamples/python-embed.py4
-rwxr-xr-xexamples/python-input.py4
-rw-r--r--examples/test-cases/ptpython-in-other-thread.py1
-rw-r--r--ptpython/__main__.py1
-rw-r--r--ptpython/completer.py6
-rw-r--r--ptpython/contrib/asyncssh_repl.py1
-rw-r--r--ptpython/entry_points/run_ptpython.py1
-rw-r--r--ptpython/eventloop.py4
-rw-r--r--ptpython/history_browser.py3
-rw-r--r--ptpython/ipython.py10
-rw-r--r--ptpython/layout.py5
-rw-r--r--ptpython/prompt_style.py4
-rw-r--r--ptpython/python_input.py26
-rw-r--r--ptpython/repl.py18
-rw-r--r--ptpython/signatures.py1
-rw-r--r--ptpython/utils.py1
-rw-r--r--ptpython/validator.py2
-rw-r--r--pyproject.toml8
-rw-r--r--setup.py24
25 files changed, 105 insertions, 56 deletions
diff --git a/CHANGELOG b/CHANGELOG
index d873862..6f2bbb9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,13 @@
CHANGELOG
=========
+3.0.27: 2024-05-27
+------------------
+
+- Limit number of completions to 5k (for performance).
+- Several typing fixes.
+
+
3.0.26: 2024-02-06
------------------
diff --git a/README.rst b/README.rst
index 8ec9aca..06c1e02 100644
--- a/README.rst
+++ b/README.rst
@@ -12,7 +12,7 @@ ptpython
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/example1.png
Ptpython is an advanced Python REPL. It should work on all
-Python versions from 2.6 up to 3.9 and work cross platform (Linux,
+Python versions from 2.6 up to 3.11 and work cross platform (Linux,
BSD, OS X and Windows).
Note: this version of ptpython requires at least Python 3.6. Install ptpython
@@ -255,6 +255,22 @@ Windows. Some things might not work, but it is usable:
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/windows.png
+Windows terminal integration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are using the `Windows Terminal <https://aka.ms/terminal>`_ and want to
+integrate ``ptpython`` as a profile, go to *Settings -> Open JSON file* and add the
+following profile under *profiles.list*:
+
+.. code-block:: JSON
+
+ {
+ "commandline": "%SystemRoot%\\System32\\cmd.exe /k ptpython",
+ "guid": "{f91d49a3-741b-409c-8a15-c4360649121f}",
+ "hidden": false,
+ "icon": "https://upload.wikimedia.org/wikipedia/commons/e/e6/Python_Windows_interpreter_icon_2006%E2%80%932016_Tiny.png",
+ "name": "ptpython@cmd"
+ }
FAQ
***
@@ -288,12 +304,12 @@ Special thanks to
- `wcwidth <https://github.com/jquast/wcwidth>`_: Determine columns needed for a wide characters.
- `prompt_toolkit <http://github.com/jonathanslenders/python-prompt-toolkit>`_ for the interface.
-.. |Build Status| image:: https://api.travis-ci.org/prompt-toolkit/ptpython.svg?branch=master
- :target: https://travis-ci.org/prompt-toolkit/ptpython#
+.. |Build Status| image:: https://github.com/prompt-toolkit/ptpython/actions/workflows/test.yaml/badge.svg
+ :target: https://github.com/prompt-toolkit/ptpython/actions/workflows/test.yaml
.. |License| image:: https://img.shields.io/github/license/prompt-toolkit/ptpython.svg
:target: https://github.com/prompt-toolkit/ptpython/blob/master/LICENSE
-.. |PyPI| image:: https://pypip.in/version/ptpython/badge.svg
- :target: https://pypi.python.org/pypi/ptpython/
+.. |PyPI| image:: https://img.shields.io/pypi/v/ptpython.svg
+ :target: https://pypi.org/project/ptpython/
:alt: Latest Version
diff --git a/examples/asyncio-python-embed.py b/examples/asyncio-python-embed.py
index a8fbba5..38cc1c2 100755
--- a/examples/asyncio-python-embed.py
+++ b/examples/asyncio-python-embed.py
@@ -11,6 +11,7 @@ The ``patch_stdout`` option makes sure that when another coroutine is writing
to stdout, it won't break the input line, but instead writes nicely above the
prompt.
"""
+
import asyncio
from ptpython.repl import embed
diff --git a/examples/asyncio-ssh-python-embed.py b/examples/asyncio-ssh-python-embed.py
index be0689e..9bbad86 100755
--- a/examples/asyncio-ssh-python-embed.py
+++ b/examples/asyncio-ssh-python-embed.py
@@ -5,6 +5,7 @@ This requires Python 3, asyncio and asyncssh.
Run this example and then SSH to localhost, port 8222.
"""
+
import asyncio
import logging
diff --git a/examples/ptpython_config/config.py b/examples/ptpython_config/config.py
index b25850a..bfd3914 100644
--- a/examples/ptpython_config/config.py
+++ b/examples/ptpython_config/config.py
@@ -5,6 +5,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
"""
+
from prompt_toolkit.filters import ViInsertMode
from prompt_toolkit.key_binding.key_processor import KeyPress
from prompt_toolkit.keys import Keys
diff --git a/examples/python-embed-with-custom-prompt.py b/examples/python-embed-with-custom-prompt.py
index d54da1d..5e8c707 100755
--- a/examples/python-embed-with-custom-prompt.py
+++ b/examples/python-embed-with-custom-prompt.py
@@ -2,6 +2,7 @@
"""
Example of embedding a Python REPL, and setting a custom prompt.
"""
+
from prompt_toolkit.formatted_text import HTML, AnyFormattedText
from ptpython.prompt_style import PromptStyle
diff --git a/examples/python-embed.py b/examples/python-embed.py
index 49224ac..a748101 100755
--- a/examples/python-embed.py
+++ b/examples/python-embed.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-"""
-"""
+""" """
+
from ptpython.repl import embed
diff --git a/examples/python-input.py b/examples/python-input.py
index 567c2ee..d586d0f 100755
--- a/examples/python-input.py
+++ b/examples/python-input.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-"""
-"""
+""" """
+
from ptpython.python_input import PythonInput
diff --git a/examples/test-cases/ptpython-in-other-thread.py b/examples/test-cases/ptpython-in-other-thread.py
index 7c78846..bfe1410 100644
--- a/examples/test-cases/ptpython-in-other-thread.py
+++ b/examples/test-cases/ptpython-in-other-thread.py
@@ -5,6 +5,7 @@ Example of running ptpython in another thread.
(For testing whether it's working fine if it's not embedded in the main
thread.)
"""
+
import threading
from ptpython.repl import embed
diff --git a/ptpython/__main__.py b/ptpython/__main__.py
index c006261..3a2f7dd 100644
--- a/ptpython/__main__.py
+++ b/ptpython/__main__.py
@@ -1,6 +1,7 @@
"""
Make `python -m ptpython` an alias for running `./ptpython`.
"""
+
from __future__ import annotations
from .entry_points.run_ptpython import run
diff --git a/ptpython/completer.py b/ptpython/completer.py
index 91d6647..264918e 100644
--- a/ptpython/completer.py
+++ b/ptpython/completer.py
@@ -6,6 +6,7 @@ import inspect
import keyword
import re
from enum import Enum
+from itertools import islice
from typing import TYPE_CHECKING, Any, Callable, Iterable
from prompt_toolkit.completion import (
@@ -617,7 +618,10 @@ class HidePrivateCompleter(Completer):
def get_completions(
self, document: Document, complete_event: CompleteEvent
) -> Iterable[Completion]:
- completions = list(self.completer.get_completions(document, complete_event))
+ completions = list(
+ # Limit at 5k completions for performance.
+ islice(self.completer.get_completions(document, complete_event), 0, 5000)
+ )
complete_private_attributes = self.complete_private_attributes()
hide_private = False
diff --git a/ptpython/contrib/asyncssh_repl.py b/ptpython/contrib/asyncssh_repl.py
index 2f74eb2..a86737b 100644
--- a/ptpython/contrib/asyncssh_repl.py
+++ b/ptpython/contrib/asyncssh_repl.py
@@ -6,6 +6,7 @@ Note that the code in this file is Python 3 only. However, we
should make sure not to use Python 3-only syntax, because this
package should be installable in Python 2 as well!
"""
+
from __future__ import annotations
import asyncio
diff --git a/ptpython/entry_points/run_ptpython.py b/ptpython/entry_points/run_ptpython.py
index 1d4a532..05df971 100644
--- a/ptpython/entry_points/run_ptpython.py
+++ b/ptpython/entry_points/run_ptpython.py
@@ -22,6 +22,7 @@ environment variables:
PTPYTHON_CONFIG_HOME: a configuration directory to use
PYTHONSTARTUP: file executed on interactive startup (no default)
"""
+
from __future__ import annotations
import argparse
diff --git a/ptpython/eventloop.py b/ptpython/eventloop.py
index 14ab64b..a646274 100644
--- a/ptpython/eventloop.py
+++ b/ptpython/eventloop.py
@@ -7,6 +7,7 @@ way we don't block the UI of for instance ``turtle`` and other Tk libraries.
in readline. ``prompt-toolkit`` doesn't understand that input hook, but this
will fix it for Tk.)
"""
+
from __future__ import annotations
import sys
@@ -23,9 +24,8 @@ def _inputhook_tk(inputhook_context: InputHookContext) -> None:
Run the Tk eventloop until prompt-toolkit needs to process the next input.
"""
# Get the current TK application.
- import tkinter
-
import _tkinter # Keep this imports inline!
+ import tkinter
root = tkinter._default_root # type: ignore
diff --git a/ptpython/history_browser.py b/ptpython/history_browser.py
index b667be1..ae0ac03 100644
--- a/ptpython/history_browser.py
+++ b/ptpython/history_browser.py
@@ -4,6 +4,7 @@ Utility to easily select lines from the history and execute them again.
`create_history_application` creates an `Application` instance that runs will
run as a sub application of the Repl/PythonInput.
"""
+
from __future__ import annotations
from functools import partial
@@ -410,7 +411,7 @@ class HistoryMapping:
if len(history_strings) > HISTORY_COUNT:
history_lines[0] = (
- "# *** History has been truncated to %s lines ***" % HISTORY_COUNT
+ f"# *** History has been truncated to {HISTORY_COUNT} lines ***"
)
self.history_lines = history_lines
diff --git a/ptpython/ipython.py b/ptpython/ipython.py
index ad0516a..0692214 100644
--- a/ptpython/ipython.py
+++ b/ptpython/ipython.py
@@ -8,6 +8,7 @@ also the power of for instance all the %-magic functions that IPython has to
offer.
"""
+
from __future__ import annotations
from typing import Iterable
@@ -156,7 +157,7 @@ class MagicsCompleter(Completer):
for m in sorted(self.magics_manager.magics["line"]):
if m.startswith(text):
- yield Completion("%s" % m, -len(text))
+ yield Completion(f"{m}", -len(text))
class AliasCompleter(Completer):
@@ -172,7 +173,7 @@ class AliasCompleter(Completer):
for a, cmd in sorted(aliases, key=lambda a: a[0]):
if a.startswith(text):
- yield Completion("%s" % a, -len(text), display_meta=cmd)
+ yield Completion(f"{a}", -len(text), display_meta=cmd)
class IPythonInput(PythonInput):
@@ -279,9 +280,8 @@ def initialize_extensions(shell, extensions):
shell.extension_manager.load_extension(ext)
except:
warn(
- "Error in loading extension: %s" % ext
- + "\nCheck your config files in %s"
- % ipy_utils.path.get_ipython_dir()
+ f"Error in loading extension: {ext}"
+ + f"\nCheck your config files in {ipy_utils.path.get_ipython_dir()}"
)
shell.showtraceback()
diff --git a/ptpython/layout.py b/ptpython/layout.py
index 2c1ec15..622df59 100644
--- a/ptpython/layout.py
+++ b/ptpython/layout.py
@@ -1,6 +1,7 @@
"""
Creation of the `Layout` instance for the Python input/REPL.
"""
+
from __future__ import annotations
import platform
@@ -131,7 +132,7 @@ def python_sidebar(python_input: PythonInput) -> Window:
tokens.append(("class:sidebar" + sel, " >" if selected else " "))
tokens.append(("class:sidebar.label" + sel, "%-24s" % label, select_item))
tokens.append(("class:sidebar.status" + sel, " ", select_item))
- tokens.append(("class:sidebar.status" + sel, "%s" % status, goto_next))
+ tokens.append(("class:sidebar.status" + sel, f"{status}", goto_next))
if selected:
tokens.append(("[SetCursorPosition]", ""))
@@ -528,7 +529,7 @@ def create_exit_confirmation(
def get_text_fragments() -> StyleAndTextTuples:
# Show "Do you really want to exit?"
return [
- (style, "\n %s ([y]/n) " % python_input.exit_message),
+ (style, f"\n {python_input.exit_message} ([y]/n) "),
("[SetCursorPosition]", ""),
(style, " \n"),
]
diff --git a/ptpython/prompt_style.py b/ptpython/prompt_style.py
index 96b738f..465c3db 100644
--- a/ptpython/prompt_style.py
+++ b/ptpython/prompt_style.py
@@ -48,7 +48,7 @@ class IPythonPrompt(PromptStyle):
def in_prompt(self) -> AnyFormattedText:
return [
("class:in", "In ["),
- ("class:in.number", "%s" % self.python_input.current_statement_index),
+ ("class:in.number", f"{self.python_input.current_statement_index}"),
("class:in", "]: "),
]
@@ -58,7 +58,7 @@ class IPythonPrompt(PromptStyle):
def out_prompt(self) -> AnyFormattedText:
return [
("class:out", "Out["),
- ("class:out.number", "%s" % self.python_input.current_statement_index),
+ ("class:out.number", f"{self.python_input.current_statement_index}"),
("class:out", "]:"),
("", " "),
]
diff --git a/ptpython/python_input.py b/ptpython/python_input.py
index 54ddbef..975d3d9 100644
--- a/ptpython/python_input.py
+++ b/ptpython/python_input.py
@@ -2,6 +2,7 @@
Application for reading Python input.
This can be used for creation of Python REPLs.
"""
+
from __future__ import annotations
from asyncio import get_running_loop
@@ -98,8 +99,7 @@ if TYPE_CHECKING:
class _SupportsLessThan(Protocol):
# Taken from typeshed. _T_lt is used by "sorted", which needs anything
# sortable.
- def __lt__(self, __other: Any) -> bool:
- ...
+ def __lt__(self, __other: Any) -> bool: ...
_T_lt = TypeVar("_T_lt", bound="_SupportsLessThan")
@@ -347,14 +347,6 @@ class PythonInput:
"classic": ClassicPrompt(),
}
- self.get_input_prompt = lambda: self.all_prompt_styles[
- self.prompt_style
- ].in_prompt()
-
- self.get_output_prompt = lambda: self.all_prompt_styles[
- self.prompt_style
- ].out_prompt()
-
#: Load styles.
self.code_styles: dict[str, BaseStyle] = get_all_code_styles()
self.ui_styles = get_all_ui_styles()
@@ -425,6 +417,12 @@ class PythonInput:
else:
self._app = None
+ def get_input_prompt(self) -> AnyFormattedText:
+ return self.all_prompt_styles[self.prompt_style].in_prompt()
+
+ def get_output_prompt(self) -> AnyFormattedText:
+ return self.all_prompt_styles[self.prompt_style].out_prompt()
+
def _accept_handler(self, buff: Buffer) -> bool:
app = get_app()
app.exit(result=buff.text)
@@ -880,18 +878,18 @@ class PythonInput:
Option(
title="Min brightness",
description="Minimum brightness for the color scheme (default=0.0).",
- get_current_value=lambda: "%.2f" % self.min_brightness,
+ get_current_value=lambda: f"{self.min_brightness:.2f}",
get_values=lambda: {
- "%.2f" % value: partial(self._set_min_brightness, value)
+ f"{value:.2f}": partial(self._set_min_brightness, value)
for value in brightness_values
},
),
Option(
title="Max brightness",
description="Maximum brightness for the color scheme (default=1.0).",
- get_current_value=lambda: "%.2f" % self.max_brightness,
+ get_current_value=lambda: f"{self.max_brightness:.2f}",
get_values=lambda: {
- "%.2f" % value: partial(self._set_max_brightness, value)
+ f"{value:.2f}": partial(self._set_max_brightness, value)
for value in brightness_values
},
),
diff --git a/ptpython/repl.py b/ptpython/repl.py
index fc9b9da..ea2d84f 100644
--- a/ptpython/repl.py
+++ b/ptpython/repl.py
@@ -7,6 +7,7 @@ Utility for creating a Python repl.
embed(globals(), locals(), vi_mode=False)
"""
+
from __future__ import annotations
import asyncio
@@ -18,7 +19,8 @@ import traceback
import types
import warnings
from dis import COMPILER_FLAG_NAMES
-from typing import Any, Callable, ContextManager, Iterable
+from pathlib import Path
+from typing import Any, Callable, ContextManager, Iterable, Sequence
from prompt_toolkit.formatted_text import OneStyleAndTextTuple
from prompt_toolkit.patch_stdout import patch_stdout as patch_stdout_context
@@ -63,7 +65,7 @@ def _has_coroutine_flag(code: types.CodeType) -> bool:
class PythonRepl(PythonInput):
def __init__(self, *a, **kw) -> None:
- self._startup_paths = kw.pop("startup_paths", None)
+ self._startup_paths: Sequence[str | Path] | None = kw.pop("startup_paths", None)
super().__init__(*a, **kw)
self._load_start_paths()
@@ -347,7 +349,7 @@ class PythonRepl(PythonInput):
def get_compiler_flags(self) -> int:
return super().get_compiler_flags() | PyCF_ALLOW_TOP_LEVEL_AWAIT
- def _compile_with_flags(self, code: str, mode: str):
+ def _compile_with_flags(self, code: str, mode: str) -> Any:
"Compile code with the right compiler flags."
return compile(
code,
@@ -458,13 +460,13 @@ def run_config(repl: PythonInput, config_file: str | None = None) -> None:
def embed(
- globals=None,
- locals=None,
+ globals: dict[str, Any] | None = None,
+ locals: dict[str, Any] | None = None,
configure: Callable[[PythonRepl], None] | None = None,
vi_mode: bool = False,
history_filename: str | None = None,
title: str | None = None,
- startup_paths=None,
+ startup_paths: Sequence[str | Path] | None = None,
patch_stdout: bool = False,
return_asyncio_coroutine: bool = False,
) -> None:
@@ -493,10 +495,10 @@ def embed(
locals = locals or globals
- def get_globals():
+ def get_globals() -> dict[str, Any]:
return globals
- def get_locals():
+ def get_locals() -> dict[str, Any]:
return locals
# Create REPL.
diff --git a/ptpython/signatures.py b/ptpython/signatures.py
index d4cb98c..b3e5c91 100644
--- a/ptpython/signatures.py
+++ b/ptpython/signatures.py
@@ -5,6 +5,7 @@ editing.
Either with the Jedi library, or using `inspect.signature` if Jedi fails and we
can use `eval()` to evaluate the function object.
"""
+
from __future__ import annotations
import inspect
diff --git a/ptpython/utils.py b/ptpython/utils.py
index 28887d2..92cfc2a 100644
--- a/ptpython/utils.py
+++ b/ptpython/utils.py
@@ -1,6 +1,7 @@
"""
For internal use only.
"""
+
from __future__ import annotations
import re
diff --git a/ptpython/validator.py b/ptpython/validator.py
index 91b9c28..cf2ee54 100644
--- a/ptpython/validator.py
+++ b/ptpython/validator.py
@@ -59,4 +59,4 @@ class PythonValidator(Validator):
except ValueError as e:
# In Python 2, compiling "\x9" (an invalid escape sequence) raises
# ValueError instead of SyntaxError.
- raise ValidationError(0, "Syntax Error: %s" % e)
+ raise ValidationError(0, f"Syntax Error: {e}")
diff --git a/pyproject.toml b/pyproject.toml
index 5421c45..ce42037 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.ruff]
target-version = "py37"
-select = [
+lint.select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
@@ -12,14 +12,14 @@ select = [
"RUF100", # unused-noqa
"Q", # quotes
]
-ignore = [
+lint.ignore = [
"E501", # Line too long, handled by black
"C901", # Too complex
"E722", # bare except.
]
-[tool.ruff.per-file-ignores]
+[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.
@@ -30,6 +30,6 @@ ignore = [
"tests/run_tests.py" = ["F401"] # Unused imports.
-[tool.ruff.isort]
+[tool.ruff.lint.isort]
known-first-party = ["ptpython"]
known-third-party = ["prompt_toolkit", "pygments", "asyncssh"]
diff --git a/setup.py b/setup.py
index a54da35..84f18be 100644
--- a/setup.py
+++ b/setup.py
@@ -11,18 +11,26 @@ with open(os.path.join(os.path.dirname(__file__), "README.rst")) as f:
setup(
name="ptpython",
author="Jonathan Slenders",
- version="3.0.26",
+ version="3.0.27",
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.34, because of `OneStyleAndTextTuple` import.
- "prompt_toolkit>=3.0.34,<3.1.0",
+ # Use prompt_toolkit 3.0.43, because of `OneStyleAndTextTuple` import.
+ "prompt_toolkit>=3.0.43,<3.1.0",
"pygments",
],
python_requires=">=3.7",
@@ -30,8 +38,11 @@ setup(
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
- "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",
],
@@ -39,12 +50,11 @@ setup(
"console_scripts": [
"ptpython = ptpython.entry_points.run_ptpython:run",
"ptipython = ptpython.entry_points.run_ptipython:run",
- "ptpython%s = ptpython.entry_points.run_ptpython:run" % sys.version_info[0],
+ f"ptpython{sys.version_info[0]} = ptpython.entry_points.run_ptpython:run",
"ptpython{}.{} = ptpython.entry_points.run_ptpython:run".format(
*sys.version_info[:2]
),
- "ptipython%s = ptpython.entry_points.run_ptipython:run"
- % sys.version_info[0],
+ f"ptipython{sys.version_info[0]} = ptpython.entry_points.run_ptipython:run",
"ptipython{}.{} = ptpython.entry_points.run_ptipython:run".format(
*sys.version_info[:2]
),