Unify the PATH searching algorithm for launch and running children

This commit is contained in:
Kovid Goyal 2021-12-08 20:10:34 +05:30
parent 62dbc1129c
commit 32768e7939
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 53 additions and 30 deletions

View File

@ -20,7 +20,7 @@ from kitty.typing import (
GRT_C, CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, GRT_C, CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t,
HandlerType HandlerType
) )
from kitty.utils import ScreenSize, find_exe, fit_image from kitty.utils import ScreenSize, fit_image, which
from .operations import cursor from .operations import cursor
@ -148,7 +148,7 @@ def run_imagemagick(path: str, cmd: Sequence[str], keep_stdout: bool = True) ->
def identify(path: str) -> ImageData: def identify(path: str) -> ImageData:
import json import json
q = '{"fmt":"%m","canvas":"%g","transparency":"%A","gap":"%T","index":"%p","size":"%wx%h","dpi":"%xx%y","dispose":"%D","orientation":"%[EXIF:Orientation]"}' q = '{"fmt":"%m","canvas":"%g","transparency":"%A","gap":"%T","index":"%p","size":"%wx%h","dpi":"%xx%y","dispose":"%D","orientation":"%[EXIF:Orientation]"}'
exe = find_exe('magick') exe = which('magick')
if exe: if exe:
cmd = [exe, 'identify'] cmd = [exe, 'identify']
else: else:
@ -190,11 +190,11 @@ def render_image(
import tempfile import tempfile
has_multiple_frames = len(m) > 1 has_multiple_frames = len(m) > 1
get_multiple_frames = has_multiple_frames and not only_first_frame get_multiple_frames = has_multiple_frames and not only_first_frame
exe = find_exe('magick') exe = which('magick')
if exe: if exe:
cmd = [exe, 'convert'] cmd = [exe, 'convert']
else: else:
exe = find_exe('convert') exe = which('convert')
if exe is None: if exe is None:
raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH') raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH')
cmd = [exe] cmd = [exe]

View File

@ -12,6 +12,7 @@ from typing import (
import kitty.fast_data_types as fast_data_types import kitty.fast_data_types as fast_data_types
from .utils import which
from .constants import is_macos, kitty_base_dir, shell_path, terminfo_dir from .constants import is_macos, kitty_base_dir, shell_path, terminfo_dir
from .types import run_once from .types import run_once
@ -269,6 +270,7 @@ class Child:
# xterm, urxvt, konsole and gnome-terminal do not do it in my # xterm, urxvt, konsole and gnome-terminal do not do it in my
# testing. # testing.
argv[0] = ('-' + exe.split('/')[-1]) argv[0] = ('-' + exe.split('/')[-1])
exe = which(exe) or exe
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd) pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
os.close(slave) os.close(slave)
self.pid = pid self.pid = pid

View File

@ -9,13 +9,11 @@ from .child import Child
from .cli import parse_args from .cli import parse_args
from .cli_stub import LaunchCLIOptions from .cli_stub import LaunchCLIOptions
from .constants import resolve_custom_file from .constants import resolve_custom_file
from .fast_data_types import ( from .fast_data_types import patch_color_profiles, set_clipboard_string
get_options, patch_color_profiles, set_clipboard_string
)
from .options.utils import env as parse_env from .options.utils import env as parse_env
from .tabs import Tab from .tabs import Tab
from .types import run_once from .types import run_once
from .utils import find_exe, read_shell_environment, set_primary_selection, log_error from .utils import log_error, set_primary_selection, which
from .window import Watchers, Window from .window import Watchers, Window
try: try:
@ -379,12 +377,7 @@ def launch(
elif x == '@cursor-y': elif x == '@cursor-y':
x = str(screen.cursor.y + 1) x = str(screen.cursor.y + 1)
final_cmd.append(x) final_cmd.append(x)
exe = find_exe(final_cmd[0]) exe = which(final_cmd[0])
if not exe:
xenv = read_shell_environment(get_options())
if 'PATH' in xenv:
import shutil
exe = shutil.which(final_cmd[0], path=xenv['PATH'])
if exe: if exe:
final_cmd[0] = exe final_cmd[0] = exe
kw['cmd'] = final_cmd kw['cmd'] = final_cmd

View File

@ -512,7 +512,7 @@ def resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> Optional[st
return ' '.join(map(shlex.quote, editor_cmd)) return ' '.join(map(shlex.quote, editor_cmd))
if shell_env is os.environ: if shell_env is os.environ:
q = find_exe(editor_exe) q = which(editor_exe, only_system=True)
if q: if q:
return patched(q) return patched(q)
elif 'PATH' in shell_env: elif 'PATH' in shell_env:
@ -602,7 +602,7 @@ def resolved_shell(opts: Optional[Options] = None) -> List[str]:
@run_once @run_once
def system_paths_on_macos() -> List[str]: def system_paths_on_macos() -> Tuple[str, ...]:
entries, seen = [], set() entries, seen = [], set()
def add_from_file(x: str) -> None: def add_from_file(x: str) -> None:
@ -624,23 +624,51 @@ def system_paths_on_macos() -> List[str]:
for name in sorted(files): for name in sorted(files):
add_from_file(os.path.join('/etc/paths.d', name)) add_from_file(os.path.join('/etc/paths.d', name))
add_from_file('/etc/paths') add_from_file('/etc/paths')
return entries return tuple(entries)
@lru_cache(maxsize=32) def which(name: str, only_system: bool = False) -> Optional[str]:
def find_exe(name: str) -> Optional[str]:
import shutil import shutil
ans = shutil.which(name) if os.sep in name:
if ans is None: return name
# In case PATH is messed up paths = []
if is_macos: ep = os.environ.get('PATH')
paths = system_paths_on_macos() if ep:
else: paths = os.pathsep.split(ep)
paths = ['/usr/local/bin', '/opt/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'] paths.append(os.path.expanduser('~/.local/bin'))
paths.insert(0, os.path.expanduser('~/.local/bin')) paths.append(os.path.expanduser('~/bin'))
path = os.pathsep.join(paths) + os.pathsep + os.defpath ans = shutil.which(name, path=os.pathsep.join(paths))
ans = shutil.which(name, path=path) if ans:
return ans return ans
# In case PATH is messed up try a default set of paths
if is_macos:
system_paths = system_paths_on_macos()
else:
system_paths = ('/usr/local/bin', '/opt/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin')
tried_paths = set(paths)
system_paths = tuple(x for x in system_paths if x not in tried_paths)
if system_paths:
ans = shutil.which(name, path=os.pathsep.join(system_paths))
if ans:
return ans
tried_paths |= set(system_paths)
if only_system:
return None
from .fast_data_types import get_options
try:
opts = get_options()
except RuntimeError:
return None
shell_env = read_shell_environment(opts)
for xenv in (shell_env, opts.env):
q = xenv.get('PATH')
if q:
paths = [x for x in xenv['PATH'].split(os.pathsep) if x not in tried_paths]
ans = shutil.which(name, path=os.pathsep.join(paths))
if ans:
return ans
tried_paths |= set(paths)
return None
def read_shell_environment(opts: Optional[Options] = None) -> Dict[str, str]: def read_shell_environment(opts: Optional[Options] = None) -> Dict[str, str]: