From 3b36470e848f4b67210c01413253ca927c5bd81d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 3 Feb 2020 08:03:35 +0530 Subject: [PATCH] When resolving scrollback pager if it is not present in system PATH try using shell PATH --- kitty/main.py | 61 +++++----------------------------------------- kitty/session.py | 13 ++-------- kitty/tabs.py | 3 +-- kitty/utils.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++-- kitty/window.py | 11 ++++++++- 5 files changed, 80 insertions(+), 71 deletions(-) diff --git a/kitty/main.py b/kitty/main.py index 2791946ee..33f3b8f55 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -10,7 +10,7 @@ from contextlib import contextmanager, suppress from .borders import load_borders_program from .boss import Boss -from .child import set_default_env, openpty, remove_blocking +from .child import set_default_env from .cli import create_opts, parse_args from .config import cached_values_for, initial_window_size_func from .constants import ( @@ -18,15 +18,15 @@ from .constants import ( is_wayland, kitty_exe, logo_data_file ) from .fast_data_types import ( - GLFW_IBEAM_CURSOR, create_os_window, free_font_data, - glfw_init, glfw_terminate, load_png_data, set_custom_cursor, - set_default_window_icon, set_options + GLFW_IBEAM_CURSOR, create_os_window, free_font_data, glfw_init, + glfw_terminate, load_png_data, set_custom_cursor, set_default_window_icon, + set_options ) from .fonts.box_drawing import set_scale from .fonts.render import set_font_family from .utils import ( - detach, log_error, single_instance, startup_notification_handler, - unix_socket_paths + detach, log_error, read_shell_environment, single_instance, + startup_notification_handler, unix_socket_paths ) from .window import load_shader_programs @@ -200,55 +200,6 @@ def macos_cmdline(argv_args): return ans -def read_shell_environment(opts=None): - if not hasattr(read_shell_environment, 'ans'): - ans = read_shell_environment.ans = {} - import subprocess - from .session import resolved_shell - shell = resolved_shell(opts) - master, slave = openpty() - remove_blocking(master) - try: - p = subprocess.Popen(shell + ['-l', '-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True) - except FileNotFoundError: - log_error('Could not find shell to read environment') - return ans - with os.fdopen(master, 'rb') as stdout, os.fdopen(slave, 'wb'): - raw = b'' - from subprocess import TimeoutExpired - from time import monotonic - start_time = monotonic() - while monotonic() - start_time < 1.5: - try: - ret = p.wait(0.01) - except TimeoutExpired: - ret = None - with suppress(Exception): - raw += stdout.read() - if ret is not None: - break - if p.returncode is None: - log_error('Timed out waiting for shell to quit while reading shell environment') - p.kill() - elif p.returncode == 0: - while True: - try: - x = stdout.read() - except Exception: - break - if not x: - break - raw += x - raw = raw.decode('utf-8', 'replace') - for line in raw.splitlines(): - k, v = line.partition('=')[::2] - if k and v: - ans[k] = v - else: - log_error('Failed to run shell to read its environment') - return read_shell_environment.ans - - def get_editor_from_env(shell_env): for var in ('VISUAL', 'EDITOR'): editor = shell_env.get(var) diff --git a/kitty/session.py b/kitty/session.py index 212f20693..506432e04 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -7,9 +7,9 @@ import sys from collections import namedtuple from .config_data import to_layout_names -from .constants import kitty_exe, shell_path +from .constants import kitty_exe from .layout import all_layouts -from .utils import log_error +from .utils import log_error, resolved_shell WindowSizeOpts = namedtuple( 'WindowSizeOpts', 'initial_window_width initial_window_height window_margin_width window_padding_width remember_window_size') @@ -74,15 +74,6 @@ class Session: self.tabs[-1].cwd = val -def resolved_shell(opts=None): - ans = getattr(opts, 'shell', '.') - if ans == '.': - ans = [shell_path] - else: - ans = shlex.split(ans) - return ans - - def parse_session(raw, opts, default_title=None): def finalize_session(ans): diff --git a/kitty/tabs.py b/kitty/tabs.py index 9a0cc08f9..0d496390a 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -16,9 +16,8 @@ from .fast_data_types import ( x11_window_id ) from .layout import create_layout_object_for, evict_cached_layouts -from .session import resolved_shell from .tab_bar import TabBar, TabBarData -from .utils import log_error +from .utils import log_error, resolved_shell from .window import Window SpecialWindowInstance = namedtuple('SpecialWindow', 'cmd stdin override_title cwd_from cwd overlay_for env') diff --git a/kitty/utils.py b/kitty/utils.py index 4543fd34c..4fefd87e2 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -10,11 +10,11 @@ import os import re import string import sys -from time import monotonic from contextlib import suppress +from time import monotonic from .constants import ( - appname, is_macos, is_wayland, supports_primary_selection + appname, is_macos, is_wayland, shell_path, supports_primary_selection ) from .rgb import Color, to_color @@ -452,3 +452,62 @@ def func_name(f): if hasattr(f, 'func') and hasattr(f.func, '__name__'): return f.func.__name__ return str(f) + + +def resolved_shell(opts=None): + ans = getattr(opts, 'shell', '.') + if ans == '.': + ans = [shell_path] + else: + import shlex + ans = shlex.split(ans) + return ans + + +def read_shell_environment(opts=None): + if not hasattr(read_shell_environment, 'ans'): + from .child import openpty, remove_blocking + ans = read_shell_environment.ans = {} + import subprocess + shell = resolved_shell(opts) + master, slave = openpty() + remove_blocking(master) + try: + p = subprocess.Popen(shell + ['-l', '-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True) + except FileNotFoundError: + log_error('Could not find shell to read environment') + return ans + with os.fdopen(master, 'rb') as stdout, os.fdopen(slave, 'wb'): + raw = b'' + from subprocess import TimeoutExpired + from time import monotonic + start_time = monotonic() + while monotonic() - start_time < 1.5: + try: + ret = p.wait(0.01) + except TimeoutExpired: + ret = None + with suppress(Exception): + raw += stdout.read() + if ret is not None: + break + if p.returncode is None: + log_error('Timed out waiting for shell to quit while reading shell environment') + p.kill() + elif p.returncode == 0: + while True: + try: + x = stdout.read() + except Exception: + break + if not x: + break + raw += x + raw = raw.decode('utf-8', 'replace') + for line in raw.splitlines(): + k, v = line.partition('=')[::2] + if k and v: + ans[k] = v + else: + log_error('Failed to run shell to read its environment') + return read_shell_environment.ans diff --git a/kitty/window.py b/kitty/window.py index 23771ca61..38e63e129 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -29,7 +29,8 @@ from .rgb import to_color from .terminfo import get_capabilities from .utils import ( color_as_int, get_primary_selection, load_shaders, open_cmd, open_url, - parse_color_set, sanitize_title, set_primary_selection + parse_color_set, read_shell_environment, sanitize_title, + set_primary_selection ) @@ -541,6 +542,14 @@ class Window: text = self.as_text(as_ansi=True, add_history=True, add_wrap_markers=True) data = self.pipe_data(text, has_wrap_markers=True) cmd = [x.replace('INPUT_LINE_NUMBER', str(data['input_line_number'])) for x in self.opts.scrollback_pager] + import shutil + exe = shutil.which(cmd[0]) + if not os.path.isabs(cmd[0]) and not exe: + env = read_shell_environment(self.opts) + if env and 'PATH' in env: + exe = shutil.which(cmd[0], path=env['PATH']) + if exe: + cmd[0] = exe get_boss().display_scrollback(self, data['text'], cmd) def paste_bytes(self, text):