Adding upstream version 3.0.19.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
0014608abc
commit
6e91d72088
14 changed files with 272 additions and 190 deletions
1
.github/workflows/test.yaml
vendored
1
.github/workflows/test.yaml
vendored
|
@ -23,6 +23,7 @@ jobs:
|
||||||
sudo apt remove python3-pip
|
sudo apt remove python3-pip
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install . black isort mypy pytest readme_renderer
|
python -m pip install . black isort mypy pytest readme_renderer
|
||||||
|
python -m pip install . types-dataclasses # Needed for Python 3.6
|
||||||
pip list
|
pip list
|
||||||
- name: Type Checker
|
- name: Type Checker
|
||||||
run: |
|
run: |
|
||||||
|
|
25
CHANGELOG
25
CHANGELOG
|
@ -1,6 +1,31 @@
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
3.0.19: 2020-07-08
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- Fix handling of `SystemExit` (fixes "ValueError: I/O operation on closed
|
||||||
|
file").
|
||||||
|
- Allow usage of `await` in assignment expressions or for-loops.
|
||||||
|
|
||||||
|
|
||||||
|
3.0.18: 2020-06-26
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- Made "black" an optional dependency.
|
||||||
|
|
||||||
|
|
||||||
|
3.0.17: 2020-03-22
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- Fix leaking file descriptors due to not closing the asyncio event loop after
|
||||||
|
reading input in a thread.
|
||||||
|
- Fix race condition during retrieval of signatures.
|
||||||
|
|
||||||
|
|
||||||
3.0.16: 2020-02-11
|
3.0.16: 2020-02-11
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ def configure(repl):
|
||||||
@repl.add_key_binding("j", "j", filter=ViInsertMode())
|
@repl.add_key_binding("j", "j", filter=ViInsertMode())
|
||||||
def _(event):
|
def _(event):
|
||||||
" Map 'jj' to Escape. "
|
" Map 'jj' to Escape. "
|
||||||
event.cli.key_processor.feed(KeyPress("escape"))
|
event.cli.key_processor.feed(KeyPress(Keys("escape")))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Custom key binding for some simple autocorrection while typing.
|
# Custom key binding for some simple autocorrection while typing.
|
||||||
|
|
|
@ -468,7 +468,7 @@ class DictionaryCompleter(Completer):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def abbr_meta(text: str) -> str:
|
def abbr_meta(text: str) -> str:
|
||||||
" Abbreviate meta text, make sure it fits on one line. "
|
"Abbreviate meta text, make sure it fits on one line."
|
||||||
# Take first line, if multiple lines.
|
# Take first line, if multiple lines.
|
||||||
if len(text) > 20:
|
if len(text) > 20:
|
||||||
text = text[:20] + "..."
|
text = text[:20] + "..."
|
||||||
|
@ -621,7 +621,7 @@ class HidePrivateCompleter(Completer):
|
||||||
|
|
||||||
|
|
||||||
class ReprFailedError(Exception):
|
class ReprFailedError(Exception):
|
||||||
" Raised when the repr() call in `DictionaryCompleter` fails. "
|
"Raised when the repr() call in `DictionaryCompleter` fails."
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -31,7 +31,7 @@ def run(user_ns=None):
|
||||||
path = a.args[0]
|
path = a.args[0]
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
code = compile(f.read(), path, "exec")
|
code = compile(f.read(), path, "exec")
|
||||||
exec(code, {})
|
exec(code, {"__name__": "__main__", "__file__": path})
|
||||||
else:
|
else:
|
||||||
enable_deprecation_warnings()
|
enable_deprecation_warnings()
|
||||||
|
|
||||||
|
|
|
@ -179,9 +179,11 @@ def run() -> None:
|
||||||
path = a.args[0]
|
path = a.args[0]
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
code = compile(f.read(), path, "exec")
|
code = compile(f.read(), path, "exec")
|
||||||
# NOTE: We have to pass an empty dictionary as namespace. Omitting
|
# NOTE: We have to pass a dict as namespace. Omitting this argument
|
||||||
# this argument causes imports to not be found. See issue #326.
|
# causes imports to not be found. See issue #326.
|
||||||
exec(code, {})
|
# However, an empty dict sets __name__ to 'builtins', which
|
||||||
|
# breaks `if __name__ == '__main__'` checks. See issue #444.
|
||||||
|
exec(code, {"__name__": "__main__", "__file__": path})
|
||||||
|
|
||||||
# Run interactive shell.
|
# Run interactive shell.
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -85,7 +85,7 @@ Further, remember that searching works like in Emacs
|
||||||
|
|
||||||
|
|
||||||
class BORDER:
|
class BORDER:
|
||||||
" Box drawing characters. "
|
"Box drawing characters."
|
||||||
HORIZONTAL = "\u2501"
|
HORIZONTAL = "\u2501"
|
||||||
VERTICAL = "\u2503"
|
VERTICAL = "\u2503"
|
||||||
TOP_LEFT = "\u250f"
|
TOP_LEFT = "\u250f"
|
||||||
|
@ -420,7 +420,7 @@ class HistoryMapping:
|
||||||
|
|
||||||
|
|
||||||
def _toggle_help(history):
|
def _toggle_help(history):
|
||||||
" Display/hide help. "
|
"Display/hide help."
|
||||||
help_buffer_control = history.history_layout.help_buffer_control
|
help_buffer_control = history.history_layout.help_buffer_control
|
||||||
|
|
||||||
if history.app.layout.current_control == help_buffer_control:
|
if history.app.layout.current_control == help_buffer_control:
|
||||||
|
@ -430,7 +430,7 @@ def _toggle_help(history):
|
||||||
|
|
||||||
|
|
||||||
def _select_other_window(history):
|
def _select_other_window(history):
|
||||||
" Toggle focus between left/right window. "
|
"Toggle focus between left/right window."
|
||||||
current_buffer = history.app.current_buffer
|
current_buffer = history.app.current_buffer
|
||||||
layout = history.history_layout.layout
|
layout = history.history_layout.layout
|
||||||
|
|
||||||
|
@ -513,17 +513,17 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
# Eager: ignore the Emacs [Ctrl-X Ctrl-X] binding.
|
# Eager: ignore the Emacs [Ctrl-X Ctrl-X] binding.
|
||||||
@handle("c-w", filter=main_buffer_focussed)
|
@handle("c-w", filter=main_buffer_focussed)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Select other window. "
|
"Select other window."
|
||||||
_select_other_window(history)
|
_select_other_window(history)
|
||||||
|
|
||||||
@handle("f4")
|
@handle("f4")
|
||||||
def _(event):
|
def _(event):
|
||||||
" Switch between Emacs/Vi mode. "
|
"Switch between Emacs/Vi mode."
|
||||||
python_input.vi_mode = not python_input.vi_mode
|
python_input.vi_mode = not python_input.vi_mode
|
||||||
|
|
||||||
@handle("f1")
|
@handle("f1")
|
||||||
def _(event):
|
def _(event):
|
||||||
" Display/hide help. "
|
"Display/hide help."
|
||||||
_toggle_help(history)
|
_toggle_help(history)
|
||||||
|
|
||||||
@handle("enter", filter=help_focussed)
|
@handle("enter", filter=help_focussed)
|
||||||
|
@ -531,7 +531,7 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
@handle("c-g", filter=help_focussed)
|
@handle("c-g", filter=help_focussed)
|
||||||
@handle("escape", filter=help_focussed)
|
@handle("escape", filter=help_focussed)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Leave help. "
|
"Leave help."
|
||||||
event.app.layout.focus_previous()
|
event.app.layout.focus_previous()
|
||||||
|
|
||||||
@handle("q", filter=main_buffer_focussed)
|
@handle("q", filter=main_buffer_focussed)
|
||||||
|
@ -539,19 +539,19 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
@handle("c-c", filter=main_buffer_focussed)
|
@handle("c-c", filter=main_buffer_focussed)
|
||||||
@handle("c-g", filter=main_buffer_focussed)
|
@handle("c-g", filter=main_buffer_focussed)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Cancel and go back. "
|
"Cancel and go back."
|
||||||
event.app.exit(result=None)
|
event.app.exit(result=None)
|
||||||
|
|
||||||
@handle("enter", filter=main_buffer_focussed)
|
@handle("enter", filter=main_buffer_focussed)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Accept input. "
|
"Accept input."
|
||||||
event.app.exit(result=history.default_buffer.text)
|
event.app.exit(result=history.default_buffer.text)
|
||||||
|
|
||||||
enable_system_bindings = Condition(lambda: python_input.enable_system_bindings)
|
enable_system_bindings = Condition(lambda: python_input.enable_system_bindings)
|
||||||
|
|
||||||
@handle("c-z", filter=enable_system_bindings)
|
@handle("c-z", filter=enable_system_bindings)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Suspend to background. "
|
"Suspend to background."
|
||||||
event.app.suspend_to_background()
|
event.app.suspend_to_background()
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
|
@ -630,7 +630,7 @@ class PythonHistory:
|
||||||
)
|
)
|
||||||
|
|
||||||
def _history_buffer_pos_changed(self, _):
|
def _history_buffer_pos_changed(self, _):
|
||||||
""" When the cursor changes in the history buffer. Synchronize. """
|
"""When the cursor changes in the history buffer. Synchronize."""
|
||||||
# Only when this buffer has the focus.
|
# Only when this buffer has the focus.
|
||||||
if self.app.current_buffer == self.history_buffer:
|
if self.app.current_buffer == self.history_buffer:
|
||||||
line_no = self.history_buffer.document.cursor_position_row
|
line_no = self.history_buffer.document.cursor_position_row
|
||||||
|
|
|
@ -282,4 +282,21 @@ def embed(**kwargs):
|
||||||
kwargs["config"] = config
|
kwargs["config"] = config
|
||||||
shell = InteractiveShellEmbed.instance(**kwargs)
|
shell = InteractiveShellEmbed.instance(**kwargs)
|
||||||
initialize_extensions(shell, config["InteractiveShellApp"]["extensions"])
|
initialize_extensions(shell, config["InteractiveShellApp"]["extensions"])
|
||||||
|
run_startup_scripts(shell)
|
||||||
shell(header=header, stack_depth=2, compile_flags=compile_flags)
|
shell(header=header, stack_depth=2, compile_flags=compile_flags)
|
||||||
|
|
||||||
|
|
||||||
|
def run_startup_scripts(shell):
|
||||||
|
"""
|
||||||
|
Contributed by linyuxu:
|
||||||
|
https://github.com/prompt-toolkit/ptpython/issues/126#issue-161242480
|
||||||
|
"""
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
startup_dir = shell.profile_dir.startup_dir
|
||||||
|
startup_files = []
|
||||||
|
startup_files += glob.glob(os.path.join(startup_dir, "*.py"))
|
||||||
|
startup_files += glob.glob(os.path.join(startup_dir, "*.ipy"))
|
||||||
|
for file in startup_files:
|
||||||
|
shell.run_cell(open(file).read())
|
||||||
|
|
|
@ -203,7 +203,7 @@ def load_python_bindings(python_input):
|
||||||
|
|
||||||
@handle("c-c", filter=has_focus(python_input.default_buffer))
|
@handle("c-c", filter=has_focus(python_input.default_buffer))
|
||||||
def _(event):
|
def _(event):
|
||||||
" Abort when Control-C has been pressed. "
|
"Abort when Control-C has been pressed."
|
||||||
event.app.exit(exception=KeyboardInterrupt, style="class:aborting")
|
event.app.exit(exception=KeyboardInterrupt, style="class:aborting")
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
|
@ -222,7 +222,7 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("c-p", filter=sidebar_visible)
|
@handle("c-p", filter=sidebar_visible)
|
||||||
@handle("k", filter=sidebar_visible)
|
@handle("k", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Go to previous option. "
|
"Go to previous option."
|
||||||
python_input.selected_option_index = (
|
python_input.selected_option_index = (
|
||||||
python_input.selected_option_index - 1
|
python_input.selected_option_index - 1
|
||||||
) % python_input.option_count
|
) % python_input.option_count
|
||||||
|
@ -231,7 +231,7 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("c-n", filter=sidebar_visible)
|
@handle("c-n", filter=sidebar_visible)
|
||||||
@handle("j", filter=sidebar_visible)
|
@handle("j", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Go to next option. "
|
"Go to next option."
|
||||||
python_input.selected_option_index = (
|
python_input.selected_option_index = (
|
||||||
python_input.selected_option_index + 1
|
python_input.selected_option_index + 1
|
||||||
) % python_input.option_count
|
) % python_input.option_count
|
||||||
|
@ -240,14 +240,14 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("l", filter=sidebar_visible)
|
@handle("l", filter=sidebar_visible)
|
||||||
@handle(" ", filter=sidebar_visible)
|
@handle(" ", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Select next value for current option. "
|
"Select next value for current option."
|
||||||
option = python_input.selected_option
|
option = python_input.selected_option
|
||||||
option.activate_next()
|
option.activate_next()
|
||||||
|
|
||||||
@handle("left", filter=sidebar_visible)
|
@handle("left", filter=sidebar_visible)
|
||||||
@handle("h", filter=sidebar_visible)
|
@handle("h", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Select previous value for current option. "
|
"Select previous value for current option."
|
||||||
option = python_input.selected_option
|
option = python_input.selected_option
|
||||||
option.activate_previous()
|
option.activate_previous()
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("enter", filter=sidebar_visible)
|
@handle("enter", filter=sidebar_visible)
|
||||||
@handle("escape", filter=sidebar_visible)
|
@handle("escape", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event):
|
||||||
" Hide sidebar. "
|
"Hide sidebar."
|
||||||
python_input.show_sidebar = False
|
python_input.show_sidebar = False
|
||||||
event.app.layout.focus_last()
|
event.app.layout.focus_last()
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ __all__ = ["PtPythonLayout", "CompletionVisualisation"]
|
||||||
|
|
||||||
|
|
||||||
class CompletionVisualisation(Enum):
|
class CompletionVisualisation(Enum):
|
||||||
" Visualisation method for the completions. "
|
"Visualisation method for the completions."
|
||||||
NONE = "none"
|
NONE = "none"
|
||||||
POP_UP = "pop-up"
|
POP_UP = "pop-up"
|
||||||
MULTI_COLUMN = "multi-column"
|
MULTI_COLUMN = "multi-column"
|
||||||
|
@ -116,7 +116,7 @@ def python_sidebar(python_input: "PythonInput") -> Window:
|
||||||
|
|
||||||
@if_mousedown
|
@if_mousedown
|
||||||
def goto_next(mouse_event: MouseEvent) -> None:
|
def goto_next(mouse_event: MouseEvent) -> None:
|
||||||
" Select item and go to next value. "
|
"Select item and go to next value."
|
||||||
python_input.selected_option_index = index
|
python_input.selected_option_index = index
|
||||||
option = python_input.selected_option
|
option = python_input.selected_option
|
||||||
option.activate_next()
|
option.activate_next()
|
||||||
|
@ -472,7 +472,7 @@ def show_sidebar_button_info(python_input: "PythonInput") -> Container:
|
||||||
|
|
||||||
@if_mousedown
|
@if_mousedown
|
||||||
def toggle_sidebar(mouse_event: MouseEvent) -> None:
|
def toggle_sidebar(mouse_event: MouseEvent) -> None:
|
||||||
" Click handler for the menu. "
|
"Click handler for the menu."
|
||||||
python_input.show_sidebar = not python_input.show_sidebar
|
python_input.show_sidebar = not python_input.show_sidebar
|
||||||
|
|
||||||
version = sys.version_info
|
version = sys.version_info
|
||||||
|
@ -544,7 +544,7 @@ def meta_enter_message(python_input: "PythonInput") -> Container:
|
||||||
|
|
||||||
@Condition
|
@Condition
|
||||||
def extra_condition() -> bool:
|
def extra_condition() -> bool:
|
||||||
" Only show when... "
|
"Only show when..."
|
||||||
b = python_input.default_buffer
|
b = python_input.default_buffer
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -646,7 +646,7 @@ class PtPythonLayout:
|
||||||
sidebar = python_sidebar(python_input)
|
sidebar = python_sidebar(python_input)
|
||||||
self.exit_confirmation = create_exit_confirmation(python_input)
|
self.exit_confirmation = create_exit_confirmation(python_input)
|
||||||
|
|
||||||
root_container = HSplit(
|
self.root_container = HSplit(
|
||||||
[
|
[
|
||||||
VSplit(
|
VSplit(
|
||||||
[
|
[
|
||||||
|
@ -759,5 +759,5 @@ class PtPythonLayout:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.layout = Layout(root_container)
|
self.layout = Layout(self.root_container)
|
||||||
self.sidebar = sidebar
|
self.sidebar = sidebar
|
||||||
|
|
|
@ -16,7 +16,7 @@ class PromptStyle(metaclass=ABCMeta):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def in_prompt(self) -> AnyFormattedText:
|
def in_prompt(self) -> AnyFormattedText:
|
||||||
" Return the input tokens. "
|
"Return the input tokens."
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -31,7 +31,7 @@ class PromptStyle(metaclass=ABCMeta):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def out_prompt(self) -> AnyFormattedText:
|
def out_prompt(self) -> AnyFormattedText:
|
||||||
" Return the output tokens. "
|
"Return the output tokens."
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ This can be used for creation of Python REPLs.
|
||||||
"""
|
"""
|
||||||
import __future__
|
import __future__
|
||||||
|
|
||||||
import threading
|
|
||||||
from asyncio import get_event_loop
|
from asyncio import get_event_loop
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, List, Optional, TypeVar
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, List, Optional, TypeVar
|
||||||
|
@ -174,6 +173,11 @@ class PythonInput:
|
||||||
|
|
||||||
python_input = PythonInput(...)
|
python_input = PythonInput(...)
|
||||||
python_code = python_input.app.run()
|
python_code = python_input.app.run()
|
||||||
|
|
||||||
|
:param create_app: When `False`, don't create and manage a prompt_toolkit
|
||||||
|
application. The default is `True` and should only be set
|
||||||
|
to false if PythonInput is being embedded in a separate
|
||||||
|
prompt_toolkit application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -188,6 +192,7 @@ class PythonInput:
|
||||||
output: Optional[Output] = None,
|
output: Optional[Output] = None,
|
||||||
# For internal use.
|
# For internal use.
|
||||||
extra_key_bindings: Optional[KeyBindings] = None,
|
extra_key_bindings: Optional[KeyBindings] = None,
|
||||||
|
create_app=True,
|
||||||
_completer: Optional[Completer] = None,
|
_completer: Optional[Completer] = None,
|
||||||
_validator: Optional[Validator] = None,
|
_validator: Optional[Validator] = None,
|
||||||
_lexer: Optional[Lexer] = None,
|
_lexer: Optional[Lexer] = None,
|
||||||
|
@ -380,10 +385,16 @@ class PythonInput:
|
||||||
extra_toolbars=self._extra_toolbars,
|
extra_toolbars=self._extra_toolbars,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.app = self._create_application(input, output)
|
# Create an app if requested. If not, the global get_app() is returned
|
||||||
|
# for self.app via property getter.
|
||||||
if vi_mode:
|
if create_app:
|
||||||
self.app.editing_mode = EditingMode.VI
|
self._app: Optional[Application] = self._create_application(input, output)
|
||||||
|
# Setting vi_mode will not work unless the prompt_toolkit
|
||||||
|
# application has been created.
|
||||||
|
if vi_mode:
|
||||||
|
self.app.editing_mode = EditingMode.VI
|
||||||
|
else:
|
||||||
|
self._app = None
|
||||||
|
|
||||||
def _accept_handler(self, buff: Buffer) -> bool:
|
def _accept_handler(self, buff: Buffer) -> bool:
|
||||||
app = get_app()
|
app = get_app()
|
||||||
|
@ -393,12 +404,12 @@ class PythonInput:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def option_count(self) -> int:
|
def option_count(self) -> int:
|
||||||
" Return the total amount of options. (In all categories together.) "
|
"Return the total amount of options. (In all categories together.)"
|
||||||
return sum(len(category.options) for category in self.options)
|
return sum(len(category.options) for category in self.options)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selected_option(self) -> Option:
|
def selected_option(self) -> Option:
|
||||||
" Return the currently selected option. "
|
"Return the currently selected option."
|
||||||
i = 0
|
i = 0
|
||||||
for category in self.options:
|
for category in self.options:
|
||||||
for o in category.options:
|
for o in category.options:
|
||||||
|
@ -521,7 +532,7 @@ class PythonInput:
|
||||||
def simple_option(
|
def simple_option(
|
||||||
title: str, description: str, field_name: str, values: Optional[List] = None
|
title: str, description: str, field_name: str, values: Optional[List] = None
|
||||||
) -> Option:
|
) -> Option:
|
||||||
" Create Simple on/of option. "
|
"Create Simple on/of option."
|
||||||
values = values or ["off", "on"]
|
values = values or ["off", "on"]
|
||||||
|
|
||||||
def get_current_value():
|
def get_current_value():
|
||||||
|
@ -914,23 +925,19 @@ class PythonInput:
|
||||||
else:
|
else:
|
||||||
self.editing_mode = EditingMode.EMACS
|
self.editing_mode = EditingMode.EMACS
|
||||||
|
|
||||||
def _on_input_timeout(self, buff: Buffer, loop=None) -> None:
|
@property
|
||||||
|
def app(self) -> Application:
|
||||||
|
if self._app is None:
|
||||||
|
return get_app()
|
||||||
|
return self._app
|
||||||
|
|
||||||
|
def _on_input_timeout(self, buff: Buffer) -> None:
|
||||||
"""
|
"""
|
||||||
When there is no input activity,
|
When there is no input activity,
|
||||||
in another thread, get the signature of the current code.
|
in another thread, get the signature of the current code.
|
||||||
"""
|
"""
|
||||||
app = self.app
|
|
||||||
|
|
||||||
# Never run multiple get-signature threads.
|
def get_signatures_in_executor(document: Document) -> List[Signature]:
|
||||||
if self._get_signatures_thread_running:
|
|
||||||
return
|
|
||||||
self._get_signatures_thread_running = True
|
|
||||||
|
|
||||||
document = buff.document
|
|
||||||
|
|
||||||
loop = loop or get_event_loop()
|
|
||||||
|
|
||||||
def run():
|
|
||||||
# First, get signatures from Jedi. If we didn't found any and if
|
# First, get signatures from Jedi. If we didn't found any and if
|
||||||
# "dictionary completion" (eval-based completion) is enabled, then
|
# "dictionary completion" (eval-based completion) is enabled, then
|
||||||
# get signatures using eval.
|
# get signatures using eval.
|
||||||
|
@ -942,26 +949,47 @@ class PythonInput:
|
||||||
document, self.get_locals(), self.get_globals()
|
document, self.get_locals(), self.get_globals()
|
||||||
)
|
)
|
||||||
|
|
||||||
self._get_signatures_thread_running = False
|
return signatures
|
||||||
|
|
||||||
# Set signatures and redraw if the text didn't change in the
|
app = self.app
|
||||||
# meantime. Otherwise request new signatures.
|
|
||||||
if buff.text == document.text:
|
|
||||||
self.signatures = signatures
|
|
||||||
|
|
||||||
# Set docstring in docstring buffer.
|
async def on_timeout_task() -> None:
|
||||||
if signatures:
|
loop = get_event_loop()
|
||||||
self.docstring_buffer.reset(
|
|
||||||
document=Document(signatures[0].docstring, cursor_position=0)
|
# Never run multiple get-signature threads.
|
||||||
|
if self._get_signatures_thread_running:
|
||||||
|
return
|
||||||
|
self._get_signatures_thread_running = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
document = buff.document
|
||||||
|
signatures = await loop.run_in_executor(
|
||||||
|
None, get_signatures_in_executor, document
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
self.docstring_buffer.reset()
|
|
||||||
|
|
||||||
app.invalidate()
|
# If the text didn't change in the meantime, take these
|
||||||
|
# signatures. Otherwise, try again.
|
||||||
|
if buff.text == document.text:
|
||||||
|
break
|
||||||
|
finally:
|
||||||
|
self._get_signatures_thread_running = False
|
||||||
|
|
||||||
|
# Set signatures and redraw.
|
||||||
|
self.signatures = signatures
|
||||||
|
|
||||||
|
# Set docstring in docstring buffer.
|
||||||
|
if signatures:
|
||||||
|
self.docstring_buffer.reset(
|
||||||
|
document=Document(signatures[0].docstring, cursor_position=0)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._on_input_timeout(buff, loop=loop)
|
self.docstring_buffer.reset()
|
||||||
|
|
||||||
loop.run_in_executor(None, run)
|
app.invalidate()
|
||||||
|
|
||||||
|
if app.is_running:
|
||||||
|
app.create_background_task(on_timeout_task())
|
||||||
|
|
||||||
def on_reset(self) -> None:
|
def on_reset(self) -> None:
|
||||||
self.signatures = []
|
self.signatures = []
|
||||||
|
@ -970,7 +998,7 @@ class PythonInput:
|
||||||
"""
|
"""
|
||||||
Display the history.
|
Display the history.
|
||||||
"""
|
"""
|
||||||
app = get_app()
|
app = self.app
|
||||||
app.vi_state.input_mode = InputMode.NAVIGATION
|
app.vi_state.input_mode = InputMode.NAVIGATION
|
||||||
|
|
||||||
history = PythonHistory(self, self.default_buffer.document)
|
history = PythonHistory(self, self.default_buffer.document)
|
||||||
|
@ -1012,43 +1040,25 @@ class PythonInput:
|
||||||
self.app.vi_state.input_mode = InputMode.NAVIGATION
|
self.app.vi_state.input_mode = InputMode.NAVIGATION
|
||||||
|
|
||||||
# Run the UI.
|
# Run the UI.
|
||||||
result: str = ""
|
while True:
|
||||||
exception: Optional[BaseException] = None
|
|
||||||
|
|
||||||
def in_thread() -> None:
|
|
||||||
nonlocal result, exception
|
|
||||||
try:
|
try:
|
||||||
while True:
|
result = self.app.run(pre_run=pre_run, in_thread=True)
|
||||||
try:
|
|
||||||
result = self.app.run(pre_run=pre_run)
|
|
||||||
|
|
||||||
if result.lstrip().startswith("\x1a"):
|
if result.lstrip().startswith("\x1a"):
|
||||||
# When the input starts with Ctrl-Z, quit the REPL.
|
# When the input starts with Ctrl-Z, quit the REPL.
|
||||||
# (Important for Windows users.)
|
# (Important for Windows users.)
|
||||||
raise EOFError
|
raise EOFError
|
||||||
|
|
||||||
# Remove leading whitespace.
|
# Remove leading whitespace.
|
||||||
# (Users can add extra indentation, which happens for
|
# (Users can add extra indentation, which happens for
|
||||||
# instance because of copy/pasting code.)
|
# instance because of copy/pasting code.)
|
||||||
result = unindent_code(result)
|
result = unindent_code(result)
|
||||||
|
|
||||||
if result and not result.isspace():
|
if result and not result.isspace():
|
||||||
return
|
if self.insert_blank_line_after_input:
|
||||||
except KeyboardInterrupt:
|
self.app.output.write("\n")
|
||||||
# Abort - try again.
|
|
||||||
self.default_buffer.document = Document()
|
|
||||||
except BaseException as e:
|
|
||||||
exception = e
|
|
||||||
return
|
|
||||||
|
|
||||||
finally:
|
return result
|
||||||
if self.insert_blank_line_after_input:
|
except KeyboardInterrupt:
|
||||||
self.app.output.write("\n")
|
# Abort - try again.
|
||||||
|
self.default_buffer.document = Document()
|
||||||
thread = threading.Thread(target=in_thread)
|
|
||||||
thread.start()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
if exception is not None:
|
|
||||||
raise exception
|
|
||||||
return result
|
|
||||||
|
|
191
ptpython/repl.py
191
ptpython/repl.py
|
@ -11,7 +11,6 @@ import asyncio
|
||||||
import builtins
|
import builtins
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
import traceback
|
import traceback
|
||||||
import types
|
import types
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -80,7 +79,7 @@ class PythonRepl(PythonInput):
|
||||||
self._load_start_paths()
|
self._load_start_paths()
|
||||||
|
|
||||||
def _load_start_paths(self) -> None:
|
def _load_start_paths(self) -> None:
|
||||||
" Start the Read-Eval-Print Loop. "
|
"Start the Read-Eval-Print Loop."
|
||||||
if self._startup_paths:
|
if self._startup_paths:
|
||||||
for path in self._startup_paths:
|
for path in self._startup_paths:
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
|
@ -91,6 +90,35 @@ class PythonRepl(PythonInput):
|
||||||
output = self.app.output
|
output = self.app.output
|
||||||
output.write("WARNING | File not found: {}\n\n".format(path))
|
output.write("WARNING | File not found: {}\n\n".format(path))
|
||||||
|
|
||||||
|
def run_and_show_expression(self, expression):
|
||||||
|
try:
|
||||||
|
# Eval.
|
||||||
|
try:
|
||||||
|
result = self.eval(expression)
|
||||||
|
except KeyboardInterrupt: # KeyboardInterrupt doesn't inherit from Exception.
|
||||||
|
raise
|
||||||
|
except SystemExit:
|
||||||
|
raise
|
||||||
|
except BaseException as e:
|
||||||
|
self._handle_exception(e)
|
||||||
|
else:
|
||||||
|
# Print.
|
||||||
|
if result is not None:
|
||||||
|
self.show_result(result)
|
||||||
|
|
||||||
|
# Loop.
|
||||||
|
self.current_statement_index += 1
|
||||||
|
self.signatures = []
|
||||||
|
|
||||||
|
except KeyboardInterrupt as e:
|
||||||
|
# Handle all possible `KeyboardInterrupt` errors. This can
|
||||||
|
# happen during the `eval`, but also during the
|
||||||
|
# `show_result` if something takes too long.
|
||||||
|
# (Try/catch is around the whole block, because we want to
|
||||||
|
# prevent that a Control-C keypress terminates the REPL in
|
||||||
|
# any case.)
|
||||||
|
self._handle_keyboard_interrupt(e)
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""
|
"""
|
||||||
Run the REPL loop.
|
Run the REPL loop.
|
||||||
|
@ -102,44 +130,41 @@ class PythonRepl(PythonInput):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
# Pull text from the user.
|
||||||
try:
|
try:
|
||||||
# Read.
|
text = self.read()
|
||||||
try:
|
except EOFError:
|
||||||
text = self.read()
|
return
|
||||||
except EOFError:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Eval.
|
# Run it; display the result (or errors if applicable).
|
||||||
try:
|
self.run_and_show_expression(text)
|
||||||
result = self.eval(text)
|
|
||||||
except KeyboardInterrupt as e: # KeyboardInterrupt doesn't inherit from Exception.
|
|
||||||
raise
|
|
||||||
except SystemExit:
|
|
||||||
return
|
|
||||||
except BaseException as e:
|
|
||||||
self._handle_exception(e)
|
|
||||||
else:
|
|
||||||
# Print.
|
|
||||||
if result is not None:
|
|
||||||
self.show_result(result)
|
|
||||||
|
|
||||||
# Loop.
|
|
||||||
self.current_statement_index += 1
|
|
||||||
self.signatures = []
|
|
||||||
|
|
||||||
except KeyboardInterrupt as e:
|
|
||||||
# Handle all possible `KeyboardInterrupt` errors. This can
|
|
||||||
# happen during the `eval`, but also during the
|
|
||||||
# `show_result` if something takes too long.
|
|
||||||
# (Try/catch is around the whole block, because we want to
|
|
||||||
# prevent that a Control-C keypress terminates the REPL in
|
|
||||||
# any case.)
|
|
||||||
self._handle_keyboard_interrupt(e)
|
|
||||||
finally:
|
finally:
|
||||||
if self.terminal_title:
|
if self.terminal_title:
|
||||||
clear_title()
|
clear_title()
|
||||||
self._remove_from_namespace()
|
self._remove_from_namespace()
|
||||||
|
|
||||||
|
async def run_and_show_expression_async(self, text):
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = await self.eval_async(text)
|
||||||
|
except KeyboardInterrupt: # KeyboardInterrupt doesn't inherit from Exception.
|
||||||
|
raise
|
||||||
|
except SystemExit:
|
||||||
|
return
|
||||||
|
except BaseException as e:
|
||||||
|
self._handle_exception(e)
|
||||||
|
else:
|
||||||
|
# Print.
|
||||||
|
if result is not None:
|
||||||
|
await loop.run_in_executor(None, lambda: self.show_result(result))
|
||||||
|
|
||||||
|
# Loop.
|
||||||
|
self.current_statement_index += 1
|
||||||
|
self.signatures = []
|
||||||
|
# Return the result for future consumers.
|
||||||
|
return result
|
||||||
|
|
||||||
async def run_async(self) -> None:
|
async def run_async(self) -> None:
|
||||||
"""
|
"""
|
||||||
Run the REPL loop, but run the blocking parts in an executor, so that
|
Run the REPL loop, but run the blocking parts in an executor, so that
|
||||||
|
@ -169,24 +194,7 @@ class PythonRepl(PythonInput):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Eval.
|
# Eval.
|
||||||
try:
|
await self.run_and_show_expression_async(text)
|
||||||
result = await self.eval_async(text)
|
|
||||||
except KeyboardInterrupt as e: # KeyboardInterrupt doesn't inherit from Exception.
|
|
||||||
raise
|
|
||||||
except SystemExit:
|
|
||||||
return
|
|
||||||
except BaseException as e:
|
|
||||||
self._handle_exception(e)
|
|
||||||
else:
|
|
||||||
# Print.
|
|
||||||
if result is not None:
|
|
||||||
await loop.run_in_executor(
|
|
||||||
None, lambda: self.show_result(result)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Loop.
|
|
||||||
self.current_statement_index += 1
|
|
||||||
self.signatures = []
|
|
||||||
|
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt as e:
|
||||||
# XXX: This does not yet work properly. In some situations,
|
# XXX: This does not yet work properly. In some situations,
|
||||||
|
@ -231,7 +239,10 @@ class PythonRepl(PythonInput):
|
||||||
# above, then `sys.exc_info()` would not report the right error.
|
# above, then `sys.exc_info()` would not report the right error.
|
||||||
# See issue: https://github.com/prompt-toolkit/ptpython/issues/435
|
# See issue: https://github.com/prompt-toolkit/ptpython/issues/435
|
||||||
code = self._compile_with_flags(line, "exec")
|
code = self._compile_with_flags(line, "exec")
|
||||||
exec(code, self.get_globals(), self.get_locals())
|
result = eval(code, self.get_globals(), self.get_locals())
|
||||||
|
|
||||||
|
if _has_coroutine_flag(code):
|
||||||
|
result = asyncio.get_event_loop().run_until_complete(result)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -263,9 +274,14 @@ class PythonRepl(PythonInput):
|
||||||
self._store_eval_result(result)
|
self._store_eval_result(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# If not a valid `eval` expression, run using `exec` instead.
|
# If not a valid `eval` expression, compile as `exec` expression
|
||||||
|
# but still run with eval to get an awaitable in case of a
|
||||||
|
# awaitable expression.
|
||||||
code = self._compile_with_flags(line, "exec")
|
code = self._compile_with_flags(line, "exec")
|
||||||
exec(code, self.get_globals(), self.get_locals())
|
result = eval(code, self.get_globals(), self.get_locals())
|
||||||
|
|
||||||
|
if _has_coroutine_flag(code):
|
||||||
|
result = await result
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -277,7 +293,7 @@ class PythonRepl(PythonInput):
|
||||||
return super().get_compiler_flags() | PyCF_ALLOW_TOP_LEVEL_AWAIT
|
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):
|
||||||
" Compile code with the right compiler flags. "
|
"Compile code with the right compiler flags."
|
||||||
return compile(
|
return compile(
|
||||||
code,
|
code,
|
||||||
"<stdin>",
|
"<stdin>",
|
||||||
|
@ -286,9 +302,9 @@ class PythonRepl(PythonInput):
|
||||||
dont_inherit=True,
|
dont_inherit=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def show_result(self, result: object) -> None:
|
def _format_result_output(self, result: object) -> StyleAndTextTuples:
|
||||||
"""
|
"""
|
||||||
Show __repr__ for an `eval` result.
|
Format __repr__ for an `eval` result.
|
||||||
|
|
||||||
Note: this can raise `KeyboardInterrupt` if either calling `__repr__`,
|
Note: this can raise `KeyboardInterrupt` if either calling `__repr__`,
|
||||||
`__pt_repr__` or formatting the output with "Black" takes to long
|
`__pt_repr__` or formatting the output with "Black" takes to long
|
||||||
|
@ -304,7 +320,7 @@ class PythonRepl(PythonInput):
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
# Calling repr failed.
|
# Calling repr failed.
|
||||||
self._handle_exception(e)
|
self._handle_exception(e)
|
||||||
return
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
compile(result_repr, "", "eval")
|
compile(result_repr, "", "eval")
|
||||||
|
@ -315,12 +331,15 @@ class PythonRepl(PythonInput):
|
||||||
if self.enable_output_formatting:
|
if self.enable_output_formatting:
|
||||||
# Inline import. Slightly speed up start-up time if black is
|
# Inline import. Slightly speed up start-up time if black is
|
||||||
# not used.
|
# not used.
|
||||||
import black
|
try:
|
||||||
|
import black
|
||||||
result_repr = black.format_str(
|
except ImportError:
|
||||||
result_repr,
|
pass # no Black package in your installation
|
||||||
mode=black.FileMode(line_length=self.app.output.get_size().columns),
|
else:
|
||||||
)
|
result_repr = black.format_str(
|
||||||
|
result_repr,
|
||||||
|
mode=black.Mode(line_length=self.app.output.get_size().columns),
|
||||||
|
)
|
||||||
|
|
||||||
formatted_result_repr = to_formatted_text(
|
formatted_result_repr = to_formatted_text(
|
||||||
PygmentsTokens(list(_lex_python_result(result_repr)))
|
PygmentsTokens(list(_lex_python_result(result_repr)))
|
||||||
|
@ -363,10 +382,18 @@ class PythonRepl(PythonInput):
|
||||||
out_prompt + [("", fragment_list_to_text(formatted_result_repr))]
|
out_prompt + [("", fragment_list_to_text(formatted_result_repr))]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return to_formatted_text(formatted_output)
|
||||||
|
|
||||||
|
def show_result(self, result: object) -> None:
|
||||||
|
"""
|
||||||
|
Show __repr__ for an `eval` result and print to ouptut.
|
||||||
|
"""
|
||||||
|
formatted_text_output = self._format_result_output(result)
|
||||||
|
|
||||||
if self.enable_pager:
|
if self.enable_pager:
|
||||||
self.print_paginated_formatted_text(to_formatted_text(formatted_output))
|
self.print_paginated_formatted_text(formatted_text_output)
|
||||||
else:
|
else:
|
||||||
self.print_formatted_text(to_formatted_text(formatted_output))
|
self.print_formatted_text(formatted_text_output)
|
||||||
|
|
||||||
self.app.output.flush()
|
self.app.output.flush()
|
||||||
|
|
||||||
|
@ -421,15 +448,7 @@ class PythonRepl(PythonInput):
|
||||||
# Run pager prompt in another thread.
|
# Run pager prompt in another thread.
|
||||||
# Same as for the input. This prevents issues with nested event
|
# Same as for the input. This prevents issues with nested event
|
||||||
# loops.
|
# loops.
|
||||||
pager_result = None
|
pager_result = pager_prompt.prompt(in_thread=True)
|
||||||
|
|
||||||
def in_thread() -> None:
|
|
||||||
nonlocal pager_result
|
|
||||||
pager_result = pager_prompt.prompt()
|
|
||||||
|
|
||||||
th = threading.Thread(target=in_thread)
|
|
||||||
th.start()
|
|
||||||
th.join()
|
|
||||||
|
|
||||||
if pager_result == PagerResult.ABORT:
|
if pager_result == PagerResult.ABORT:
|
||||||
print("...")
|
print("...")
|
||||||
|
@ -494,9 +513,7 @@ class PythonRepl(PythonInput):
|
||||||
"""
|
"""
|
||||||
return create_pager_prompt(self._current_style, self.title)
|
return create_pager_prompt(self._current_style, self.title)
|
||||||
|
|
||||||
def _handle_exception(self, e: BaseException) -> None:
|
def _format_exception_output(self, e: BaseException) -> PygmentsTokens:
|
||||||
output = self.app.output
|
|
||||||
|
|
||||||
# Instead of just calling ``traceback.format_exc``, we take the
|
# Instead of just calling ``traceback.format_exc``, we take the
|
||||||
# traceback and skip the bottom calls of this framework.
|
# traceback and skip the bottom calls of this framework.
|
||||||
t, v, tb = sys.exc_info()
|
t, v, tb = sys.exc_info()
|
||||||
|
@ -525,9 +542,15 @@ class PythonRepl(PythonInput):
|
||||||
tokens = list(_lex_python_traceback(tb_str))
|
tokens = list(_lex_python_traceback(tb_str))
|
||||||
else:
|
else:
|
||||||
tokens = [(Token, tb_str)]
|
tokens = [(Token, tb_str)]
|
||||||
|
return PygmentsTokens(tokens)
|
||||||
|
|
||||||
|
def _handle_exception(self, e: BaseException) -> None:
|
||||||
|
output = self.app.output
|
||||||
|
|
||||||
|
tokens = self._format_exception_output(e)
|
||||||
|
|
||||||
print_formatted_text(
|
print_formatted_text(
|
||||||
PygmentsTokens(tokens),
|
tokens,
|
||||||
style=self._current_style,
|
style=self._current_style,
|
||||||
style_transformation=self.style_transformation,
|
style_transformation=self.style_transformation,
|
||||||
include_default_pygments_style=False,
|
include_default_pygments_style=False,
|
||||||
|
@ -564,13 +587,13 @@ class PythonRepl(PythonInput):
|
||||||
|
|
||||||
|
|
||||||
def _lex_python_traceback(tb):
|
def _lex_python_traceback(tb):
|
||||||
" Return token list for traceback string. "
|
"Return token list for traceback string."
|
||||||
lexer = PythonTracebackLexer()
|
lexer = PythonTracebackLexer()
|
||||||
return lexer.get_tokens(tb)
|
return lexer.get_tokens(tb)
|
||||||
|
|
||||||
|
|
||||||
def _lex_python_result(tb):
|
def _lex_python_result(tb):
|
||||||
" Return token list for Python string. "
|
"Return token list for Python string."
|
||||||
lexer = PythonLexer()
|
lexer = PythonLexer()
|
||||||
# Use `get_tokens_unprocessed`, so that we get exactly the same string,
|
# Use `get_tokens_unprocessed`, so that we get exactly the same string,
|
||||||
# without line endings appended. `print_formatted_text` already appends a
|
# without line endings appended. `print_formatted_text` already appends a
|
||||||
|
@ -590,7 +613,9 @@ def enable_deprecation_warnings() -> None:
|
||||||
warnings.filterwarnings("default", category=DeprecationWarning, module="__main__")
|
warnings.filterwarnings("default", category=DeprecationWarning, module="__main__")
|
||||||
|
|
||||||
|
|
||||||
def run_config(repl: PythonInput, config_file: str = "~/.ptpython/config.py") -> None:
|
def run_config(
|
||||||
|
repl: PythonInput, config_file: str = "~/.config/ptpython/config.py"
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Execute REPL config file.
|
Execute REPL config file.
|
||||||
|
|
||||||
|
@ -738,7 +763,7 @@ def create_pager_prompt(
|
||||||
|
|
||||||
@bindings.add("<any>")
|
@bindings.add("<any>")
|
||||||
def _(event: KeyPressEvent) -> None:
|
def _(event: KeyPressEvent) -> None:
|
||||||
" Disallow inserting other text. "
|
"Disallow inserting other text."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
style
|
style
|
||||||
|
|
12
setup.py
12
setup.py
|
@ -11,7 +11,7 @@ with open(os.path.join(os.path.dirname(__file__), "README.rst")) as f:
|
||||||
setup(
|
setup(
|
||||||
name="ptpython",
|
name="ptpython",
|
||||||
author="Jonathan Slenders",
|
author="Jonathan Slenders",
|
||||||
version="3.0.16",
|
version="3.0.19",
|
||||||
url="https://github.com/prompt-toolkit/ptpython",
|
url="https://github.com/prompt-toolkit/ptpython",
|
||||||
description="Python REPL build on top of prompt_toolkit",
|
description="Python REPL build on top of prompt_toolkit",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
|
@ -20,10 +20,9 @@ setup(
|
||||||
"appdirs",
|
"appdirs",
|
||||||
"importlib_metadata;python_version<'3.8'",
|
"importlib_metadata;python_version<'3.8'",
|
||||||
"jedi>=0.16.0",
|
"jedi>=0.16.0",
|
||||||
# Use prompt_toolkit 3.0.16, because of the `DeduplicateCompleter`.
|
# Use prompt_toolkit 3.0.18, because of the `in_thread` option.
|
||||||
"prompt_toolkit>=3.0.16,<3.1.0",
|
"prompt_toolkit>=3.0.18,<3.1.0",
|
||||||
"pygments",
|
"pygments",
|
||||||
"black",
|
|
||||||
],
|
],
|
||||||
python_requires=">=3.6",
|
python_requires=">=3.6",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
@ -47,5 +46,8 @@ setup(
|
||||||
% sys.version_info[:2],
|
% sys.version_info[:2],
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
extras_require={"ptipython": ["ipython"]}, # For ptipython, we need to have IPython
|
extras_require={
|
||||||
|
"ptipython": ["ipython"], # For ptipython, we need to have IPython
|
||||||
|
"all": ["black"], # Black not always possible on PyPy
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue