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,
HandlerType
)
from kitty.utils import ScreenSize, find_exe, fit_image
from kitty.utils import ScreenSize, fit_image, which
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:
import json
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:
cmd = [exe, 'identify']
else:
@ -190,11 +190,11 @@ def render_image(
import tempfile
has_multiple_frames = len(m) > 1
get_multiple_frames = has_multiple_frames and not only_first_frame
exe = find_exe('magick')
exe = which('magick')
if exe:
cmd = [exe, 'convert']
else:
exe = find_exe('convert')
exe = which('convert')
if exe is None:
raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH')
cmd = [exe]

View File

@ -12,6 +12,7 @@ from typing import (
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 .types import run_once
@ -269,6 +270,7 @@ class Child:
# xterm, urxvt, konsole and gnome-terminal do not do it in my
# testing.
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)
os.close(slave)
self.pid = pid

View File

@ -9,13 +9,11 @@ from .child import Child
from .cli import parse_args
from .cli_stub import LaunchCLIOptions
from .constants import resolve_custom_file
from .fast_data_types import (
get_options, patch_color_profiles, set_clipboard_string
)
from .fast_data_types import patch_color_profiles, set_clipboard_string
from .options.utils import env as parse_env
from .tabs import Tab
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
try:
@ -379,12 +377,7 @@ def launch(
elif x == '@cursor-y':
x = str(screen.cursor.y + 1)
final_cmd.append(x)
exe = find_exe(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'])
exe = which(final_cmd[0])
if exe:
final_cmd[0] = exe
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))
if shell_env is os.environ:
q = find_exe(editor_exe)
q = which(editor_exe, only_system=True)
if q:
return patched(q)
elif 'PATH' in shell_env:
@ -602,7 +602,7 @@ def resolved_shell(opts: Optional[Options] = None) -> List[str]:
@run_once
def system_paths_on_macos() -> List[str]:
def system_paths_on_macos() -> Tuple[str, ...]:
entries, seen = [], set()
def add_from_file(x: str) -> None:
@ -624,23 +624,51 @@ def system_paths_on_macos() -> List[str]:
for name in sorted(files):
add_from_file(os.path.join('/etc/paths.d', name))
add_from_file('/etc/paths')
return entries
return tuple(entries)
@lru_cache(maxsize=32)
def find_exe(name: str) -> Optional[str]:
def which(name: str, only_system: bool = False) -> Optional[str]:
import shutil
ans = shutil.which(name)
if ans is None:
# In case PATH is messed up
if is_macos:
paths = system_paths_on_macos()
else:
paths = ['/usr/local/bin', '/opt/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin']
paths.insert(0, os.path.expanduser('~/.local/bin'))
path = os.pathsep.join(paths) + os.pathsep + os.defpath
ans = shutil.which(name, path=path)
return ans
if os.sep in name:
return name
paths = []
ep = os.environ.get('PATH')
if ep:
paths = os.pathsep.split(ep)
paths.append(os.path.expanduser('~/.local/bin'))
paths.append(os.path.expanduser('~/bin'))
ans = shutil.which(name, path=os.pathsep.join(paths))
if 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]: