From 1508ecfeae89659a1d8927e5ffefb8742cb318ef Mon Sep 17 00:00:00 2001 From: Vladimir Iakovlev Date: Wed, 14 Mar 2018 00:12:40 +0100 Subject: [PATCH] #N/A: Use mmap for sharing output in instant mode --- thefuck/const.py | 4 ++-- thefuck/entrypoints/shell_logger.py | 19 +++++++++++++------ thefuck/output_readers/read_log.py | 23 +++++++++++------------ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/thefuck/const.py b/thefuck/const.py index 3f71d39..dc55a5a 100644 --- a/thefuck/const.py +++ b/thefuck/const.py @@ -76,8 +76,8 @@ CONFIGURATION_TIMEOUT = 60 USER_COMMAND_MARK = u'\u200B' * 10 -LOG_SIZE = 1000 - LOG_SIZE_IN_BYTES = 1024 * 1024 +LOG_SIZE_TO_CLEAN = 10 * 1024 + DIFF_WITH_ALIAS = 0.5 diff --git a/thefuck/entrypoints/shell_logger.py b/thefuck/entrypoints/shell_logger.py index 6c90365..3df3f76 100644 --- a/thefuck/entrypoints/shell_logger.py +++ b/thefuck/entrypoints/shell_logger.py @@ -1,19 +1,24 @@ import array import fcntl from functools import partial +import mmap import os import pty import signal import sys import termios import tty -from ..logs import warn +from .. import logs, const def _read(f, fd): data = os.read(fd, 1024) - f.write(data) - f.flush() + try: + f.write(data) + except ValueError: + f.move(0, const.LOG_SIZE_TO_CLEAN, + const.LOG_SIZE_IN_BYTES - const.LOG_SIZE_TO_CLEAN) + f.seek(const.LOG_SIZE_IN_BYTES - const.LOG_SIZE_TO_CLEAN) return data @@ -61,10 +66,12 @@ def shell_logger(output): """ if not os.environ.get('SHELL'): - warn("Shell logger doesn't support your platform.") + logs.warn("Shell logger doesn't support your platform.") sys.exit(1) - with open(output, 'wb') as f: - return_code = _spawn(os.environ['SHELL'], partial(_read, f)) + fd = os.open(output, os.O_CREAT | os.O_TRUNC | os.O_RDWR) + os.write(fd, b'\x00' * const.LOG_SIZE_IN_BYTES) + buffer = mmap.mmap(fd, const.LOG_SIZE_IN_BYTES, mmap.MAP_SHARED, mmap.PROT_WRITE) + return_code = _spawn(os.environ['SHELL'], partial(_read, buffer)) sys.exit(return_code) diff --git a/thefuck/output_readers/read_log.py b/thefuck/output_readers/read_log.py index 9262fa8..4da63e0 100644 --- a/thefuck/output_readers/read_log.py +++ b/thefuck/output_readers/read_log.py @@ -1,5 +1,7 @@ import os import shlex +import mmap +import re try: from shutil import get_terminal_size except ImportError: @@ -18,11 +20,6 @@ def _group_by_calls(log): script_line = None lines = [] for line in log: - try: - line = line.decode() - except UnicodeDecodeError: - continue - if const.USER_COMMAND_MARK in line or ps1_counter > 0: if script_line and ps1_counter == 0: yield script_line, lines @@ -53,13 +50,14 @@ def _get_script_group_lines(grouped, script): def _get_output_lines(script, log_file): - lines = log_file.readlines()[-const.LOG_SIZE:] + data = log_file.read().decode() + data = re.sub(r'\x00+$', '', data) + lines = data.split('\n') grouped = list(_group_by_calls(lines)) script_lines = _get_script_group_lines(grouped, script) - screen = pyte.Screen(get_terminal_size().columns, len(script_lines)) stream = pyte.Stream(screen) - stream.feed(''.join(script_lines)) + stream.feed('\n'.join(script_lines)) return screen.display @@ -91,10 +89,11 @@ def get_output(script): return None try: - with logs.debug_time(u'Read output from log'), \ - open(os.environ['THEFUCK_OUTPUT_LOG'], 'rb') as log_file: - _skip_old_lines(log_file) - lines = _get_output_lines(script, log_file) + with logs.debug_time(u'Read output from log'): + fd = os.open(os.environ['THEFUCK_OUTPUT_LOG'], os.O_RDONLY) + buffer = mmap.mmap(fd, const.LOG_SIZE_IN_BYTES, mmap.MAP_SHARED, mmap.PROT_READ) + _skip_old_lines(buffer) + lines = _get_output_lines(script, buffer) output = '\n'.join(lines).strip() logs.debug(u'Received output: {}'.format(output)) return output