#!/usr/bin/env python3 # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2018, Kovid Goyal import importlib import os import sys from contextlib import suppress from functools import partial aliases = {'url_hints': 'hints'} def resolved_kitten(k): return aliases.get(k, k).replace('-', '_') def path_to_custom_kitten(config_dir, kitten): 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, kitten): 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': m.main, 'end': getattr(m, 'handle_result', lambda *a, **k: None)} def create_kitten_handler(kitten, orig_args): 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) ans.type_of_input = getattr(m['end'], 'type_of_input', None) ans.no_ui = getattr(m['end'], 'no_ui', False) return ans def set_debug(kitten): from kittens.tui.loop import debug import builtins builtins.debug = debug def launch(args): 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): 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, run_name='__main__'): import runpy original_kitten_name = kitten kitten = resolved_kitten(kitten) set_debug(kitten) with suppress(ImportError): runpy.run_module('kittens.{}.main'.format(kitten), run_name=run_name) 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) 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) def all_kitten_names(): ans = getattr(all_kitten_names, 'ans', None) if ans is None: 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) all_kitten_names.ans = ans = frozenset(n) return ans def list_kittens(): 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): sys.cli_docs = {} run_kitten(kitten, run_name='__doc__') ans = sys.cli_docs del sys.cli_docs if 'help_text' in ans and 'usage' in ans and 'options' in ans: return ans def get_kitten_conf_docs(kitten): sys.all_options = None run_kitten(kitten, run_name='__conf__') ans = sys.all_options del sys.all_options return ans def main(): try: args = sys.argv[1:] launch(args) except Exception: print('Unhandled exception running kitten:') import traceback traceback.print_exc() input('Press Enter to quit...')