diff --git a/kittens/ssh/main.py b/kittens/ssh/main.py index ca4492161..8fe2508fa 100644 --- a/kittens/ssh/main.py +++ b/kittens/ssh/main.py @@ -7,7 +7,7 @@ import re import shlex import subprocess import sys -from typing import List, Tuple +from typing import List, Set, Tuple SHELL_SCRIPT = '''\ #!/bin/sh @@ -43,10 +43,12 @@ exec -a "-$shell_name" "$0" ''' -def get_ssh_cli(): +def get_ssh_cli() -> Tuple[Set[str], Set[str]]: other_ssh_args: List[str] = [] boolean_ssh_args: List[str] = [] - raw = subprocess.Popen(['ssh'], stderr=subprocess.PIPE).stderr.read().decode('utf-8') + stderr = subprocess.Popen(['ssh'], stderr=subprocess.PIPE).stderr + assert stderr is not None + raw = stderr.read().decode('utf-8') for m in re.finditer(r'\[(.+?)\]', raw): q = m.group(1) if len(q) < 2 or q[0] != '-': diff --git a/kittens/tui/handler.py b/kittens/tui/handler.py index e6a74bd21..1599ac8f7 100644 --- a/kittens/tui/handler.py +++ b/kittens/tui/handler.py @@ -3,19 +3,36 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal -from typing import TYPE_CHECKING, Callable, Optional, Type - +from typing import ( + TYPE_CHECKING, Any, Callable, ContextManager, Dict, Optional, Sequence, Type, + Union +) if TYPE_CHECKING: from kitty.utils import ScreenSize - ScreenSize + from .loop import TermManager, Loop, Debug, MouseEvent + from .images import ImageManager + from kitty.config import KeyAction + from kitty.boss import Boss + from kitty.key_encoding import KeyEvent + from types import TracebackType + ScreenSize, TermManager, Loop, Debug, KeyAction, KeyEvent, MouseEvent, TracebackType, Boss, ImageManager + import asyncio class Handler: - image_manager_class: Optional[Type['ImageManagerBase']] = None + image_manager_class: Optional[Type['ImageManager']] = None - def _initialize(self, screen_size: 'ScreenSize', term_manager, schedule_write, tui_loop, debug, image_manager=None): + def _initialize( + self, + screen_size: 'ScreenSize', + term_manager: 'TermManager', + schedule_write: Callable[[bytes], None], + tui_loop: 'Loop', + debug: 'Debug', + image_manager: Optional['ImageManager'] = None + ) -> None: from .operations import commander self.screen_size = screen_size self._term_manager = term_manager @@ -23,13 +40,18 @@ class Handler: self._schedule_write = schedule_write self.debug = debug self.cmd = commander(self) - self.image_manager = image_manager + self._image_manager = image_manager @property - def asyncio_loop(self): + def image_manager(self) -> 'ImageManager': + assert self._image_manager is not None + return self._image_manager + + @property + def asyncio_loop(self) -> 'asyncio.AbstractEventLoop': return self._tui_loop.asycio_loop - def add_shortcut(self, action, key, mods=None, is_text=False): + def add_shortcut(self, action: 'KeyAction', key: str, mods: Optional[int] = None, is_text: Optional[bool] = False) -> None: if not hasattr(self, '_text_shortcuts'): self._text_shortcuts, self._key_shortcuts = {}, {} if is_text: @@ -37,104 +59,95 @@ class Handler: else: self._key_shortcuts[(key, mods or 0)] = action - def shortcut_action(self, key_event_or_text): + def shortcut_action(self, key_event_or_text: Union[str, 'KeyEvent']) -> Optional['KeyAction']: if isinstance(key_event_or_text, str): return self._text_shortcuts.get(key_event_or_text) return self._key_shortcuts.get((key_event_or_text.key, key_event_or_text.mods)) - def __enter__(self): - if self.image_manager is not None: - self.image_manager.__enter__() + def __enter__(self) -> None: + if self._image_manager is not None: + self._image_manager.__enter__() self.debug.fobj = self self.initialize() - def __exit__(self, etype, value, tb): + def __exit__(self, etype: type, value: Exception, tb: 'TracebackType') -> None: del self.debug.fobj self.finalize() - if self.image_manager is not None: - self.image_manager.__exit__(etype, value, tb) + if self._image_manager is not None: + self._image_manager.__exit__(etype, value, tb) - def initialize(self): + def initialize(self) -> None: pass - def finalize(self): + def finalize(self) -> None: pass - def on_resize(self, screen_size): + def on_resize(self, screen_size: 'ScreenSize') -> None: self.screen_size = screen_size - def quit_loop(self, return_code=None): + def quit_loop(self, return_code: Optional[int] = None) -> None: self._tui_loop.quit(return_code) - def on_term(self): + def on_term(self) -> None: self._tui_loop.quit(1) - def on_text(self, text, in_bracketed_paste=False): + def on_text(self, text: str, in_bracketed_paste: bool = False) -> None: pass - def on_key(self, key_event): + def on_key(self, key_event: 'KeyEvent') -> None: pass - def on_mouse(self, mouse_event): + def on_mouse(self, mouse_event: 'MouseEvent') -> None: pass - def on_interrupt(self): + def on_interrupt(self) -> None: pass - def on_eot(self): + def on_eot(self) -> None: pass - def on_kitty_cmd_response(self, response): + def on_kitty_cmd_response(self, response: Dict) -> None: pass - def on_clipboard_response(self, text, from_primary=False): + def on_clipboard_response(self, text: str, from_primary: bool = False) -> None: pass - def on_capability_response(self, name, val): + def on_capability_response(self, name: str, val: str) -> None: pass - def write(self, data): + def write(self, data: Union[bytes, str]) -> None: if isinstance(data, str): data = data.encode('utf-8') self._schedule_write(data) - def print(self, *args, sep=' ', end='\r\n'): + def flush(self) -> None: + pass + + def print(self, *args: object, sep: str = ' ', end: str = '\r\n') -> None: data = sep.join(map(str, args)) + end self.write(data) - def suspend(self): + def suspend(self) -> ContextManager['TermManager']: return self._term_manager.suspend() -class ImageManagerBase: - - def __init__(self, handler: Handler): - pass - - def __enter__(self): - pass - - def __exit__(self, etype, value, tb): - pass - - class HandleResult: type_of_input: Optional[str] = None no_ui: bool = False - def __init__(self, impl, type_of_input: Optional[str], no_ui: bool): + def __init__(self, impl: Callable, type_of_input: Optional[str], no_ui: bool): self.impl = impl self.no_ui = no_ui self.type_of_input = type_of_input - def __call__(self, args, data, target_window_id, boss): + def __call__(self, args: Sequence[str], data: Dict, target_window_id: int, boss: 'Boss') -> Any: return self.impl(args, data, target_window_id, boss) -def result_handler(type_of_input: Optional[str] = None, no_ui=False) -> Callable[[Callable], HandleResult]: +def result_handler(type_of_input: Optional[str] = None, no_ui: bool = False) -> Callable[[Callable], HandleResult]: - def wrapper(impl): + def wrapper(impl: Callable) -> HandleResult: return HandleResult(impl, type_of_input, no_ui) return wrapper diff --git a/kittens/tui/images.py b/kittens/tui/images.py index 061acfd70..71bd4fac7 100644 --- a/kittens/tui/images.py +++ b/kittens/tui/images.py @@ -16,7 +16,6 @@ from typing import ( from kitty.utils import ScreenSize, fit_image -from .handler import ImageManagerBase from .operations import cursor try: @@ -192,7 +191,7 @@ class Placement(TypedDict): y: int -class ImageManager(ImageManagerBase): +class ImageManager: def __init__(self, handler: 'Handler'): self.image_id_counter = count() diff --git a/kittens/tui/line_edit.py b/kittens/tui/line_edit.py index 65a04922a..495d106b7 100644 --- a/kittens/tui/line_edit.py +++ b/kittens/tui/line_edit.py @@ -2,8 +2,10 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2018, Kovid Goyal +from typing import Callable, Tuple + from kitty.fast_data_types import truncate_point_for_length, wcswidth -from kitty.key_encoding import RELEASE, key_defs as K +from kitty.key_encoding import RELEASE, KeyEvent, key_defs as K HOME = K['HOME'] END = K['END'] @@ -15,21 +17,21 @@ RIGHT = K['RIGHT'] class LineEdit: - def __init__(self): + def __init__(self) -> None: self.clear() - def clear(self): + def clear(self) -> None: self.current_input = '' self.cursor_pos = 0 self.pending_bell = False - def split_at_cursor(self, delta=0): + def split_at_cursor(self, delta: int = 0) -> Tuple[str, str]: pos = max(0, self.cursor_pos + delta) x = truncate_point_for_length(self.current_input, pos) if pos else 0 before, after = self.current_input[:x], self.current_input[x:] return before, after - def write(self, write, prompt=''): + def write(self, write: Callable[[str], None], prompt: str = '') -> None: if self.pending_bell: write('\a') self.pending_bell = False @@ -37,7 +39,7 @@ class LineEdit: write(self.current_input) write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt))) - def add_text(self, text): + def add_text(self, text: str) -> None: if self.current_input: x = truncate_point_for_length(self.current_input, self.cursor_pos) if self.cursor_pos else 0 self.current_input = self.current_input[:x] + text + self.current_input[x:] @@ -45,10 +47,10 @@ class LineEdit: self.current_input = text self.cursor_pos += wcswidth(text) - def on_text(self, text, in_bracketed_paste): + def on_text(self, text: str, in_bracketed_paste: bool) -> None: self.add_text(text) - def backspace(self, num=1): + def backspace(self, num: int = 1) -> bool: before, after = self.split_at_cursor() nbefore = before[:-num] if nbefore != before: @@ -58,7 +60,7 @@ class LineEdit: self.pending_bell = True return False - def delete(self, num=1): + def delete(self, num: int = 1) -> bool: before, after = self.split_at_cursor() nafter = after[num:] if nafter != after: @@ -68,7 +70,7 @@ class LineEdit: self.pending_bell = True return False - def _left(self): + def _left(self) -> None: if not self.current_input: self.cursor_pos = 0 return @@ -76,7 +78,7 @@ class LineEdit: before, after = self.split_at_cursor(-1) self.cursor_pos = wcswidth(before) - def _right(self): + def _right(self) -> None: if not self.current_input: self.cursor_pos = 0 return @@ -87,33 +89,37 @@ class LineEdit: before, after = self.split_at_cursor(1) self.cursor_pos += 1 + int(wcswidth(before) == self.cursor_pos) - def _move_loop(self, func, num): + def _move_loop(self, func: Callable[[], None], num: int) -> bool: before = self.cursor_pos - while func() and num > 0: + changed = False + while num > 0: + func() + changed = self.cursor_pos != before + if not changed: + break num -= 1 - changed = self.cursor_pos != before if not changed: self.pending_bell = True return changed - def left(self, num=1): + def left(self, num: int = 1) -> bool: return self._move_loop(self._left, num) - def right(self, num=1): + def right(self, num: int = 1) -> bool: return self._move_loop(self._right, num) - def home(self): + def home(self) -> bool: if self.cursor_pos: self.cursor_pos = 0 return True return False - def end(self): + def end(self) -> bool: orig = self.cursor_pos self.cursor_pos = wcswidth(self.current_input) return self.cursor_pos != orig - def on_key(self, key_event): + def on_key(self, key_event: KeyEvent) -> bool: if key_event.type is RELEASE: return False elif key_event.key is HOME: diff --git a/kittens/tui/loop.py b/kittens/tui/loop.py index 3bb2d28f1..0681d599b 100644 --- a/kittens/tui/loop.py +++ b/kittens/tui/loop.py @@ -10,69 +10,90 @@ import re import selectors import signal import sys -from collections import namedtuple from contextlib import contextmanager from functools import partial -from typing import List +from typing import ( + TYPE_CHECKING, Any, Callable, Dict, Generator, List, NamedTuple, Optional, + Protocol +) from kitty.constants import is_macos from kitty.fast_data_types import ( close_tty, normal_tty, open_tty, parse_input_from_terminal, raw_tty ) from kitty.key_encoding import ( - ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, - decode_key_event, enter_key, key_defs as K + ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, decode_key_event, + enter_key, key_defs as K ) from kitty.utils import screen_size_function, write_all from .handler import Handler from .operations import init_state, reset_state +if TYPE_CHECKING: + from kitty.key_encoding import KeyEvent + from .images import ImageManager + KeyEvent, ImageManager C, D = K['C'], K['D'] -def debug(*a, **kw): - from base64 import standard_b64encode - buf = io.StringIO() - kw['file'] = buf - print(*a, **kw) - stext = buf.getvalue() - text = b'\x1bP@kitty-print|' + standard_b64encode(stext.encode('utf-8')) + b'\x1b\\' - fobj = getattr(debug, 'fobj', sys.stdout.buffer) - fobj.write(text) - if hasattr(fobj, 'flush'): +class BinaryWrite(Protocol): + + def write(self, data: bytes) -> None: + pass + + def flush(self) -> None: + pass + + +class Debug: + + fobj: Optional[BinaryWrite] = None + + def __call__(self, *a: Any, **kw: Any) -> None: + from base64 import standard_b64encode + buf = io.StringIO() + kw['file'] = buf + print(*a, **kw) + stext = buf.getvalue() + text = b'\x1bP@kitty-print|' + standard_b64encode(stext.encode('utf-8')) + b'\x1b\\' + fobj = self.fobj or sys.stdout.buffer + fobj.write(text) fobj.flush() +debug = Debug() + + class TermManager: - def __init__(self): - self.extra_finalize = None + def __init__(self) -> None: + self.extra_finalize: Optional[str] = None - def set_state_for_loop(self, set_raw=True): + def set_state_for_loop(self, set_raw: bool = True) -> None: if set_raw: raw_tty(self.tty_fd, self.original_termios) write_all(self.tty_fd, init_state()) - def reset_state_to_original(self): + def reset_state_to_original(self) -> None: normal_tty(self.tty_fd, self.original_termios) if self.extra_finalize: write_all(self.tty_fd, self.extra_finalize) write_all(self.tty_fd, reset_state()) @contextmanager - def suspend(self): + def suspend(self) -> Generator['TermManager', None, None]: self.reset_state_to_original() yield self self.set_state_for_loop() - def __enter__(self): + def __enter__(self) -> 'TermManager': self.tty_fd, self.original_termios = open_tty() self.set_state_for_loop(set_raw=False) return self - def __exit__(self, *a): + def __exit__(self, *a: object) -> None: self.reset_state_to_original() close_tty(self.tty_fd, self.original_termios) del self.tty_fd, self.original_termios @@ -80,7 +101,6 @@ class TermManager: LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16 DRAG = REPEAT -MouseEvent = namedtuple('MouseEvent', 'x y type buttons mods') bmap = {0: LEFT, 1: MIDDLE, 2: RIGHT} MOTION_INDICATOR = 1 << 5 EXTRA_BUTTON_INDICATOR = 1 << 6 @@ -89,10 +109,18 @@ ALT_INDICATOR = 1 << 3 CTRL_INDICATOR = 1 << 4 -def decode_sgr_mouse(text): - cb, x, y = text.split(';') - m, y = y[-1], y[:-1] - cb, x, y = map(int, (cb, x, y)) +class MouseEvent(NamedTuple): + x: int + y: int + type: int + buttons: int + mods: int + + +def decode_sgr_mouse(text: str) -> MouseEvent: + cb_, x_, y_ = text.split(';') + m, y_ = y_[-1], y_[:-1] + cb, x, y = map(int, (cb_, x_, y_)) typ = RELEASE if m == 'm' else (DRAG if cb & MOTION_INDICATOR else PRESS) buttons = 0 cb3 = cb & 3 @@ -113,10 +141,10 @@ def decode_sgr_mouse(text): class UnhandledException(Handler): - def __init__(self, tb): + def __init__(self, tb: str) -> None: self.tb = tb - def initialize(self): + def initialize(self) -> None: self.cmd.clear_screen() self.cmd.set_scrolling_region() self.cmd.set_cursor_visible(True) @@ -125,37 +153,45 @@ class UnhandledException(Handler): self.write('\r\n') self.write('Press the Enter key to quit') - def on_key(self, key_event): + def on_key(self, key_event: 'KeyEvent') -> None: if key_event is enter_key: self.quit_loop(1) - def on_interrupt(self): + def on_interrupt(self) -> None: self.quit_loop(1) on_eot = on_term = on_interrupt class SignalManager: - def __init__(self, loop, on_winch, on_interrupt, on_term): + def __init__( + self, + loop: asyncio.AbstractEventLoop, + on_winch: Callable, + on_interrupt: Callable, + on_term: Callable + ) -> None: self.asycio_loop = loop self.on_winch, self.on_interrupt, self.on_term = on_winch, on_interrupt, on_term - def __enter__(self): + def __enter__(self) -> None: tuple(map(lambda x: self.asycio_loop.add_signal_handler(*x), ( (signal.SIGWINCH, self.on_winch), (signal.SIGINT, self.on_interrupt), (signal.SIGTERM, self.on_term) ))) - def __exit__(self, *a): + def __exit__(self, *a: Any) -> None: tuple(map(self.asycio_loop.remove_signal_handler, ( signal.SIGWINCH, signal.SIGINT, signal.SIGTERM))) class Loop: - def __init__(self, - sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'): + def __init__( + self, + sanitize_bracketed_paste: str = '[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]' + ): if is_macos: # On macOS PTY devices are not supported by the KqueueSelector and # the PollSelector is broken, causes 100% CPU usage @@ -177,7 +213,7 @@ class Loop: if self.sanitize_bracketed_paste: self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste) - def _read_ready(self, handler, fd): + def _read_ready(self, handler: Handler, fd: int) -> None: try: bdata = os.read(fd, io.DEFAULT_BUFFER_SIZE) except BlockingIOError: @@ -198,7 +234,7 @@ class Loop: del self.handler # terminal input callbacks {{{ - def _on_text(self, text): + def _on_text(self, text: str) -> None: if self.in_bracketed_paste and self.sanitize_bracketed_paste: text = self.sanitize_ibp_pat.sub('', text) @@ -217,8 +253,7 @@ class Loop: elif chunk: self.handler.on_text(chunk, self.in_bracketed_paste) - def _on_dcs(self, dcs): - debug(dcs) + def _on_dcs(self, dcs: str) -> None: if dcs.startswith('@kitty-cmd'): import json self.handler.on_kitty_cmd_response(json.loads(dcs[len('@kitty-cmd'):])) @@ -233,7 +268,7 @@ class Loop: continue self.handler.on_capability_response(name, val) - def _on_csi(self, csi): + def _on_csi(self, csi: str) -> None: q = csi[-1] if q in 'mM': if csi.startswith('<'): @@ -250,10 +285,10 @@ class Loop: elif csi == '201~': self.in_bracketed_paste = False - def _on_pm(self, pm): + def _on_pm(self, pm: str) -> None: pass - def _on_osc(self, osc): + def _on_osc(self, osc: str) -> None: m = re.match(r'(\d+);', osc) if m is not None: code = int(m.group(1)) @@ -264,7 +299,7 @@ class Loop: from base64 import standard_b64decode self.handler.on_clipboard_response(standard_b64decode(rest).decode('utf-8'), from_primary) - def _on_apc(self, apc): + def _on_apc(self, apc: str) -> None: if apc.startswith('K'): try: k = decode_key_event(apc) @@ -284,7 +319,7 @@ class Loop: self.handler.image_manager.handle_response(apc) # }}} - def _write_ready(self, handler, fd): + def _write_ready(self, handler: Handler, fd: int) -> None: if len(self.write_buf) > self.iov_limit: self.write_buf[self.iov_limit - 1] = b''.join(self.write_buf[self.iov_limit - 1:]) del self.write_buf[self.iov_limit:] @@ -312,24 +347,24 @@ class Loop: break del self.write_buf[:consumed] - def quit(self, return_code=None): + def quit(self, return_code: Optional[int] = None) -> None: if return_code is not None: self.return_code = return_code self.asycio_loop.stop() - def loop_impl(self, handler, term_manager, image_manager=None): + def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: Optional['ImageManager'] = None) -> Optional[str]: self.write_buf = [] tty_fd = term_manager.tty_fd tb = None self.waiting_for_writes = True - def schedule_write(data): + def schedule_write(data: bytes) -> None: self.write_buf.append(data) if not self.waiting_for_writes: self.asycio_loop.add_writer(tty_fd, self._write_ready, handler, tty_fd) self.waiting_for_writes = True - def handle_exception(loop, context): + def handle_exception(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None: nonlocal tb loop.stop() tb = context['message'] @@ -351,10 +386,10 @@ class Loop: self.asycio_loop.remove_writer(tty_fd) return tb - def loop(self, handler): - tb = None + def loop(self, handler: Handler) -> None: + tb: Optional[str] = None - def _on_sigwinch(): + def _on_sigwinch() -> None: self._get_screen_size.changed = True handler.screen_size = self._get_screen_size() handler.on_resize(handler.screen_size) @@ -376,5 +411,5 @@ class Loop: self.return_code = 1 self._report_error_loop(tb, term_manager) - def _report_error_loop(self, tb, term_manager): + def _report_error_loop(self, tb: str, term_manager: TermManager) -> None: self.loop_impl(UnhandledException(tb), term_manager) diff --git a/kittens/tui/operations.py b/kittens/tui/operations.py index 02252aa38..efcd48a92 100644 --- a/kittens/tui/operations.py +++ b/kittens/tui/operations.py @@ -346,6 +346,9 @@ def request_from_clipboard(use_primary: bool = False) -> str: return '\x1b]52;{};?\x07'.format('p' if use_primary else 'c') +# Boilerplate to make operations availble via Handler.cmd {{{ + + def writer(handler: 'Handler', func: Callable) -> Callable: @wraps(func) def f(*a: Any, **kw: Any) -> None: @@ -373,6 +376,8 @@ def as_type_stub() -> str: 'from typing import * # noqa', 'from kitty.utils import ScreenSize', 'from kittens.tui.images import GraphicsCommand', + 'from kitty.rgb import Color', + 'import kitty.rgb', ] methods = [] for name, func in all_cmds.items(): @@ -383,3 +388,4 @@ def as_type_stub() -> str: ans += ['', '', 'class CMD:'] + methods return '\n'.join(ans) + '\n\n\n' +# }}} diff --git a/kitty/key_encoding.py b/kitty/key_encoding.py index 29315ec67..f34a00119 100644 --- a/kitty/key_encoding.py +++ b/kitty/key_encoding.py @@ -3,7 +3,7 @@ # License: GPL v3 Copyright: 2017, Kovid Goyal import string -from collections import namedtuple +from typing import NamedTuple from . import fast_data_types as defines from .key_names import key_name_aliases @@ -451,9 +451,14 @@ def update_encoding(): subprocess.check_call(['yapf', '-i', __file__]) +class KeyEvent(NamedTuple): + type: int + mods: int + key: str + + PRESS, REPEAT, RELEASE = 1, 2, 4 SHIFT, ALT, CTRL, SUPER = 1, 2, 4, 8 -KeyEvent = namedtuple('KeyEvent', 'type mods key') type_map = {'p': PRESS, 't': REPEAT, 'r': RELEASE} rtype_map = {v: k for k, v in type_map.items()} mod_map = {c: i for i, c in enumerate('ABCDEFGHIJKLMNOP')} diff --git a/kitty/utils.py b/kitty/utils.py index 039be7353..ae533c82e 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -14,7 +14,7 @@ from contextlib import suppress from functools import lru_cache from time import monotonic from typing import ( - Any, Dict, Generator, List, NamedTuple, Optional, Tuple, cast + Any, Dict, Generator, List, NamedTuple, Optional, Tuple, Union, cast ) from .constants import ( @@ -382,7 +382,7 @@ def parse_address_spec(spec): return family, address, socket_path -def write_all(fd, data): +def write_all(fd: int, data: Union[str, bytes]) -> None: if isinstance(data, str): data = data.encode('utf-8') while data: diff --git a/setup.cfg b/setup.cfg index 1a51df432..45100a4ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,7 +27,7 @@ warn_unused_configs = True check_untyped_defs = True # disallow_untyped_defs = True -[mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kitty.launch,kitty.child,kitty.cli,kitty.config] +[mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kittens.tui.*,kitty.launch,kitty.child,kitty.cli,kitty.config,kitty.choose_entry] disallow_untyped_defs = True [mypy-conf] diff --git a/setup.py b/setup.py index 4b0c90398..bae30cd3a 100755 --- a/setup.py +++ b/setup.py @@ -196,11 +196,14 @@ def get_sanitize_args(cc, ccver): return sanitize_args -def test_compile(cc, *cflags, src=None): +def test_compile(cc: str, *cflags: str, src: Optional[str] = None) -> bool: src = src or 'int main(void) { return 0; }' p = subprocess.Popen([cc] + list(cflags) + ['-x', 'c', '-o', os.devnull, '-'], stdin=subprocess.PIPE) + stdin = p.stdin + assert stdin is not None try: - p.stdin.write(src.encode('utf-8')), p.stdin.close() + stdin.write(src.encode('utf-8')) + stdin.close() except BrokenPipeError: return False return p.wait() == 0