From c817ba9eaea52bc813cfbd38e0c0073b92c65b50 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 9 Mar 2020 13:32:45 +0530 Subject: [PATCH] more typing work --- kittens/tui/images.py | 11 ++++---- kitty/config.py | 20 +++++++-------- kitty/config_data.py | 2 +- kitty/fast_data_types.pyi | 40 ++++++++++++++++++++++++++--- kitty/fonts/core_text.py | 54 ++++++++++++++++++++++++--------------- kitty/fonts/fontconfig.py | 2 +- kitty/fonts/render.py | 33 +++++++++++++++++++----- kitty/key_names.py | 22 ++++++++++------ kitty/marks.py | 24 +++++++++-------- kitty/options_stub.py | 3 ++- kitty/rc/detach_tab.py | 3 ++- kitty/tab_bar.py | 6 ++--- kitty/utils.py | 16 +++++++----- kitty/window.py | 8 ++++-- 14 files changed, 163 insertions(+), 81 deletions(-) diff --git a/kittens/tui/images.py b/kittens/tui/images.py index e805d655e..51b3978f8 100644 --- a/kittens/tui/images.py +++ b/kittens/tui/images.py @@ -7,13 +7,14 @@ import os import sys from base64 import standard_b64encode from collections import defaultdict, deque -from itertools import count from contextlib import suppress +from itertools import count +from typing import Any, DefaultDict, Deque, Dict, Tuple from kitty.utils import fit_image -from .operations import cursor from .handler import ImageManagerBase +from .operations import cursor try: fsenc = sys.getfilesystemencoding() or 'utf-8' @@ -64,7 +65,7 @@ def run_imagemagick(path, cmd, keep_stdout=True): def identify(path): p = run_imagemagick(path, ['identify', '-format', '%m %w %h %A', '--', path]) - parts = tuple(filter(None, p.stdout.decode('utf-8').split())) + parts: Tuple[str, ...] = tuple(filter(None, p.stdout.decode('utf-8').split())) mode = 'rgb' if parts[3].lower() == 'false' else 'rgba' return ImageData(parts[0].lower(), int(parts[1]), int(parts[2]), mode) @@ -106,7 +107,7 @@ def can_display_images(): ans = getattr(can_display_images, 'ans', None) if ans is None: ans = shutil.which('convert') is not None - can_display_images.ans = ans + setattr(can_display_images, 'ans', ans) return ans @@ -123,7 +124,7 @@ class ImageManager(ImageManagerBase): self.image_id_to_image_data = {} self.image_id_to_converted_data = {} self.transmission_status = {} - self.placements_in_flight = defaultdict(deque) + self.placements_in_flight: DefaultDict[int, Deque[Dict[str, Any]]] = defaultdict(deque) @property def next_image_id(self): diff --git a/kitty/config.py b/kitty/config.py index 51fdcb5e0..bb5c5c1e9 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -31,15 +31,13 @@ if TYPE_CHECKING: SequenceMap, KeyMap -def parse_shortcut(sc): +def parse_shortcut(sc: str) -> Tuple[int, bool, Optional[int]]: parts = sc.split('+') mods = 0 if len(parts) > 1: mods = parse_mods(parts[:-1], sc) - if mods is None: - return None, None, None - key = parts[-1].upper() - key = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(key, key), None) + q = parts[-1].upper() + key: Optional[int] = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(q, q), None) is_native = False if key is None: q = parts[-1] @@ -47,7 +45,7 @@ def parse_shortcut(sc): with suppress(Exception): key = int(q, 16) else: - key = get_key_name_lookup()(q) + key = get_key_name_lookup()(q, False) is_native = key is not None return mods, is_native, key @@ -369,7 +367,7 @@ def parse_key(val, key_definitions): return is_sequence = sequence_sep in sc if is_sequence: - trigger = None + trigger: Optional[Tuple[int, bool, int]] = None restl: List[Tuple[int, bool, int]] = [] for part in sc.split(sequence_sep): mods, is_native, key = parse_shortcut(part) @@ -484,8 +482,8 @@ def handle_symbol_map(key, val, ans): class FontFeature(str): - def __new__(cls, name, parsed): - ans = str.__new__(cls, name) + def __new__(cls, name: str, parsed: bytes): + ans = str.__new__(cls, name) # type: ignore ans.parsed = parsed return ans @@ -531,7 +529,7 @@ def handle_clear_all_shortcuts(key, val, ans): @deprecated_handler('x11_hide_window_decorations', 'macos_hide_titlebar') def handle_deprecated_hide_window_decorations_aliases(key, val, ans): if not hasattr(handle_deprecated_hide_window_decorations_aliases, key): - handle_deprecated_hide_window_decorations_aliases.key = True + setattr(handle_deprecated_hide_window_decorations_aliases, 'key', True) log_error('The option {} is deprecated. Use hide_window_decorations instead.'.format(key)) if to_bool(val): if is_macos and key == 'macos_hide_titlebar' or (not is_macos and key == 'x11_hide_window_decorations'): @@ -541,7 +539,7 @@ def handle_deprecated_hide_window_decorations_aliases(key, val, ans): @deprecated_handler('macos_show_window_title_in_menubar') def handle_deprecated_macos_show_window_title_in_menubar_alias(key, val, ans): if not hasattr(handle_deprecated_macos_show_window_title_in_menubar_alias, key): - handle_deprecated_macos_show_window_title_in_menubar_alias.key = True + setattr(handle_deprecated_macos_show_window_title_in_menubar_alias, 'key', True) log_error('The option {} is deprecated. Use macos_show_window_title_in menubar instead.'.format(key)) macos_show_window_title_in = ans.get('macos_show_window_title_in', 'all') if to_bool(val): diff --git a/kitty/config_data.py b/kitty/config_data.py index 4b623c114..5b461e094 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -28,7 +28,7 @@ mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER', '⌥': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'} -def parse_mods(parts, sc): +def parse_mods(parts: Iterable[str], sc: str) -> int: def map_mod(m): return mod_map.get(m, m) diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 071fa4b07..c29176400 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -4,6 +4,7 @@ from typing import ( ) from kitty.boss import Boss +from kitty.fonts.render import FontObject from kitty.options_stub import Options # Constants {{{ @@ -420,7 +421,20 @@ def fc_match( pass -def coretext_all_fonts() -> Tuple[Dict[str, Any], ...]: +class CoreTextFont(TypedDict): + path: str + postscript_name: str + family: str + style: str + bold: bool + italic: bool + monospace: bool + weight: float + width: float + traits: int + + +def coretext_all_fonts() -> Tuple[CoreTextFont, ...]: pass @@ -635,6 +649,14 @@ class ColorProfile: def reset_color(self, num: int) -> None: pass + def update_ansi_color_table(self, val: List[int]) -> None: + pass + + def set_configured_colors( + self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, cursor_text_uses_bg: int = 0, highlight_fg: int = 0, highlight_bg: int = 0 + ) -> None: + pass + def patch_color_profiles( spec: Dict[str, int], cursor_text_color: Optional[Union[bool, int]], @@ -866,11 +888,11 @@ def set_font_data( Tuple[int, Union[bytearray, bytes]]], prerender_func: Callable[ [int, int, int, int, int, float, float, float, float], - Tuple[int, ...]], descriptor_for_idx: Callable[[int], Tuple[dict, bool, - bool]], + Tuple[int, ...]], + descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]], bold: int, italic: int, bold_italic: int, num_symbol_fonts: int, symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float, - font_feature_settings: Dict[str, Tuple[bytes, ...]] + font_feature_settings: Dict[str, Tuple[str, ...]] ): pass @@ -902,6 +924,10 @@ class LineBuf: class Cursor: x: int y: int + bg: int + fg: int + bold: bool + italic: bool class Screen: @@ -959,6 +985,9 @@ class Screen: def is_main_linebuf(self) -> bool: pass + def erase_in_line(self, mode: int = 0, private: bool = False) -> None: + pass + def scroll(self, amt: int, upwards: bool) -> bool: pass @@ -968,6 +997,9 @@ class Screen: def clear_selection(self) -> None: pass + def reset_mode(self, mode: int, private: bool = False) -> None: + pass + def refresh_sprite_positions(self) -> None: pass diff --git a/kitty/fonts/core_text.py b/kitty/fonts/core_text.py index cc871f1ad..de81e424f 100644 --- a/kitty/fonts/core_text.py +++ b/kitty/fonts/core_text.py @@ -3,21 +3,32 @@ # License: GPL v3 Copyright: 2017, Kovid Goyal import re -from typing import Generator +from typing import ( + TYPE_CHECKING, Dict, Generator, Iterable, List, Optional, Sequence, Tuple +) from kitty.fast_data_types import coretext_all_fonts +from kitty.options_stub import Options from kitty.utils import log_error from . import ListedFont +if TYPE_CHECKING: + from kitty.fast_data_types import CoreTextFont + CoreTextFont + + attr_map = {(False, False): 'font_family', (True, False): 'bold_font', (False, True): 'italic_font', (True, True): 'bold_italic_font'} -def create_font_map(all_fonts): - ans = {'family_map': {}, 'ps_map': {}, 'full_map': {}} +FontMap = Dict[str, Dict[str, List['CoreTextFont']]] + + +def create_font_map(all_fonts: Iterable['CoreTextFont']) -> FontMap: + ans: FontMap = {'family_map': {}, 'ps_map': {}, 'full_map': {}} for x in all_fonts: f = (x['family'] or '').lower() s = (x['style'] or '').lower() @@ -28,10 +39,11 @@ def create_font_map(all_fonts): return ans -def all_fonts_map(): - ans = getattr(all_fonts_map, 'ans', None) +def all_fonts_map() -> FontMap: + ans: Optional[FontMap] = getattr(all_fonts_map, 'ans', None) if ans is None: - ans = all_fonts_map.ans = create_font_map(coretext_all_fonts()) + ans = create_font_map(coretext_all_fonts()) + setattr(all_fonts_map, 'ans', ans) return ans @@ -44,7 +56,15 @@ def list_fonts() -> Generator[ListedFont, None, None]: yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono} -def find_best_match(family, bold=False, italic=False): +def bi_match(fonts: Sequence['CoreTextFont'], bold: bool, italic: bool) -> 'CoreTextFont': + for b, i in ((bold, italic), (False, False)): + for q in fonts: + if q['bold'] == b and q['italic'] == i: + return q + return fonts[0] + + +def find_best_match(family: str, bold: bool = False, italic: bool = False) -> 'CoreTextFont': q = re.sub(r'\s+', ' ', family.lower()) font_map = all_fonts_map() @@ -66,16 +86,10 @@ def find_best_match(family, bold=False, italic=False): # fallback to Menlo if q not in font_map['family_map']: log_error('The font {} was not found, falling back to Menlo'.format(family)) - family = 'Menlo' - return { - 'monospace': True, - 'bold': bold, - 'italic': italic, - 'family': family - } + return bi_match(font_map['family_map']['menlo'], bold, italic) -def resolve_family(f, main_family, bold=False, italic=False): +def resolve_family(f: str, main_family: str, bold: bool = False, italic: bool = False) -> str: if (bold or italic) and f == 'auto': f = main_family if f.lower() == 'monospace': @@ -83,8 +97,8 @@ def resolve_family(f, main_family, bold=False, italic=False): return f -def get_font_files(opts): - ans = {} +def get_font_files(opts: Options) -> Dict[str, 'CoreTextFont']: + ans: Dict[str, 'CoreTextFont'] = {} for (bold, italic), attr in attr_map.items(): face = find_best_match(resolve_family(getattr(opts, attr), opts.font_family, bold, italic), bold, italic) key = {(False, False): 'medium', @@ -93,10 +107,10 @@ def get_font_files(opts): (True, True): 'bi'}[(bold, italic)] ans[key] = face if key == 'medium': - get_font_files.medium_family = face['family'] + setattr(get_font_files, 'medium_family', face['family']) return ans -def font_for_family(family): - ans = find_best_match(resolve_family(family, get_font_files.medium_family)) +def font_for_family(family: str) -> Tuple['CoreTextFont', bool, bool]: + ans = find_best_match(resolve_family(family, getattr(get_font_files, 'medium_family'))) return ans, ans['bold'], ans['italic'] diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index 15eb2533f..afc452407 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -131,6 +131,6 @@ def get_font_files(opts: Options) -> Dict[str, 'FontConfigPattern']: return ans -def font_for_family(family) -> Tuple['FontConfigPattern', bool, bool]: +def font_for_family(family: str) -> Tuple['FontConfigPattern', bool, bool]: ans = find_best_match(family, monospaced=False) return ans, ans.get('weight', 0) >= FC_WEIGHT_BOLD, ans.get('slant', FC_SLANT_ROMAN) != FC_SLANT_ROMAN diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index f99281e26..3ba72979b 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -6,7 +6,7 @@ import ctypes import sys from functools import partial from math import ceil, cos, floor, pi -from typing import Any, Dict, List, Optional, Tuple, cast +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast from kitty.config import defaults from kitty.constants import is_macos @@ -22,11 +22,30 @@ from kitty.options_stub import Options as OptionsStub from kitty.utils import log_error if is_macos: - from .core_text import get_font_files, font_for_family + from .core_text import get_font_files as get_font_files_coretext, font_for_family as font_for_family_macos + if TYPE_CHECKING: + from .core_text import CoreTextFont + CoreTextFont else: - from .fontconfig import get_font_files, font_for_family + from .fontconfig import get_font_files as get_font_files_fontconfig, font_for_family as font_for_family_fontconfig + if TYPE_CHECKING: + from .fontconfig import FontConfigPattern + FontConfigPattern -current_faces: List[Tuple[Any, bool, bool]] = [] +FontObject = Union['CoreTextFont', 'FontConfigPattern'] +current_faces: List[Tuple[FontObject, bool, bool]] = [] + + +def get_font_files(opts: OptionsStub) -> Dict[str, Any]: + if is_macos: + return get_font_files_coretext(opts) + return get_font_files_fontconfig(opts) + + +def font_for_family(family: str) -> Tuple[FontObject, bool, bool]: + if is_macos: + return font_for_family_macos(family) + return font_for_family_fontconfig(family) def coalesce_symbol_maps(maps: Dict[Tuple[int, int], str]) -> Dict[Tuple[int, int], str]: @@ -64,11 +83,11 @@ def create_symbol_map(opts: OptionsStub) -> Tuple[Tuple[int, int, int], ...]: return sm -def descriptor_for_idx(idx): +def descriptor_for_idx(idx: int) -> Tuple[FontObject, bool, bool]: return current_faces[idx] -def dump_faces(ftypes, indices): +def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None: def face_str(f): f = f[0] if is_macos: @@ -87,7 +106,7 @@ def dump_faces(ftypes, indices): log_error(face_str(face)) -def set_font_family(opts=None, override_font_size=None, debug_font_matching=False): +def set_font_family(opts: Optional[OptionsStub] = None, override_font_size=None, debug_font_matching=False): global current_faces opts = opts or defaults sz = override_font_size or opts.font_size diff --git a/kitty/key_names.py b/kitty/key_names.py index b0d45ed29..65e292fb1 100644 --- a/kitty/key_names.py +++ b/kitty/key_names.py @@ -4,6 +4,7 @@ import sys from contextlib import suppress +from typing import Callable, Optional from .constants import is_macos @@ -96,16 +97,18 @@ key_name_aliases = { 'ARROWLEFT': 'LEFT' } +LookupFunc = Callable[[str, bool], Optional[int]] -def null_lookup(name, case_sensitive=False): - pass + +def null_lookup(name: str, case_sensitive: bool = False) -> Optional[int]: + return None if is_macos: - def get_key_name_lookup(): + def get_key_name_lookup() -> LookupFunc: return null_lookup else: - def load_libxkb_lookup(): + def load_libxkb_lookup() -> LookupFunc: import ctypes for suffix in ('.0', ''): with suppress(Exception): @@ -113,7 +116,10 @@ else: break else: from ctypes.util import find_library - lib = ctypes.CDLL(find_library('xkbcommon')) + lname = find_library('xkbcommon') + if lname is None: + raise RuntimeError('Failed to find libxkbcommon') + lib = ctypes.CDLL(lname) f = lib.xkb_keysym_from_name f.argtypes = [ctypes.c_char_p, ctypes.c_int] @@ -125,13 +131,13 @@ else: return xkb_lookup - def get_key_name_lookup(): - ans = getattr(get_key_name_lookup, 'ans', None) + def get_key_name_lookup() -> LookupFunc: + ans: Optional[LookupFunc] = getattr(get_key_name_lookup, 'ans', None) if ans is None: try: ans = load_libxkb_lookup() except Exception as e: print('Failed to load libxkbcommon.xkb_keysym_from_name with error:', e, file=sys.stderr) ans = null_lookup - get_key_name_lookup.ans = ans + setattr(get_key_name_lookup, 'ans', ans) return ans diff --git a/kitty/marks.py b/kitty/marks.py index 64fae7780..c4f376cf6 100644 --- a/kitty/marks.py +++ b/kitty/marks.py @@ -5,14 +5,14 @@ import os import re from ctypes import POINTER, c_uint, c_void_p, cast +from typing import Callable, Generator, Iterable, Pattern, Tuple, Union, Sequence from .constants import config_dir pointer_to_uint = POINTER(c_uint) -def null_marker(*a): - return iter(()) +MarkerFunc = Callable[[str, int, int, int], Generator[None, None, None]] def get_output_variables(left_address, right_address, color_address): @@ -23,14 +23,14 @@ def get_output_variables(left_address, right_address, color_address): ) -def marker_from_regex(expression, color, flags=re.UNICODE): +def marker_from_regex(expression: Union[str, Pattern], color: int, flags: int = re.UNICODE) -> MarkerFunc: color = max(1, min(color, 3)) if isinstance(expression, str): pat = re.compile(expression, flags=flags) else: pat = expression - def marker(text, left_address, right_address, color_address): + def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]: left, right, colorv = get_output_variables(left_address, right_address, color_address) colorv.value = color for match in pat.finditer(text): @@ -41,7 +41,7 @@ def marker_from_regex(expression, color, flags=re.UNICODE): return marker -def marker_from_multiple_regex(regexes, flags=re.UNICODE): +def marker_from_multiple_regex(regexes: Iterable[Tuple[int, str]], flags: int = re.UNICODE) -> MarkerFunc: expr = '' color_map = {} for i, (color, spec) in enumerate(regexes): @@ -51,24 +51,24 @@ def marker_from_multiple_regex(regexes, flags=re.UNICODE): expr = expr[1:] pat = re.compile(expr, flags=flags) - def marker(text, left_address, right_address, color_address): + def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]: left, right, color = get_output_variables(left_address, right_address, color_address) for match in pat.finditer(text): left.value = match.start() right.value = match.end() - 1 - grp = next(k for k, v in match.groupdict().items() if v is not None) + grp = next(k for k, v in match.groupdict().items()) color.value = color_map[grp] yield return marker -def marker_from_text(expression, color): +def marker_from_text(expression: str, color: int) -> MarkerFunc: return marker_from_regex(re.escape(expression), color) -def marker_from_function(func): - def marker(text, left_address, right_address, color_address): +def marker_from_function(func: Callable[[str], Iterable[Tuple[int, int, int]]]) -> MarkerFunc: + def marker(text: str, left_address: int, right_address: int, color_address: int) -> Generator[None, None, None]: left, right, colorv = get_output_variables(left_address, right_address, color_address) for (l, r, c) in func(text): left.value = l @@ -79,13 +79,15 @@ def marker_from_function(func): return marker -def marker_from_spec(ftype, spec, flags): +def marker_from_spec(ftype: str, spec: Union[str, Sequence[Tuple[int, str]]], flags: int) -> MarkerFunc: if ftype == 'regex': + assert not isinstance(spec, str) if len(spec) == 1: return marker_from_regex(spec[0][1], spec[0][0], flags=flags) return marker_from_multiple_regex(spec, flags=flags) if ftype == 'function': import runpy + assert isinstance(spec, str) path = spec if not os.path.isabs(path): path = os.path.join(config_dir, path) diff --git a/kitty/options_stub.py b/kitty/options_stub.py index b0b86d656..799f42dff 100644 --- a/kitty/options_stub.py +++ b/kitty/options_stub.py @@ -16,7 +16,8 @@ def generate_stub(): text = as_type_stub( all_options, special_types={ - 'symbol_map': 'typing.Dict[typing.Tuple[int, int], str]' + 'symbol_map': 'typing.Dict[typing.Tuple[int, int], str]', + 'font_features': 'typing.Dict[str, typing.Tuple[str, ...]]' }, preamble_lines=( 'from kitty.config import KeyAction', diff --git a/kitty/rc/detach_tab.py b/kitty/rc/detach_tab.py index cb60f19d9..e961b82cc 100644 --- a/kitty/rc/detach_tab.py +++ b/kitty/rc/detach_tab.py @@ -43,7 +43,8 @@ If specified detach the tab this command is run in, rather than the active tab. if not tabs: raise MatchError(match) else: - tabs = tuple(window.tabref() if payload_get('self') and window and window.tabref() else boss.active_tab) + tab = window.tabref() + tabs = tuple(tab if payload_get('self') and window and tab else boss.active_tab) match = payload_get('target_tab') kwargs = {} if match: diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index fa79fd167..0c2336f91 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -3,7 +3,7 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal from collections import namedtuple -from typing import Set +from typing import Set, Tuple, Union from .config import build_ansi_color_table from .constants import WindowGeometry @@ -165,7 +165,7 @@ class TabBar: color_as_int(opts.inactive_tab_foreground), color_as_int(opts.inactive_tab_background) ) - self.blank_rects = () + self.blank_rects: Union[Tuple, Tuple[Rect, Rect]] = () sep = opts.tab_separator self.trailing_spaces = self.leading_spaces = 0 while sep and sep[0] == ' ': @@ -218,7 +218,7 @@ class TabBar: return self.cell_width = cell_width s = self.screen - viewport_width = tab_bar.width - 2 * self.margin_width + viewport_width = int(tab_bar.width - 2 * self.margin_width) ncells = viewport_width // cell_width s.resize(1, ncells) s.reset_mode(DECAWM) diff --git a/kitty/utils.py b/kitty/utils.py index d365a2c08..039be7353 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -13,7 +13,9 @@ import sys from contextlib import suppress from functools import lru_cache from time import monotonic -from typing import Any, Dict, List, NamedTuple, Optional, cast +from typing import ( + Any, Dict, Generator, List, NamedTuple, Optional, Tuple, cast +) from .constants import ( appname, is_macos, is_wayland, shell_path, supports_primary_selection @@ -61,21 +63,23 @@ def color_from_int(val): return Color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF) -def parse_color_set(raw): +def parse_color_set(raw: str) -> Generator[Tuple[int, Optional[int]], None, None]: parts = raw.split(';') lp = len(parts) if lp % 2 != 0: return - for c, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]: + for c_, spec in [parts[i:i + 2] for i in range(0, len(parts), 2)]: try: - c = int(c) + c = int(c_) if c < 0 or c > 255: continue if spec == '?': yield c, None else: - r, g, b = to_color(spec) - yield c, r << 16 | g << 8 | b + q = to_color(spec) + if q is not None: + r, g, b = q + yield c, r << 16 | g << 8 | b except Exception: continue diff --git a/kitty/window.py b/kitty/window.py index 1ae316da6..67cadafa8 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -10,7 +10,7 @@ from collections import deque from enum import IntEnum from itertools import chain from re import Pattern -from typing import Deque, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Callable, Deque, Dict, List, Optional, Tuple, Union from .config import build_ansi_color_table from .constants import ScreenGeometry, WindowGeometry, appname, wakeup @@ -33,6 +33,10 @@ from .utils import ( set_primary_selection ) +if TYPE_CHECKING: + from .tabs import Tab + Tab + MatchPatternType = Union[Pattern, Tuple[Pattern, Optional[Pattern]]] @@ -164,7 +168,7 @@ class Window: raise Exception('No tab with id: {} in OS Window: {} was found, or the window counter wrapped'.format(tab.id, tab.os_window_id)) self.tab_id = tab.id self.os_window_id = tab.os_window_id - self.tabref = weakref.ref(tab) + self.tabref: Callable[[], Optional['Tab']] = weakref.ref(tab) self.clipboard_control_buffers = {'p': '', 'c': ''} self.destroyed = False self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)