Start work on a unified interface for launching processes

This commit is contained in:
Kovid Goyal 2019-11-13 09:58:28 +05:30
parent a649eb2a48
commit 6aa82d82ad
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 182 additions and 15 deletions

View File

@ -1006,14 +1006,20 @@ class Boss:
def _new_window(self, args, cwd_from=None):
tab = self.active_tab
if tab is not None:
allow_remote_control = False
location = None
if args and args[0].startswith('!'):
location = args[0][1:].lower()
args = args[1:]
if args and args[0] == '@':
args = args[1:]
allow_remote_control = True
if args:
return tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from), location=location)
return tab.new_special_window(
self.args_to_special_window(args, cwd_from=cwd_from),
location=location, allow_remote_control=allow_remote_control)
else:
return tab.new_window(cwd_from=cwd_from, location=location)
return tab.new_window(cwd_from=cwd_from, location=location, allow_remote_control=allow_remote_control)
def new_window(self, *args):
self._new_window(args)
@ -1025,6 +1031,11 @@ class Boss:
cwd_from = w.child.pid_for_cwd if w is not None else None
self._new_window(args, cwd_from=cwd_from)
def launch(self, *args):
from kitty.launch import parse_launch_args, launch
opts, args = parse_launch_args(args)
launch(self, opts, args)
def move_tab_forward(self):
tm = self.active_tab_manager
if tm is not None:

View File

@ -160,12 +160,8 @@ class Child:
child_fd = pid = None
forked = False
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None):
self.allow_remote_control = False
if argv and argv[0] == '@':
self.allow_remote_control = True
if len(argv) > 1:
argv = argv[1:]
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None, allow_remote_control=False):
self.allow_remote_control = allow_remote_control
self.argv = argv
if cwd_from is not None:
try:
@ -271,6 +267,13 @@ class Child:
except Exception:
return list(self.argv)
@property
def foreground_cmdline(self):
try:
return cmdline_of_process(self.pid_for_cwd) or self.cmdline
except Exception:
return self.cmdline
@property
def environ(self):
try:
@ -296,3 +299,14 @@ class Child:
def foreground_cwd(self):
with suppress(Exception):
return cwd_of_process(self.pid_for_cwd) or None
@property
def foreground_environ(self):
try:
return environ_of_process(self.pid_for_cwd)
except Exception:
try:
return environ_of_process(self.pid)
except Exception:
pass
return {}

View File

@ -50,7 +50,8 @@ func_with_args, args_funcs = key_func()
@func_with_args(
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd'
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
'launch'
)
def shlex_parse(func, rest):
return func, to_cmdline(rest)

140
kitty/launch.py Normal file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
from kitty.cli import parse_args
def options_spec():
if not hasattr(options_spec, 'ans'):
OPTIONS = '''
--window-title --title
The title to set for the new window. By default, title is controlled by the
child process.
--tab-title
The title for the new tab if launching in a new tab. By default, the title
of the actie window in the tab is used as the tab title.
--type
type=choices
default=window
choices=window,tab,os-window
Where to launch the child process, in a new kitty window in the current tab,
a new tab, or a new OS window.
--cwd
The working directory for the newly launched child. Use the special value
:code:`current` to use the working directory of the currently active window.
--env
type=list
Environment variables to set in the child process. Can be specified multiple
times to set different environment variables.
Syntax: :italic:`name=value`.
--copy-colors
type=bool-set
Set the colors of the newly created window to be the same as the colors in the
currently active window.
--copy-cmdline
type=bool-set
Ignore any specified command line and instead use the command line from the
currently active window.
--copy-env
type=bool-set
Copy the environment variables from the currently active window into the
newly launched child process.
--location
type=choices
default=last
choices=first,neighbor,last
Where to place the newly created window when it is added to a tab which
already has existing windows in it.
--allow-remote-control
type=bool-set
Programs running in this window can control kitty (if remote control is
enabled). Note that any program with the right level of permissions can still
write to the pipes of any other program on the same computer and therefore can
control kitty. It can, however, be useful to block programs running on other
computers (for example, over ssh) or as other users.
'''
options_spec.ans = OPTIONS
return options_spec.ans
def parse_launch_args(args):
args = list(args or ())
try:
opts, args = parse_args(args=args, ospec=options_spec)
except SystemExit as e:
raise ValueError from e
return opts, args
def get_env(opts, active_child):
env = {}
if opts.copy_env and active_child:
env.update(active_child.foreground_environ)
for x in opts.env:
parts = x.split('=', 1)
if len(parts) == 2:
env[parts[0]] = parts[1]
return env
def launch(boss, opts, args):
if opts.type == 'tab':
tm = boss.active_tab_manager
tab = tm.new_tab(empty_tab=True)
if opts.tab_title:
tab.set_title(opts.tab_title)
elif opts.type == 'os-window':
oswid = boss.add_os_window()
tm = boss.os_window_map[oswid]
tab = tm.new_tab(empty_tab=True)
if opts.tab_title:
tab.set_title(opts.tab_title)
else:
tab = boss.active_tab
active = boss.active_window_for_cwd
active_child = getattr(active, 'child', None)
kw = {
'env': get_env(opts, active_child) or None,
'allow_remote_control': opts.allow_remote_control
}
if opts.cwd:
if opts.cwd == 'current':
if active_child:
kw['cwd_from'] = active_child.pid_for_cwd
else:
kw['cwd'] = opts.cwd
if opts.location != 'last':
kw['location'] = opts.location
if opts.window_title:
kw['override_title'] = opts.window_title
if opts.copy_colors and active:
kw['copy_colors_from'] = active
cmd = args or None
if opts.copy_cmdline and active_child:
cmd = active_child.foreground_cmdline
if cmd:
kw['cmd'] = cmd
return tab.new_window(**kw)

View File

@ -236,7 +236,7 @@ class Tab: # {{{
if self.current_layout.remove_all_biases():
self.relayout()
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None, env=None):
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None, env=None, allow_remote_control=False):
if cmd is None:
if use_shell:
cmd = resolved_shell(self.opts)
@ -252,7 +252,7 @@ class Tab: # {{{
except Exception:
import traceback
traceback.print_exc()
ans = Child(cmd, cwd or self.cwd, self.opts, stdin, fenv, cwd_from)
ans = Child(cmd, cwd or self.cwd, self.opts, stdin, fenv, cwd_from, allow_remote_control=allow_remote_control)
ans.fork()
return ans
@ -263,9 +263,10 @@ class Tab: # {{{
def new_window(
self, use_shell=True, cmd=None, stdin=None, override_title=None,
cwd_from=None, cwd=None, overlay_for=None, env=None, location=None,
copy_colors_from=None
copy_colors_from=None, allow_remote_control=False
):
child = self.launch_child(use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env)
child = self.launch_child(
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env, allow_remote_control=allow_remote_control)
window = Window(self, child, self.opts, self.args, override_title=override_title, copy_colors_from=copy_colors_from)
if overlay_for is not None:
overlaid = next(w for w in self.windows if w.id == overlay_for)
@ -276,8 +277,8 @@ class Tab: # {{{
self._add_window(window, location=location)
return window
def new_special_window(self, special_window, location=None, copy_colors_from=None):
return self.new_window(False, *special_window, location=location, copy_colors_from=copy_colors_from)
def new_special_window(self, special_window, location=None, copy_colors_from=None, allow_remote_control=False):
return self.new_window(False, *special_window, location=location, copy_colors_from=copy_colors_from, allow_remote_control=allow_remote_control)
def close_window(self):
if self.windows: