2019-05-20 13:38:06 +03:00
|
|
|
#!/usr/bin/env python3
|
2018-04-11 10:33:40 +03:00
|
|
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
|
|
|
|
|
|
|
|
import importlib
|
|
|
|
import os
|
|
|
|
import sys
|
2021-06-21 02:05:10 +03:00
|
|
|
from contextlib import contextmanager
|
2021-02-05 08:10:52 +03:00
|
|
|
from functools import partial
|
2023-02-17 17:20:57 +03:00
|
|
|
from typing import TYPE_CHECKING, Any, Dict, FrozenSet, Generator, List, Optional, cast
|
2018-04-11 10:33:40 +03:00
|
|
|
|
2022-03-02 05:49:47 +03:00
|
|
|
from kitty.constants import list_kitty_resources
|
2021-02-05 08:10:52 +03:00
|
|
|
from kitty.types import run_once
|
2021-12-09 15:43:07 +03:00
|
|
|
from kitty.utils import resolve_abs_or_config_path
|
2021-02-05 08:10:52 +03:00
|
|
|
|
2018-04-12 06:53:48 +03:00
|
|
|
aliases = {'url_hints': 'hints'}
|
2021-05-31 10:59:29 +03:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from kitty.conf.types import Definition
|
|
|
|
else:
|
|
|
|
Definition = object
|
2018-04-12 06:53:48 +03:00
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def resolved_kitten(k: str) -> str:
|
2021-07-04 15:35:54 +03:00
|
|
|
ans = aliases.get(k, k)
|
|
|
|
head, tail = os.path.split(ans)
|
|
|
|
tail = tail.replace('-', '_')
|
|
|
|
return os.path.join(head, tail)
|
2018-04-12 06:53:48 +03:00
|
|
|
|
2018-04-11 10:33:40 +03:00
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def path_to_custom_kitten(config_dir: str, kitten: str) -> str:
|
2021-12-10 05:06:09 +03:00
|
|
|
path = resolve_abs_or_config_path(kitten, conf_dir=config_dir)
|
2021-12-09 15:43:07 +03:00
|
|
|
return os.path.abspath(path)
|
2019-06-14 06:35:03 +03:00
|
|
|
|
|
|
|
|
2021-06-21 02:05:10 +03:00
|
|
|
@contextmanager
|
|
|
|
def preserve_sys_path() -> Generator[None, None, None]:
|
|
|
|
orig = sys.path[:]
|
|
|
|
try:
|
|
|
|
yield
|
|
|
|
finally:
|
|
|
|
if sys.path != orig:
|
|
|
|
del sys.path[:]
|
|
|
|
sys.path.extend(orig)
|
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def import_kitten_main_module(config_dir: str, kitten: str) -> Dict[str, Any]:
|
2018-04-11 10:33:40 +03:00
|
|
|
if kitten.endswith('.py'):
|
2021-06-21 02:05:10 +03:00
|
|
|
with preserve_sys_path():
|
|
|
|
path = path_to_custom_kitten(config_dir, kitten)
|
|
|
|
if os.path.dirname(path):
|
|
|
|
sys.path.insert(0, os.path.dirname(path))
|
|
|
|
with open(path) as f:
|
|
|
|
src = f.read()
|
|
|
|
code = compile(src, path, 'exec')
|
|
|
|
g = {'__name__': 'kitten'}
|
|
|
|
exec(code, g)
|
|
|
|
hr = g.get('handle_result', lambda *a, **kw: None)
|
2018-05-23 10:55:46 +03:00
|
|
|
return {'start': g['main'], 'end': hr}
|
2018-07-24 07:48:37 +03:00
|
|
|
|
|
|
|
kitten = resolved_kitten(kitten)
|
2021-10-21 10:13:55 +03:00
|
|
|
m = importlib.import_module(f'kittens.{kitten}.main')
|
2020-03-09 06:26:02 +03:00
|
|
|
return {'start': getattr(m, 'main'), 'end': getattr(m, 'handle_result', lambda *a, **k: None)}
|
2018-04-11 10:33:40 +03:00
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def create_kitten_handler(kitten: str, orig_args: List[str]) -> Any:
|
2018-04-11 10:33:40 +03:00
|
|
|
from kitty.constants import config_dir
|
2018-04-12 06:53:48 +03:00
|
|
|
kitten = resolved_kitten(kitten)
|
2018-04-11 10:33:40 +03:00
|
|
|
m = import_kitten_main_module(config_dir, kitten)
|
2018-05-18 20:25:42 +03:00
|
|
|
ans = partial(m['end'], [kitten] + orig_args)
|
2020-03-09 06:26:02 +03:00
|
|
|
setattr(ans, 'type_of_input', getattr(m['end'], 'type_of_input', None))
|
|
|
|
setattr(ans, 'no_ui', getattr(m['end'], 'no_ui', False))
|
2022-03-23 13:25:11 +03:00
|
|
|
setattr(ans, 'has_ready_notification', getattr(m['end'], 'has_ready_notification', False))
|
2018-05-18 20:25:42 +03:00
|
|
|
return ans
|
2018-04-11 10:33:40 +03:00
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def set_debug(kitten: str) -> None:
|
2018-05-06 11:18:40 +03:00
|
|
|
import builtins
|
2021-02-05 08:10:52 +03:00
|
|
|
|
|
|
|
from kittens.tui.loop import debug
|
2020-03-09 06:26:02 +03:00
|
|
|
setattr(builtins, 'debug', debug)
|
2018-05-06 11:18:40 +03:00
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def launch(args: List[str]) -> None:
|
2018-04-11 10:33:40 +03:00
|
|
|
config_dir, kitten = args[:2]
|
2018-04-12 06:53:48 +03:00
|
|
|
kitten = resolved_kitten(kitten)
|
2018-04-11 10:33:40 +03:00
|
|
|
del args[:2]
|
|
|
|
args = [kitten] + args
|
|
|
|
os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir
|
2018-05-06 11:18:40 +03:00
|
|
|
set_debug(kitten)
|
2018-04-11 10:33:40 +03:00
|
|
|
m = import_kitten_main_module(config_dir, kitten)
|
2018-04-23 04:49:40 +03:00
|
|
|
try:
|
|
|
|
result = m['start'](args)
|
|
|
|
finally:
|
|
|
|
sys.stdin = sys.__stdin__
|
2018-04-11 10:33:40 +03:00
|
|
|
if result is not None:
|
2022-03-23 12:03:53 +03:00
|
|
|
import base64
|
2023-01-09 14:17:42 +03:00
|
|
|
import json
|
2022-03-23 12:03:53 +03:00
|
|
|
data = base64.b85encode(json.dumps(result).encode('utf-8'))
|
|
|
|
sys.stdout.buffer.write(b'\x1bP@kitty-kitten-result|')
|
|
|
|
sys.stdout.buffer.write(data)
|
|
|
|
sys.stdout.buffer.write(b'\x1b\\')
|
2018-04-11 17:54:39 +03:00
|
|
|
sys.stderr.flush()
|
|
|
|
sys.stdout.flush()
|
2018-04-11 10:33:40 +03:00
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def run_kitten(kitten: str, run_name: str = '__main__') -> None:
|
2018-04-12 06:53:48 +03:00
|
|
|
import runpy
|
2019-06-14 06:35:03 +03:00
|
|
|
original_kitten_name = kitten
|
2018-04-12 06:53:48 +03:00
|
|
|
kitten = resolved_kitten(kitten)
|
2018-05-06 11:18:40 +03:00
|
|
|
set_debug(kitten)
|
2022-03-02 05:49:47 +03:00
|
|
|
if kitten in all_kitten_names():
|
2021-10-21 10:13:55 +03:00
|
|
|
runpy.run_module(f'kittens.{kitten}.main', run_name=run_name)
|
2019-06-14 06:35:03 +03:00
|
|
|
return
|
|
|
|
# Look for a custom kitten
|
|
|
|
if not kitten.endswith('.py'):
|
|
|
|
kitten += '.py'
|
|
|
|
from kitty.constants import config_dir
|
|
|
|
path = path_to_custom_kitten(config_dir, kitten)
|
|
|
|
if not os.path.exists(path):
|
|
|
|
print('Available builtin kittens:', file=sys.stderr)
|
|
|
|
for kitten in all_kitten_names():
|
|
|
|
print(kitten, file=sys.stderr)
|
2021-09-03 06:36:03 +03:00
|
|
|
raise SystemExit(f'No kitten named {original_kitten_name}')
|
2021-06-09 06:00:22 +03:00
|
|
|
m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__')
|
2022-06-07 10:23:39 +03:00
|
|
|
from kitty.fast_data_types import set_options
|
|
|
|
try:
|
|
|
|
m['main'](sys.argv)
|
|
|
|
finally:
|
|
|
|
set_options(None)
|
2018-06-02 09:54:21 +03:00
|
|
|
|
|
|
|
|
2021-02-05 08:10:52 +03:00
|
|
|
@run_once
|
2020-03-12 05:40:51 +03:00
|
|
|
def all_kitten_names() -> FrozenSet[str]:
|
2021-02-16 11:18:02 +03:00
|
|
|
ans = []
|
2021-10-19 06:44:58 +03:00
|
|
|
for name in list_kitty_resources('kittens'):
|
2023-10-21 05:25:23 +03:00
|
|
|
if '__' not in name and '.' not in name and name != 'tui':
|
2021-02-16 11:18:02 +03:00
|
|
|
ans.append(name)
|
|
|
|
return frozenset(ans)
|
2018-06-02 09:54:21 +03:00
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def list_kittens() -> None:
|
2018-06-09 11:17:49 +03:00
|
|
|
print('You must specify the name of a kitten to run')
|
|
|
|
print('Choose from:')
|
|
|
|
print()
|
|
|
|
for kitten in all_kitten_names():
|
|
|
|
print(kitten)
|
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def get_kitten_cli_docs(kitten: str) -> Any:
|
2020-03-09 06:26:02 +03:00
|
|
|
setattr(sys, 'cli_docs', {})
|
2018-06-02 09:54:21 +03:00
|
|
|
run_kitten(kitten, run_name='__doc__')
|
2020-03-09 06:26:02 +03:00
|
|
|
ans = getattr(sys, 'cli_docs')
|
|
|
|
delattr(sys, 'cli_docs')
|
2018-06-02 09:54:21 +03:00
|
|
|
if 'help_text' in ans and 'usage' in ans and 'options' in ans:
|
|
|
|
return ans
|
2018-04-12 06:53:48 +03:00
|
|
|
|
|
|
|
|
2022-09-19 11:01:32 +03:00
|
|
|
def get_kitten_wrapper_of(kitten: str) -> str:
|
|
|
|
setattr(sys, 'cli_docs', {})
|
|
|
|
run_kitten(kitten, run_name='__wrapper_of__')
|
|
|
|
ans = getattr(sys, 'cli_docs')
|
|
|
|
delattr(sys, 'cli_docs')
|
|
|
|
return ans.get('wrapper_of') or ''
|
|
|
|
|
|
|
|
|
2021-06-25 15:20:08 +03:00
|
|
|
def get_kitten_completer(kitten: str) -> Any:
|
|
|
|
run_kitten(kitten, run_name='__completer__')
|
|
|
|
ans = getattr(sys, 'kitten_completer', None)
|
|
|
|
if ans is not None:
|
|
|
|
delattr(sys, 'kitten_completer')
|
|
|
|
return ans
|
|
|
|
|
|
|
|
|
2023-02-17 17:20:57 +03:00
|
|
|
def get_kitten_conf_docs(kitten: str) -> Optional[Definition]:
|
2021-05-31 10:59:29 +03:00
|
|
|
setattr(sys, 'options_definition', None)
|
2018-06-05 08:14:18 +03:00
|
|
|
run_kitten(kitten, run_name='__conf__')
|
2021-05-31 10:59:29 +03:00
|
|
|
ans = getattr(sys, 'options_definition')
|
|
|
|
delattr(sys, 'options_definition')
|
|
|
|
return cast(Definition, ans)
|
2018-06-05 08:14:18 +03:00
|
|
|
|
|
|
|
|
2023-02-18 14:22:16 +03:00
|
|
|
def get_kitten_extra_cli_parsers(kitten: str) -> Dict[str,str]:
|
|
|
|
setattr(sys, 'extra_cli_parsers', {})
|
|
|
|
run_kitten(kitten, run_name='__extra_cli_parsers__')
|
|
|
|
ans = getattr(sys, 'extra_cli_parsers')
|
|
|
|
delattr(sys, 'extra_cli_parsers')
|
|
|
|
return cast(Dict[str, str], ans)
|
|
|
|
|
|
|
|
|
2020-03-12 05:40:51 +03:00
|
|
|
def main() -> None:
|
2018-04-11 10:33:40 +03:00
|
|
|
try:
|
|
|
|
args = sys.argv[1:]
|
|
|
|
launch(args)
|
|
|
|
except Exception:
|
|
|
|
print('Unhandled exception running kitten:')
|
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
2022-04-27 10:58:20 +03:00
|
|
|
input('Press Enter to quit')
|