kitty/kittens/runner.py
2020-03-12 08:10:51 +05:30

167 lines
5.0 KiB
Python

#!/usr/bin/env python3
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import importlib
import os
import sys
from functools import lru_cache, partial
from typing import Any, Dict, FrozenSet, List
aliases = {'url_hints': 'hints'}
def resolved_kitten(k: str) -> str:
return aliases.get(k, k).replace('-', '_')
def path_to_custom_kitten(config_dir: str, kitten: str) -> str:
path = os.path.expanduser(kitten)
if not os.path.isabs(path):
path = os.path.join(config_dir, path)
path = os.path.abspath(path)
return path
def import_kitten_main_module(config_dir: str, kitten: str) -> Dict[str, Any]:
if kitten.endswith('.py'):
path_modified = False
path = path_to_custom_kitten(config_dir, kitten)
if os.path.dirname(path):
sys.path.insert(0, os.path.dirname(path))
path_modified = True
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)
if path_modified:
del sys.path[0]
return {'start': g['main'], 'end': hr}
kitten = resolved_kitten(kitten)
m = importlib.import_module('kittens.{}.main'.format(kitten))
return {'start': getattr(m, 'main'), 'end': getattr(m, 'handle_result', lambda *a, **k: None)}
def create_kitten_handler(kitten: str, orig_args: List[str]) -> Any:
from kitty.constants import config_dir
kitten = resolved_kitten(kitten)
m = import_kitten_main_module(config_dir, kitten)
ans = partial(m['end'], [kitten] + orig_args)
setattr(ans, 'type_of_input', getattr(m['end'], 'type_of_input', None))
setattr(ans, 'no_ui', getattr(m['end'], 'no_ui', False))
return ans
def set_debug(kitten: str) -> None:
from kittens.tui.loop import debug
import builtins
setattr(builtins, 'debug', debug)
def launch(args: List[str]) -> None:
config_dir, kitten = args[:2]
kitten = resolved_kitten(kitten)
del args[:2]
args = [kitten] + args
os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir
from kittens.tui.operations import clear_screen, reset_mode
set_debug(kitten)
m = import_kitten_main_module(config_dir, kitten)
try:
result = m['start'](args)
finally:
sys.stdin = sys.__stdin__
print(reset_mode('ALTERNATE_SCREEN') + clear_screen(), end='')
if result is not None:
import json
data = json.dumps(result)
print('OK:', len(data), data)
sys.stderr.flush()
sys.stdout.flush()
def deserialize(output: str) -> Any:
import json
if output.startswith('OK: '):
try:
prefix, sz, rest = output.split(' ', 2)
return json.loads(rest[:int(sz)])
except Exception:
raise ValueError('Failed to parse kitten output: {!r}'.format(output))
def run_kitten(kitten: str, run_name: str = '__main__') -> None:
import runpy
original_kitten_name = kitten
kitten = resolved_kitten(kitten)
set_debug(kitten)
try:
runpy.run_module('kittens.{}.main'.format(kitten), run_name=run_name)
return
except ImportError:
pass
# 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)
raise SystemExit('No kitten named {}'.format(original_kitten_name))
m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__')
m['main'](sys.argv)
@lru_cache(maxsize=2)
def all_kitten_names() -> FrozenSet[str]:
n = []
import glob
base = os.path.dirname(os.path.abspath(__file__))
for x in glob.glob(os.path.join(base, '*', '__init__.py')):
q = os.path.basename(os.path.dirname(x))
if q != 'tui':
n.append(q)
return frozenset(n)
def list_kittens() -> None:
print('You must specify the name of a kitten to run')
print('Choose from:')
print()
for kitten in all_kitten_names():
print(kitten)
def get_kitten_cli_docs(kitten: str) -> Any:
setattr(sys, 'cli_docs', {})
run_kitten(kitten, run_name='__doc__')
ans = getattr(sys, 'cli_docs')
delattr(sys, 'cli_docs')
if 'help_text' in ans and 'usage' in ans and 'options' in ans:
return ans
def get_kitten_conf_docs(kitten: str) -> Any:
setattr(sys, 'all_options', None)
run_kitten(kitten, run_name='__conf__')
ans = getattr(sys, 'all_options')
delattr(sys, 'all_options')
return ans
def main() -> None:
try:
args = sys.argv[1:]
launch(args)
except Exception:
print('Unhandled exception running kitten:')
import traceback
traceback.print_exc()
input('Press Enter to quit...')