diff --git a/.travis.yml b/.travis.yml index 9f464ccde..94c2eae11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ matrix: - python: 3.5 - python: 3.4 - python: 2.7 - - python: 2.6 - python: nightly - python: pypy - python: pypy3 diff --git a/CHANGELOG b/CHANGELOG index 883c6f6ce..c748dd29d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,22 @@ CHANGELOG ========= +2.0.10: 2019-10-03 +------------------ + +Bug fixes: +- Handle HANDLE sizes correctly on windows. This made things break randomly.on + 64 bit systems. +- Handle terminal size correctly when reported as (0, 0). +- Fix width computation in progress bar formatter. +- Fix option-up and -down on Mac with iTerm2. +- Removed ctrl-c in confirmation prompt. + +New features: +- Added PROMPT_TOOLKIT_NO_CPR=1 environment variable to disable CPR requests. +- Accept a pattern in `WordCompleter`. + + 2.0.9: 2019-02-19 ----------------- diff --git a/docs/conf.py b/docs/conf.py index dea27d51a..1c541ae90 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ # built documents. # # The short X.Y version. -version = '2.0.9' +version = '2.0.10' # The full version, including alpha/beta/rc tags. -release = '2.0.9' +release = '2.0.10' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/prompt_toolkit/__init__.py b/prompt_toolkit/__init__.py index 8bb60044a..76d182c9e 100644 --- a/prompt_toolkit/__init__.py +++ b/prompt_toolkit/__init__.py @@ -20,7 +20,7 @@ from .shortcuts import PromptSession, print_formatted_text, prompt # Don't forget to update in `docs/conf.py`! -__version__ = '2.0.9' +__version__ = '2.0.10' # Version tuple. VERSION = tuple(__version__.split('.')) diff --git a/prompt_toolkit/completion/fuzzy_completer.py b/prompt_toolkit/completion/fuzzy_completer.py index 9c1b8de27..cc163cd07 100644 --- a/prompt_toolkit/completion/fuzzy_completer.py +++ b/prompt_toolkit/completion/fuzzy_completer.py @@ -159,6 +159,7 @@ def __init__(self, words, meta_dict=None, WORD=False): self.word_completer = WordCompleter( words=lambda: self.words, + meta_dict=self.meta_dict, WORD=self.WORD) self.fuzzy_completer = FuzzyCompleter( diff --git a/prompt_toolkit/eventloop/asyncio_win32.py b/prompt_toolkit/eventloop/asyncio_win32.py index bcc4dc6ec..a722ee70b 100644 --- a/prompt_toolkit/eventloop/asyncio_win32.py +++ b/prompt_toolkit/eventloop/asyncio_win32.py @@ -30,7 +30,7 @@ def __init__(self, loop=None): self.closed = False # Maps win32 handles to their callbacks. - self._handle_callbacks = {} + self._handle_callbacks = {} # HANDLE fd to callback. def close(self): # Note: we should not close the asyncio loop itself, because that one @@ -90,7 +90,7 @@ def default_exception_handler(self, context): def add_win32_handle(self, handle, callback): " Add a Win32 handle to the event loop. " callback = wrap_in_current_context(callback) - self._handle_callbacks[handle] = callback + self._handle_callbacks[handle.value] = callback # Add reader. def ready(): @@ -104,7 +104,7 @@ def ready(): # (Use an executor for this, the Windows asyncio event loop doesn't # allow us to wait for handles like stdin.) def wait(): - if self._handle_callbacks.get(handle) != callback: + if self._handle_callbacks.get(handle.value) != callback: return wait_for_handles([handle]) @@ -114,5 +114,5 @@ def wait(): def remove_win32_handle(self, handle): " Remove a Win32 handle from the event loop. " - if handle in self._handle_callbacks: - del self._handle_callbacks[handle] + if handle.value in self._handle_callbacks: + del self._handle_callbacks[handle.value] diff --git a/prompt_toolkit/eventloop/win32.py b/prompt_toolkit/eventloop/win32.py index 6b5759395..bafb4f797 100644 --- a/prompt_toolkit/eventloop/win32.py +++ b/prompt_toolkit/eventloop/win32.py @@ -41,7 +41,7 @@ def __init__(self, recognize_paste=True): self._running = False # Additional readers. - self._read_fds = {} # Maps fd to handler. + self._read_fds = {} # Maps HANDLE value to handler. # Create inputhook context. self._inputhook_context = None @@ -86,9 +86,9 @@ def ready(wait): self._inputhook_context.call_inputhook(ready, inputhook) # Wait for the next event. - handle = self._ready_for_reading(INFINITE) + handle = self._ready_for_reading(INFINITE).value - if handle == self._event: + if handle == self._event.value: # When the Windows Event has been trigger, process the messages in the queue. windll.kernel32.ResetEvent(self._event) self._process_queued_calls_from_executor() @@ -110,7 +110,7 @@ def _ready_for_reading(self, timeout=INFINITE): Return the handle that is ready for reading or `None` on timeout. """ handles = [self._event] - handles.extend(self._read_fds.keys()) + handles.extend(HANDLE(fd) for fd in self._read_fds.keys()) return wait_for_handles(handles, timeout) def close(self): @@ -161,23 +161,23 @@ def add_reader(self, fd, callback): " Start watching the file descriptor for read availability. " callback = wrap_in_current_context(callback) - h = msvcrt.get_osfhandle(fd) + h = HANDLE(msvcrt.get_osfhandle(fd)) self.add_win32_handle(h, callback) def remove_reader(self, fd): " Stop watching the file descriptor for read availability. " - h = msvcrt.get_osfhandle(fd) + h = HANDLE(msvcrt.get_osfhandle(fd)) self.remove_win32_handle(h) def add_win32_handle(self, handle, callback): " Add a Win32 handle to the event loop. " callback = wrap_in_current_context(callback) - self._read_fds[handle] = callback + self._read_fds[handle.value] = callback def remove_win32_handle(self, handle): " Remove a Win32 handle from the event loop. " - if handle in self._read_fds: - del self._read_fds[handle] + if handle.value in self._read_fds: + del self._read_fds[handle.value] def wait_for_handles(handles, timeout=INFINITE): @@ -187,7 +187,7 @@ def wait_for_handles(handles, timeout=INFINITE): http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx """ - assert isinstance(handles, list) + assert isinstance(handles, list) # List of `HANDLE` objects. assert isinstance(timeout, int) arrtype = HANDLE * len(handles) @@ -200,7 +200,7 @@ def wait_for_handles(handles, timeout=INFINITE): return None else: h = handle_array[ret] - return h + return HANDLE(h) def create_win32_event(): @@ -209,9 +209,9 @@ def create_win32_event(): http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx """ - return windll.kernel32.CreateEventA( + return HANDLE(windll.kernel32.CreateEventA( pointer(SECURITY_ATTRIBUTES()), BOOL(True), # Manual reset event. BOOL(False), # Initial state. None # Unnamed event object. - ) + )) diff --git a/prompt_toolkit/input/vt100.py b/prompt_toolkit/input/vt100.py index 5fcceb095..dd60d82b0 100644 --- a/prompt_toolkit/input/vt100.py +++ b/prompt_toolkit/input/vt100.py @@ -69,6 +69,8 @@ def __init__(self, stdin): def responds_to_cpr(self): # When the input is a tty, we assume that CPR is supported. # It's not when the input is piped from Pexpect. + if os.environ.get('PROMPT_TOOLKIT_NO_CPR', '') == '1': + return False return self.stdin.isatty() def attach(self, input_ready_callback): diff --git a/prompt_toolkit/input/win32.py b/prompt_toolkit/input/win32.py index 60cccc2e5..0b6d71c58 100644 --- a/prompt_toolkit/input/win32.py +++ b/prompt_toolkit/input/win32.py @@ -5,7 +5,7 @@ import sys from contextlib import contextmanager from ctypes import pointer, windll -from ctypes.wintypes import DWORD +from ctypes.wintypes import DWORD, HANDLE import six from six.moves import range @@ -181,10 +181,10 @@ def __init__(self, recognize_paste=True): # When stdin is a tty, use that handle, otherwise, create a handle from # CONIN$. if sys.stdin.isatty(): - self.handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE) + self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) else: self._fdcon = os.open('CONIN$', os.O_RDWR | os.O_BINARY) - self.handle = msvcrt.get_osfhandle(self._fdcon) + self.handle = HANDLE(msvcrt.get_osfhandle(self._fdcon)) def close(self): " Close fdcon. " @@ -465,7 +465,7 @@ class raw_mode(object): `raw_input` method of `.vt100_input`. """ def __init__(self, fileno=None): - self.handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE) + self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) def __enter__(self): # Remember original mode. diff --git a/prompt_toolkit/key_binding/bindings/auto_suggest.py b/prompt_toolkit/key_binding/bindings/auto_suggest.py index 58688db24..4c994b8ab 100644 --- a/prompt_toolkit/key_binding/bindings/auto_suggest.py +++ b/prompt_toolkit/key_binding/bindings/auto_suggest.py @@ -29,6 +29,7 @@ def load_auto_suggest_bindings(): def suggestion_available(): app = get_app() return (app.current_buffer.suggestion is not None and + app.current_buffer.suggestion.text and app.current_buffer.document.is_cursor_at_the_end) @handle('c-f', filter=suggestion_available) diff --git a/prompt_toolkit/output/win32.py b/prompt_toolkit/output/win32.py index 6090ec4b5..c09edcdbd 100644 --- a/prompt_toolkit/output/win32.py +++ b/prompt_toolkit/output/win32.py @@ -11,7 +11,7 @@ pointer, windll, ) -from ctypes.wintypes import DWORD +from ctypes.wintypes import DWORD, HANDLE import six @@ -88,7 +88,7 @@ def __init__(self, stdout, use_complete_width=False): self._buffer = [] self.stdout = stdout - self.hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) self._in_alternate_screen = False self._hidden = False @@ -370,8 +370,8 @@ def enter_alternate_screen(self): GENERIC_WRITE = 0x40000000 # Create a new console buffer and activate that one. - handle = self._winapi(windll.kernel32.CreateConsoleScreenBuffer, GENERIC_READ|GENERIC_WRITE, - DWORD(0), None, DWORD(1), None) + handle = HANDLE(self._winapi(windll.kernel32.CreateConsoleScreenBuffer, GENERIC_READ|GENERIC_WRITE, + DWORD(0), None, DWORD(1), None)) self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle) self.hconsole = handle @@ -382,7 +382,7 @@ def quit_alternate_screen(self): Make stdout again the active buffer. """ if self._in_alternate_screen: - stdout = self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE) + stdout = HANDLE(self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE)) self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout) self._winapi(windll.kernel32.CloseHandle, self.hconsole) self.hconsole = stdout @@ -390,7 +390,7 @@ def quit_alternate_screen(self): def enable_mouse_support(self): ENABLE_MOUSE_INPUT = 0x10 - handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE) + handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) original_mode = DWORD() self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) @@ -398,7 +398,7 @@ def enable_mouse_support(self): def disable_mouse_support(self): ENABLE_MOUSE_INPUT = 0x10 - handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE) + handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) original_mode = DWORD() self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) @@ -420,7 +420,7 @@ def win32_refresh_window(cls): to a bug in the Windows Console. Sending a repaint request solves it. """ # Get console handle - handle = windll.kernel32.GetConsoleWindow() + handle = HANDLE(windll.kernel32.GetConsoleWindow()) RDW_INVALIDATE = 0x0001 windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE)) diff --git a/prompt_toolkit/output/windows10.py b/prompt_toolkit/output/windows10.py index 88221982a..65c6c286d 100644 --- a/prompt_toolkit/output/windows10.py +++ b/prompt_toolkit/output/windows10.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from ctypes import byref, windll -from ctypes.wintypes import DWORD +from ctypes.wintypes import DWORD, HANDLE from prompt_toolkit.renderer import Output from prompt_toolkit.utils import is_windows @@ -26,7 +26,7 @@ class Windows10_Output(object): def __init__(self, stdout): self.win32_output = Win32Output(stdout) self.vt100_output = Vt100_Output(stdout, lambda: None) - self._hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) def flush(self): """ @@ -68,7 +68,7 @@ def is_win_vt100_enabled(): if not is_windows(): return False - hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) # Get original console mode. original_mode = DWORD(0) diff --git a/prompt_toolkit/renderer.py b/prompt_toolkit/renderer.py index 1381fb012..c2de3d7a2 100644 --- a/prompt_toolkit/renderer.py +++ b/prompt_toolkit/renderer.py @@ -679,9 +679,11 @@ def print_formatted_text( else: output.reset_attributes() + # Eliminate carriage returns + text = text.replace('\r', '') + # Assume that the output is raw, and insert a carriage return before # every newline. (Also important when the front-end is a telnet client.) - assert '\r' not in text output.write(text.replace('\n', '\r\n')) # Reset again. diff --git a/setup.py b/setup.py index 0fb4dcbeb..1ad523fac 100755 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ def get_version(package): 'six>=1.9.0', 'wcwidth', ], + python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', diff --git a/tests/test_print_formatted_text.py b/tests/test_print_formatted_text.py index 27f5d01ce..0ff094d51 100644 --- a/tests/test_print_formatted_text.py +++ b/tests/test_print_formatted_text.py @@ -48,6 +48,12 @@ def test_print_formatted_text(): assert b'hello' in f.data assert b'world' in f.data +@pytest.mark.skipif( + is_windows(), reason="Doesn't run on Windows yet.") +def test_print_formatted_text_backslash_r(): + f = _Capture() + pt_print('hello\r\n', file=f) + assert b'hello' in f.data @pytest.mark.skipif( is_windows(), reason="Doesn't run on Windows yet.")