From da1626090a7f531e7903d2573d92fb47280c7985 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 10 Jan 2025 22:31:41 +0530 Subject: [PATCH] Update codebase to Python 3.10 using pyupgrade --- kittens/ask/main.py | 12 +- kittens/broadcast/main.py | 10 +- kittens/choose_fonts/backend.py | 34 +- kittens/diff/__init__.py | 5 +- kittens/diff/main.py | 3 +- kittens/hints/main.py | 25 +- kittens/pager/main.py | 3 +- kittens/panel/main.py | 13 +- kittens/query_terminal/main.py | 8 +- kittens/remote_file/main.py | 8 +- kittens/resize_window/main.py | 8 +- kittens/runner.py | 23 +- kittens/show_key/main.py | 3 +- kittens/ssh/main.py | 3 +- kittens/ssh/utils.py | 35 +- kittens/themes/main.py | 3 +- kittens/transfer/main.py | 3 +- kittens/transfer/utils.py | 2 +- kittens/tui/dircolors.py | 14 +- kittens/tui/handler.py | 35 +- kittens/tui/images.py | 51 +- kittens/tui/line_edit.py | 4 +- kittens/tui/loop.py | 17 +- kittens/tui/operations.py | 47 +- kittens/tui/path_completer.py | 7 +- kittens/tui/spinners.py | 4 +- kittens/tui/utils.py | 5 +- kittens/unicode_input/main.py | 5 +- kitty/boss.py | 235 ++++---- kitty/child.py | 34 +- kitty/cli.py | 36 +- kitty/clipboard.py | 18 +- kitty/colors.py | 17 +- kitty/conf/generate.py | 37 +- kitty/conf/types.py | 106 ++-- kitty/conf/utils.py | 74 ++- kitty/config.py | 14 +- kitty/constants.py | 2 +- kitty/debug_config.py | 6 +- kitty/file_transmission.py | 58 +- kitty/fonts/__init__.py | 16 +- kitty/fonts/common.py | 44 +- kitty/fonts/core_text.py | 6 +- kitty/fonts/fontconfig.py | 6 +- kitty/fonts/list.py | 3 +- kitty/fonts/render.py | 16 +- kitty/guess_mime_type.py | 5 +- kitty/key_encoding.py | 8 +- kitty/key_names.py | 9 +- kitty/keys.py | 10 +- kitty/launch.py | 62 +- kitty/layout/base.py | 69 +-- kitty/layout/grid.py | 65 +-- kitty/layout/interface.py | 7 +- kitty/layout/splits.py | 45 +- kitty/layout/tall.py | 29 +- kitty/layout/vertical.py | 15 +- kitty/main.py | 11 +- kitty/marks.py | 6 +- kitty/multiprocessing.py | 10 +- kitty/notifications.py | 44 +- kitty/open_actions.py | 4 +- kitty/options/parse.py | 890 ++++++++++++++--------------- kitty/options/types.py | 112 ++-- kitty/options/utils.py | 162 +++--- kitty/os_window_size.py | 5 +- kitty/rc/action.py | 4 +- kitty/rc/base.py | 57 +- kitty/rc/close_tab.py | 4 +- kitty/rc/close_window.py | 4 +- kitty/rc/create_marker.py | 4 +- kitty/rc/detach_tab.py | 4 +- kitty/rc/detach_window.py | 8 +- kitty/rc/disable_ligatures.py | 4 +- kitty/rc/env.py | 4 +- kitty/rc/focus_tab.py | 4 +- kitty/rc/focus_window.py | 4 +- kitty/rc/get_colors.py | 4 +- kitty/rc/get_text.py | 4 +- kitty/rc/goto_layout.py | 5 +- kitty/rc/kitten.py | 4 +- kitty/rc/last_used_layout.py | 4 +- kitty/rc/launch.py | 6 +- kitty/rc/load_config.py | 4 +- kitty/rc/ls.py | 15 +- kitty/rc/new_window.py | 4 +- kitty/rc/remove_marker.py | 4 +- kitty/rc/resize_os_window.py | 4 +- kitty/rc/resize_window.py | 6 +- kitty/rc/run.py | 8 +- kitty/rc/scroll_window.py | 6 +- kitty/rc/select_window.py | 4 +- kitty/rc/send_key.py | 4 +- kitty/rc/send_text.py | 10 +- kitty/rc/set_background_image.py | 4 +- kitty/rc/set_background_opacity.py | 4 +- kitty/rc/set_colors.py | 8 +- kitty/rc/set_enabled_layouts.py | 7 +- kitty/rc/set_font_size.py | 4 +- kitty/rc/set_spacing.py | 17 +- kitty/rc/set_tab_color.py | 10 +- kitty/rc/set_tab_title.py | 4 +- kitty/rc/set_user_vars.py | 4 +- kitty/rc/set_window_logo.py | 4 +- kitty/rc/set_window_title.py | 4 +- kitty/rc/signal_child.py | 4 +- kitty/remote_control.py | 21 +- kitty/rgb.py | 11 +- kitty/search_query_parser.py | 12 +- kitty/session.py | 32 +- kitty/shaders.py | 4 +- kitty/shell_integration.py | 10 +- kitty/shm.py | 5 +- kitty/short_uuid.py | 9 +- kitty/tab_bar.py | 22 +- kitty/tabs.py | 158 +++-- kitty/terminfo.py | 4 +- kitty/types.py | 6 +- kitty/typing.py | 19 +- kitty/update_check.py | 4 +- kitty/utils.py | 78 ++- kitty/window.py | 113 ++-- kitty/window_list.py | 30 +- kitty_tests/__init__.py | 3 +- kitty_tests/fonts.py | 2 +- kitty_tests/main.py | 3 +- 126 files changed, 1739 insertions(+), 1764 deletions(-) diff --git a/kittens/ask/main.py b/kittens/ask/main.py index 5a4cbca2e..ea821dbfd 100644 --- a/kittens/ask/main.py +++ b/kittens/ask/main.py @@ -2,10 +2,6 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal import sys -from typing import ( - List, - Optional, -) from kitty.typing import BossType, TypedDict @@ -69,15 +65,15 @@ The text in the message to be replaced by hidden text. The hidden text is read v class Response(TypedDict): - items: List[str] - response: Optional[str] + items: list[str] + response: str | None -def main(args: List[str]) -> Response: +def main(args: list[str]) -> Response: raise SystemExit('This must be run as kitten ask') @result_handler() -def handle_result(args: List[str], data: Response, target_window_id: int, boss: BossType) -> None: +def handle_result(args: list[str], data: Response, target_window_id: int, boss: BossType) -> None: if data['response'] is not None: func, *args = data['items'] getattr(boss, func)(data['response'], *args) diff --git a/kittens/broadcast/main.py b/kittens/broadcast/main.py index 80baf0262..f24abbef3 100644 --- a/kittens/broadcast/main.py +++ b/kittens/broadcast/main.py @@ -4,7 +4,7 @@ import sys from base64 import standard_b64encode from gettext import gettext as _ -from typing import Any, Dict, List, Optional, Tuple +from typing import Any from kitty.cli import parse_args from kitty.cli_stub import BroadcastCLIOptions @@ -20,7 +20,7 @@ from ..tui.loop import Loop from ..tui.operations import RESTORE_CURSOR, SAVE_CURSOR, styled -def session_command(payload: Dict[str, Any], start: bool = True) -> bytes: +def session_command(payload: dict[str, Any], start: bool = True) -> bytes: payload = payload.copy() payload['data'] = 'session:' + ('start' if start else 'end') send = create_basic_command('send-text', payload, no_response=True) @@ -29,7 +29,7 @@ def session_command(payload: Dict[str, Any], start: bool = True) -> bytes: class Broadcast(Handler): - def __init__(self, opts: BroadcastCLIOptions, initial_strings: List[str]) -> None: + def __init__(self, opts: BroadcastCLIOptions, initial_strings: list[str]) -> None: self.opts = opts self.hide_input = False self.initial_strings = initial_strings @@ -128,11 +128,11 @@ help_text = 'Broadcast typed text to kitty windows. By default text is sent to a usage = '[initial text to send ...]' -def parse_broadcast_args(args: List[str]) -> Tuple[BroadcastCLIOptions, List[str]]: +def parse_broadcast_args(args: list[str]) -> tuple[BroadcastCLIOptions, list[str]]: return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten broadcast', result_class=BroadcastCLIOptions) -def main(args: List[str]) -> Optional[Dict[str, Any]]: +def main(args: list[str]) -> dict[str, Any] | None: try: opts, items = parse_broadcast_args(args[1:]) except SystemExit as e: diff --git a/kittens/choose_fonts/backend.py b/kittens/choose_fonts/backend.py index 5fcc89ae0..387bd16f2 100644 --- a/kittens/choose_fonts/backend.py +++ b/kittens/choose_fonts/backend.py @@ -6,7 +6,7 @@ import os import string import sys import tempfile -from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Tuple, TypedDict +from typing import TYPE_CHECKING, Any, Literal, Optional, TypedDict from kitty.cli import create_default_opts from kitty.conf.utils import to_color @@ -67,10 +67,10 @@ class TextStyle(TypedDict): OptNames = Literal['font_family', 'bold_font', 'italic_font', 'bold_italic_font'] -FamilyKey = Tuple[OptNames, ...] +FamilyKey = tuple[OptNames, ...] -def opts_from_cmd(cmd: Dict[str, Any]) -> Tuple[Options, FamilyKey, float, float]: +def opts_from_cmd(cmd: dict[str, Any]) -> tuple[Options, FamilyKey, float, float]: opts = Options() ts: TextStyle = cmd['text_style'] opts.font_size = ts['font_size'] @@ -88,10 +88,10 @@ def opts_from_cmd(cmd: Dict[str, Any]) -> Tuple[Options, FamilyKey, float, float return opts, tuple(family_key), ts['dpi_x'], ts['dpi_y'] -BaseKey = Tuple[str, int, int] -FaceKey = Tuple[str, BaseKey] -RenderedSample = Tuple[bytes, Dict[str, Any]] -RenderedSampleTransmit = Dict[str, Any] +BaseKey = tuple[str, int, int] +FaceKey = tuple[str, BaseKey] +RenderedSample = tuple[bytes, dict[str, Any]] +RenderedSampleTransmit = dict[str, Any] SAMPLE_TEXT = string.ascii_lowercase + ' ' + string.digits + ' ' + string.ascii_uppercase + ' ' + string.punctuation @@ -100,11 +100,11 @@ class FD(TypedDict): name: NotRequired[str] tooltip: NotRequired[str] sample: NotRequired[str] - params: NotRequired[Tuple[str, ...]] + params: NotRequired[tuple[str, ...]] -def get_features(features: Dict[str, Optional['FeatureData']]) -> Dict[str, FD]: +def get_features(features: dict[str, Optional['FeatureData']]) -> dict[str, FD]: ans = {} for tag, data in features.items(): kf = known_features.get(tag) @@ -150,10 +150,10 @@ def render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: flo def render_family_sample( opts: Options, family_key: FamilyKey, dpi_x: float, dpi_y: float, width: int, height: int, output_dir: str, - cache: Dict[FaceKey, RenderedSampleTransmit] -) -> Dict[str, RenderedSampleTransmit]: + cache: dict[FaceKey, RenderedSampleTransmit] +) -> dict[str, RenderedSampleTransmit]: base_key: BaseKey = opts.font_family.created_from_string, width, height - ans: Dict[str, RenderedSampleTransmit] = {} + ans: dict[str, RenderedSampleTransmit] = {} font_files = get_font_files(opts) for x in family_key: key: FaceKey = x + ': ' + str(getattr(opts, x)), base_key @@ -177,7 +177,7 @@ def render_family_sample( return ans -ResolvedFace = Dict[Literal['family', 'spec', 'setting'], str] +ResolvedFace = dict[Literal['family', 'spec', 'setting'], str] def spec_for_descriptor(d: Descriptor, font_size: float) -> str: @@ -185,9 +185,9 @@ def spec_for_descriptor(d: Descriptor, font_size: float) -> str: return spec_for_face(d['family'], face).as_setting -def resolved_faces(opts: Options) -> Dict[OptNames, ResolvedFace]: +def resolved_faces(opts: Options) -> dict[OptNames, ResolvedFace]: font_files = get_font_files(opts) - ans: Dict[OptNames, ResolvedFace] = {} + ans: dict[OptNames, ResolvedFace] = {} def d(key: Literal['medium', 'bold', 'italic', 'bi'], opt_name: OptNames) -> None: descriptor = font_files[key] ans[opt_name] = { @@ -203,7 +203,7 @@ def resolved_faces(opts: Options) -> Dict[OptNames, ResolvedFace]: def main() -> None: setup_debug_print() - cache: Dict[FaceKey, RenderedSampleTransmit] = {} + cache: dict[FaceKey, RenderedSampleTransmit] = {} for line in sys.stdin.buffer: cmd = json.loads(line) action = cmd.get('action', '') @@ -222,7 +222,7 @@ def main() -> None: raise SystemExit(f'Unknown action: {action}') -def query_kitty() -> Dict[str, str]: +def query_kitty() -> dict[str, str]: import subprocess ans = {} for line in subprocess.check_output([kitten_exe(), 'query-terminal']).decode().splitlines(): diff --git a/kittens/diff/__init__.py b/kittens/diff/__init__.py index d11f4dd01..e74008787 100644 --- a/kittens/diff/__init__.py +++ b/kittens/diff/__init__.py @@ -1,7 +1,4 @@ -from typing import Dict - - -def syntax_aliases(x: str) -> Dict[str, str]: +def syntax_aliases(x: str) -> dict[str, str]: ans = {} for x in x.split(): k, _, v = x.partition(':') diff --git a/kittens/diff/main.py b/kittens/diff/main.py index 2f18cddb0..d0fe8f563 100644 --- a/kittens/diff/main.py +++ b/kittens/diff/main.py @@ -3,14 +3,13 @@ import sys from functools import partial -from typing import List from kitty.cli import CONFIG_HELP, CompletionSpec from kitty.conf.types import Definition from kitty.constants import appname -def main(args: List[str]) -> None: +def main(args: list[str]) -> None: raise SystemExit('Must be run as kitten diff') definition = Definition( diff --git a/kittens/hints/main.py b/kittens/hints/main.py index 53aee8479..b037eeb59 100644 --- a/kittens/hints/main.py +++ b/kittens/hints/main.py @@ -2,8 +2,9 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal import sys +from collections.abc import Sequence from functools import lru_cache -from typing import Any, Dict, List, Optional, Sequence, Tuple +from typing import Any from kitty.cli_stub import HintsCLIOptions from kitty.clipboard import set_clipboard_string, set_primary_selection @@ -37,7 +38,7 @@ class Mark: text: str, groupdict: Any, is_hyperlink: bool = False, - group_id: Optional[str] = None + group_id: str | None = None ): self.index, self.start, self.end = index, start, end self.text = text @@ -45,7 +46,7 @@ class Mark: self.is_hyperlink = is_hyperlink self.group_id = group_id - def as_dict(self) -> Dict[str, Any]: + def as_dict(self) -> dict[str, Any]: return { 'index': self.index, 'start': self.start, 'end': self.end, 'text': self.text, 'groupdict': {str(k):v for k, v in (self.groupdict or {}).items()}, @@ -53,7 +54,7 @@ class Mark: } -def parse_hints_args(args: List[str]) -> Tuple[HintsCLIOptions, List[str]]: +def parse_hints_args(args: list[str]) -> tuple[HintsCLIOptions, list[str]]: from kitty.cli import parse_args return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten hints', result_class=HintsCLIOptions) @@ -255,11 +256,11 @@ help_text = 'Select text from the screen using the keyboard. Defaults to searchi usage = '' -def main(args: List[str]) -> Optional[Dict[str, Any]]: +def main(args: list[str]) -> dict[str, Any] | None: raise SystemExit('Should be run as kitten hints') -def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]: +def linenum_process_result(data: dict[str, Any]) -> tuple[str, int]: for match, g in zip(data['match'], data['groupdicts']): path, line = g['path'], g['line'] if path and line: @@ -267,7 +268,7 @@ def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]: return '', -1 -def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType, extra_cli_args: Sequence[str], *a: Any) -> None: +def linenum_handle_result(args: list[str], data: dict[str, Any], target_window_id: int, boss: BossType, extra_cli_args: Sequence[str], *a: Any) -> None: path, line = linenum_process_result(data) if not path: return @@ -301,7 +302,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i boss.set_clipboard_buffer(program[1:], text) else: import shlex - text = ' '.join(shlex.quote(arg) for arg in cmd) + text = shlex.join(cmd) w.paste_bytes(f'{text}\r') elif action == 'background': import subprocess @@ -320,7 +321,7 @@ def on_mark_clicked(boss: BossType, window: WindowType, url: str, hyperlink_id: @result_handler(type_of_input='screen-ansi', has_ready_notification=True, open_url_handler=on_mark_clicked) -def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType) -> None: +def handle_result(args: list[str], data: dict[str, Any], target_window_id: int, boss: BossType) -> None: cp = data['customize_processing'] if data['type'] == 'linenum': cp = '::linenum::' @@ -331,7 +332,7 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, return None programs = data['programs'] or ('default',) - matches: List[str] = [] + matches: list[str] = [] groupdicts = [] for m, g in zip(data['match'], data['groupdicts']): if m: @@ -339,12 +340,12 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, groupdicts.append(g) joiner = data['multiple_joiner'] try: - is_int: Optional[int] = int(joiner) + is_int: int | None = int(joiner) except Exception: is_int = None text_type = data['type'] - @lru_cache() + @lru_cache def joined_text() -> str: if is_int is not None: try: diff --git a/kittens/pager/main.py b/kittens/pager/main.py index 55d783e18..8f7a0365d 100644 --- a/kittens/pager/main.py +++ b/kittens/pager/main.py @@ -3,7 +3,6 @@ import sys -from typing import List from kitty.cli import CompletionSpec @@ -27,7 +26,7 @@ and STDIN is not a TTY, it is used. usage = '[filename]' -def main(args: List[str]) -> None: +def main(args: list[str]) -> None: raise SystemExit('Must be run as kitten pager') diff --git a/kittens/panel/main.py b/kittens/panel/main.py index 421c831e1..6ecb9be44 100644 --- a/kittens/panel/main.py +++ b/kittens/panel/main.py @@ -2,7 +2,8 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal import sys -from typing import Any, Callable, Dict, List, Tuple +from collections.abc import Callable +from typing import Any from kitty.cli import parse_args from kitty.cli_stub import PanelCLIOptions @@ -152,11 +153,11 @@ help_text = 'Use a command line program to draw a GPU accelerated panel on your usage = 'program-to-run' -def parse_panel_args(args: List[str]) -> Tuple[PanelCLIOptions, List[str]]: +def parse_panel_args(args: list[str]) -> tuple[PanelCLIOptions, list[str]]: return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten panel', result_class=PanelCLIOptions) -Strut = Tuple[int, int, int, int, int, int, int, int, int, int, int, int] +Strut = tuple[int, int, int, int, int, int, int, int, int, int, int, int] def create_strut( @@ -195,12 +196,12 @@ def setup_x11_window(win_id: int) -> None: make_x11_window_a_dock_window(win_id, strut) -def initial_window_size_func(opts: WindowSizeData, cached_values: Dict[str, Any]) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]: +def initial_window_size_func(opts: WindowSizeData, cached_values: dict[str, Any]) -> Callable[[int, int, float, float, float, float], tuple[int, int]]: def es(which: EdgeLiteral) -> float: return edge_spacing(which, opts) - def initial_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]: + def initial_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> tuple[int, int]: if not is_macos and not is_wayland(): # Not sure what the deal with scaling on X11 is xscale = yscale = 1 @@ -248,7 +249,7 @@ def layer_shell_config(opts: PanelCLIOptions) -> LayerShellConfig: output_name=opts.output_name or '') -def main(sys_args: List[str]) -> None: +def main(sys_args: list[str]) -> None: global args if is_macos: raise SystemExit('Currently the panel kitten is not supported on macOS') diff --git a/kittens/query_terminal/main.py b/kittens/query_terminal/main.py index e256895d8..a9e6c39f5 100644 --- a/kittens/query_terminal/main.py +++ b/kittens/query_terminal/main.py @@ -5,7 +5,7 @@ import re import sys from binascii import hexlify, unhexlify from contextlib import suppress -from typing import Dict, Optional, Type, get_args +from typing import get_args from kitty.conf.utils import OSNames, os_name from kitty.constants import appname, str_version @@ -52,10 +52,10 @@ class Query: raise NotImplementedError() -all_queries: Dict[str, Type[Query]] = {} +all_queries: dict[str, type[Query]] = {} -def query(cls: Type[Query]) -> Type[Query]: +def query(cls: type[Query]) -> type[Query]: all_queries[cls.name] = cls return cls @@ -237,7 +237,7 @@ class OSName(Query): return os_name() -def get_result(name: str, window_id: int, os_window_id: int) -> Optional[str]: +def get_result(name: str, window_id: int, os_window_id: int) -> str | None: from kitty.fast_data_types import get_options q = all_queries.get(name) if q is None: diff --git a/kittens/remote_file/main.py b/kittens/remote_file/main.py index b56d3261f..81958ce92 100644 --- a/kittens/remote_file/main.py +++ b/kittens/remote_file/main.py @@ -10,7 +10,7 @@ import subprocess import sys import tempfile import time -from typing import Any, List, Optional +from typing import Any, Optional from kitty.cli import parse_args from kitty.cli_stub import RemoteFileCLIOptions @@ -129,7 +129,7 @@ class ControlMaster: cmd.extend(['-i', conn_data.identity_file]) self.batch_cmd_prefix = cmd + ['-o', 'BatchMode=yes'] - def check_call(self, cmd: List[str]) -> None: + def check_call(self, cmd: list[str]) -> None: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL) stdout = p.communicate()[0] if p.wait() != 0: @@ -228,7 +228,7 @@ class ControlMaster: Result = Optional[str] -def main(args: List[str]) -> Result: +def main(args: list[str]) -> Result: msg = 'Ask the user what to do with the remote file. For internal use by kitty, do not run it directly.' try: cli_opts, items = parse_args(args[1:], option_text, '', msg, 'kitty +kitten remote_file', result_class=RemoteFileCLIOptions) @@ -355,7 +355,7 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result: @result_handler() -def handle_result(args: List[str], data: Result, target_window_id: int, boss: BossType) -> None: +def handle_result(args: list[str], data: Result, target_window_id: int, boss: BossType) -> None: if data: from kitty.fast_data_types import get_options cmd = command_for_open(get_options().open_url_with) diff --git a/kittens/resize_window/main.py b/kittens/resize_window/main.py index 4062c4d30..02769255a 100644 --- a/kittens/resize_window/main.py +++ b/kittens/resize_window/main.py @@ -3,7 +3,7 @@ import sys -from typing import Any, Dict, List, Optional +from typing import Any from kitty.cli import parse_args from kitty.cli_stub import RCOptions, ResizeCLIOptions @@ -22,7 +22,7 @@ global_opts = RCOptions() class Resize(Handler): - print_on_fail: Optional[str] = None + print_on_fail: str | None = None def __init__(self, opts: ResizeCLIOptions): self.opts = opts @@ -48,7 +48,7 @@ class Resize(Handler): send = {'cmd': resize_window.name, 'version': version, 'payload': payload, 'no_response': False} self.write(encode_send(send)) - def on_kitty_cmd_response(self, response: Dict[str, Any]) -> None: + def on_kitty_cmd_response(self, response: dict[str, Any]) -> None: if not response.get('ok'): err = response['error'] if response.get('tb'): @@ -114,7 +114,7 @@ The base vertical increment. '''.format -def main(args: List[str]) -> None: +def main(args: list[str]) -> None: msg = 'Resize the current window' try: cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'resize_window', result_class=ResizeCLIOptions) diff --git a/kittens/runner.py b/kittens/runner.py index 2b8196f43..2f97e5cf6 100644 --- a/kittens/runner.py +++ b/kittens/runner.py @@ -5,9 +5,10 @@ import importlib import os import sys +from collections.abc import Callable, Generator from contextlib import contextmanager from functools import partial -from typing import TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Generator, List, NamedTuple, Optional, Union, cast +from typing import TYPE_CHECKING, Any, NamedTuple, cast from kitty.constants import list_kitty_resources from kitty.types import run_once @@ -49,7 +50,7 @@ class CLIOnlyKitten(TypeError): super().__init__(f'The {kitten} kitten must be run only at the commandline, as: kitten {kitten}') -def import_kitten_main_module(config_dir: str, kitten: str) -> Dict[str, Any]: +def import_kitten_main_module(config_dir: str, kitten: str) -> dict[str, Any]: if kitten.endswith('.py'): with preserve_sys_path(): path = path_to_custom_kitten(config_dir, kitten) @@ -76,15 +77,15 @@ def import_kitten_main_module(config_dir: str, kitten: str) -> Dict[str, Any]: class KittenMetadata(NamedTuple): handle_result: Callable[[Any, int, BossType], None] = lambda *a: None - type_of_input: Optional[str] = None + type_of_input: str | None = None no_ui: bool = False has_ready_notification: bool = False - open_url_handler: Optional[Callable[[BossType, WindowType, str, int, str], bool]] = None + open_url_handler: Callable[[BossType, WindowType, str, int, str], bool] | None = None allow_remote_control: bool = False - remote_control_password: Union[str, bool] = False + remote_control_password: str | bool = False -def create_kitten_handler(kitten: str, orig_args: List[str]) -> KittenMetadata: +def create_kitten_handler(kitten: str, orig_args: list[str]) -> KittenMetadata: from kitty.constants import config_dir kitten = resolved_kitten(kitten) m = import_kitten_main_module(config_dir, kitten) @@ -107,7 +108,7 @@ def set_debug(kitten: str) -> None: setattr(builtins, 'debug', debug) -def launch(args: List[str]) -> None: +def launch(args: list[str]) -> None: config_dir, kitten = args[:2] kitten = resolved_kitten(kitten) del args[:2] @@ -157,7 +158,7 @@ def run_kitten(kitten: str, run_name: str = '__main__') -> None: @run_once -def all_kitten_names() -> FrozenSet[str]: +def all_kitten_names() -> frozenset[str]: ans = [] for name in list_kitty_resources('kittens'): if '__' not in name and '.' not in name and name != 'tui': @@ -198,7 +199,7 @@ def get_kitten_completer(kitten: str) -> Any: return ans -def get_kitten_conf_docs(kitten: str) -> Optional[Definition]: +def get_kitten_conf_docs(kitten: str) -> Definition | None: setattr(sys, 'options_definition', None) run_kitten(kitten, run_name='__conf__') ans = getattr(sys, 'options_definition') @@ -206,12 +207,12 @@ def get_kitten_conf_docs(kitten: str) -> Optional[Definition]: return cast(Definition, ans) -def get_kitten_extra_cli_parsers(kitten: str) -> Dict[str,str]: +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) + return cast(dict[str, str], ans) def main() -> None: diff --git a/kittens/show_key/main.py b/kittens/show_key/main.py index 0b6c22e4a..d49e511ef 100644 --- a/kittens/show_key/main.py +++ b/kittens/show_key/main.py @@ -3,7 +3,6 @@ import sys -from typing import List OPTIONS = r''' --key-mode -m @@ -18,7 +17,7 @@ help_text = 'Show the codes generated by the terminal for key presses in various usage = '' -def main(args: List[str]) -> None: +def main(args: list[str]) -> None: raise SystemExit('This should be reun as kitten show_key') diff --git a/kittens/ssh/main.py b/kittens/ssh/main.py index 8ae34ae53..567aacdd7 100644 --- a/kittens/ssh/main.py +++ b/kittens/ssh/main.py @@ -2,7 +2,6 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal import sys -from typing import List, Optional from kitty.conf.types import Definition from kitty.types import run_once @@ -222,7 +221,7 @@ environment variable. egr() # }}} -def main(args: List[str]) -> Optional[str]: +def main(args: list[str]) -> str | None: raise SystemExit('This should be run as kitten ssh') if __name__ == '__main__': diff --git a/kittens/ssh/utils.py b/kittens/ssh/utils.py index 01fc15390..20f78c9ef 100644 --- a/kittens/ssh/utils.py +++ b/kittens/ssh/utils.py @@ -5,15 +5,16 @@ import os import subprocess import traceback +from collections.abc import Iterator, Sequence from contextlib import suppress -from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Tuple +from typing import Any from kitty.types import run_once from kitty.utils import SSHConnectionData @run_once -def ssh_options() -> Dict[str, str]: +def ssh_options() -> dict[str, str]: try: p = subprocess.run(['ssh'], stderr=subprocess.PIPE, encoding='utf-8') raw = p.stderr or '' @@ -28,7 +29,7 @@ def ssh_options() -> Dict[str, str]: 'S': 'ctl_path', 'W': 'host:port', 'w': 'local_tun[:remote_tun]' } - ans: Dict[str, str] = {} + ans: dict[str, str] = {} pos = 0 while True: pos = raw.find('[', pos) @@ -68,7 +69,7 @@ def is_kitten_cmdline(q: Sequence[str]) -> bool: return q[1:3] == ['+runpy', 'from kittens.runner import main; main()'] and len(q) >= 6 and q[5] == 'ssh' -def patch_cmdline(key: str, val: str, argv: List[str]) -> None: +def patch_cmdline(key: str, val: str, argv: list[str]) -> None: for i, arg in enumerate(tuple(argv)): if arg.startswith(f'--kitten={key}='): argv[i] = f'--kitten={key}={val}' @@ -80,7 +81,7 @@ def patch_cmdline(key: str, val: str, argv: List[str]) -> None: argv.insert(idx + 1, f'--kitten={key}={val}') -def set_cwd_in_cmdline(cwd: str, argv: List[str]) -> None: +def set_cwd_in_cmdline(cwd: str, argv: list[str]) -> None: patch_cmdline('cwd', cwd, argv) @@ -135,7 +136,7 @@ def get_ssh_data(msgb: memoryview, request_id: str) -> Iterator[bytes]: raise ValueError(f'Incorrect request id: {rq_id!r} expecting the KITTY_PID-KITTY_WINDOW_ID for the current kitty window') except Exception as e: traceback.print_exc() - yield f'{e}\n'.encode('utf-8') + yield f'{e}\n'.encode() else: yield b'OK\n' encoded_data = memoryview(env_data['tarfile'].encode('ascii')) @@ -150,7 +151,7 @@ def get_ssh_data(msgb: memoryview, request_id: str) -> Iterator[bytes]: yield b'KITTY_DATA_END\n' -def set_env_in_cmdline(env: Dict[str, str], argv: List[str], clone: bool = True) -> None: +def set_env_in_cmdline(env: dict[str, str], argv: list[str], clone: bool = True) -> None: from kitty.options.utils import DELETE_ENV_VAR if clone: patch_cmdline('clone_env', create_shared_memory(env, 'ksse-'), argv) @@ -171,9 +172,9 @@ def set_env_in_cmdline(env: Dict[str, str], argv: List[str], clone: bool = True) argv[idx+1:idx+1] = env_dirs -def get_ssh_cli() -> Tuple[Set[str], Set[str]]: - other_ssh_args: Set[str] = set() - boolean_ssh_args: Set[str] = set() +def get_ssh_cli() -> tuple[set[str], set[str]]: + other_ssh_args: set[str] = set() + boolean_ssh_args: set[str] = set() for k, v in ssh_options().items(): k = f'-{k}' if v: @@ -183,7 +184,7 @@ def get_ssh_cli() -> Tuple[Set[str], Set[str]]: return boolean_ssh_args, other_ssh_args -def is_extra_arg(arg: str, extra_args: Tuple[str, ...]) -> str: +def is_extra_arg(arg: str, extra_args: tuple[str, ...]) -> str: for x in extra_args: if arg == x or arg.startswith(f'{x}='): return x @@ -194,14 +195,14 @@ passthrough_args = {f'-{x}' for x in 'NnfGT'} def set_server_args_in_cmdline( - server_args: List[str], argv: List[str], - extra_args: Tuple[str, ...] = ('--kitten',), + server_args: list[str], argv: list[str], + extra_args: tuple[str, ...] = ('--kitten',), allocate_tty: bool = False ) -> None: boolean_ssh_args, other_ssh_args = get_ssh_cli() ssh_args = [] expecting_option_val = False - found_extra_args: List[str] = [] + found_extra_args: list[str] = [] expecting_extra_val = '' ans = list(argv) found_ssh = False @@ -257,15 +258,15 @@ def set_server_args_in_cmdline( argv[:] = ans + server_args -def get_connection_data(args: List[str], cwd: str = '', extra_args: Tuple[str, ...] = ()) -> Optional[SSHConnectionData]: +def get_connection_data(args: list[str], cwd: str = '', extra_args: tuple[str, ...] = ()) -> SSHConnectionData | None: boolean_ssh_args, other_ssh_args = get_ssh_cli() - port: Optional[int] = None + port: int | None = None expecting_port = expecting_identity = False expecting_option_val = False expecting_hostname = False expecting_extra_val = '' host_name = identity_file = found_ssh = '' - found_extra_args: List[Tuple[str, str]] = [] + found_extra_args: list[tuple[str, str]] = [] for i, arg in enumerate(args): if not found_ssh: diff --git a/kittens/themes/main.py b/kittens/themes/main.py index a47296b8f..2afd31f41 100644 --- a/kittens/themes/main.py +++ b/kittens/themes/main.py @@ -2,7 +2,6 @@ # License: GPLv3 Copyright: 2021, Kovid Goyal import sys -from typing import List from kitty.cli import CompletionSpec @@ -46,7 +45,7 @@ to your kitty.conf and then have the kitten operate only on :file:`themes.conf`, allowing :code:`kitty.conf` to remain unchanged. '''.format -def main(args: List[str]) -> None: +def main(args: list[str]) -> None: raise SystemExit('This must be run as kitten themes') if __name__ == '__main__': diff --git a/kittens/transfer/main.py b/kittens/transfer/main.py index 7ba499df5..0168b4851 100644 --- a/kittens/transfer/main.py +++ b/kittens/transfer/main.py @@ -3,7 +3,6 @@ import sys -from typing import List usage = 'source_files_or_directories destination_path' help_text = '''\ @@ -121,7 +120,7 @@ actually degrade performance on fast links or with small files, so use with care ''' -def main(args: List[str]) -> None: +def main(args: list[str]) -> None: raise SystemExit('This should be run as kitten transfer') diff --git a/kittens/transfer/utils.py b/kittens/transfer/utils.py index 55203f19b..f3735d0c9 100644 --- a/kittens/transfer/utils.py +++ b/kittens/transfer/utils.py @@ -2,8 +2,8 @@ # License: GPLv3 Copyright: 2021, Kovid Goyal import os +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator _cwd = _home = '' diff --git a/kittens/tui/dircolors.py b/kittens/tui/dircolors.py index 804b865ca..93277a26d 100644 --- a/kittens/tui/dircolors.py +++ b/kittens/tui/dircolors.py @@ -3,8 +3,8 @@ import os import stat +from collections.abc import Generator from contextlib import suppress -from typing import Dict, Generator, Optional, Tuple, Union DEFAULT_DIRCOLORS = r"""# {{{ # Configuration file for dircolors, a utility to help you set the @@ -236,8 +236,8 @@ CODE_MAP = { } -def stat_at(file: str, cwd: Optional[Union[int, str]] = None, follow_symlinks: bool = False) -> os.stat_result: - dirfd: Optional[int] = None +def stat_at(file: str, cwd: int | str | None = None, follow_symlinks: bool = False) -> os.stat_result: + dirfd: int | None = None need_to_close = False if isinstance(cwd, str): dirfd = os.open(cwd, os.O_RDONLY | getattr(os, 'O_CLOEXEC', 0)) @@ -255,8 +255,8 @@ def stat_at(file: str, cwd: Optional[Union[int, str]] = None, follow_symlinks: b class Dircolors: def __init__(self) -> None: - self.codes: Dict[str, str] = {} - self.extensions: Dict[str, str] = {} + self.codes: dict[str, str] = {} + self.extensions: dict[str, str] = {} if not self.load_from_environ() and not self.load_from_file(): self.load_defaults() @@ -324,7 +324,7 @@ class Dircolors: def generate_lscolors(self) -> str: """ Output the database in the format used by the LS_COLORS environment variable. """ - def gen_pairs() -> Generator[Tuple[str, str], None, None]: + def gen_pairs() -> Generator[tuple[str, str], None, None]: for pair in self.codes.items(): yield pair for pair in self.extensions.items(): @@ -370,7 +370,7 @@ class Dircolors: return self._format_ext(text, ext) return text - def __call__(self, path: str, text: str, cwd: Optional[Union[int, str]] = None) -> str: + def __call__(self, path: str, text: str, cwd: int | str | None = None) -> str: follow_symlinks = self.codes.get('ln') == 'target' try: sr = stat_at(path, cwd, follow_symlinks) diff --git a/kittens/tui/handler.py b/kittens/tui/handler.py index af6a2b691..30f684b8c 100644 --- a/kittens/tui/handler.py +++ b/kittens/tui/handler.py @@ -4,9 +4,10 @@ import os from collections import deque +from collections.abc import Callable, Sequence from contextlib import suppress from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, ContextManager, Deque, Dict, NamedTuple, Optional, Sequence, Type, Union, cast +from typing import TYPE_CHECKING, Any, ContextManager, Deque, NamedTuple, Optional, cast from kitty.constants import kitten_exe, running_in_kitty from kitty.fast_data_types import monotonic, safe_pipe @@ -51,9 +52,9 @@ def is_click(a: ButtonEvent, b: ButtonEvent) -> bool: class KittenUI: allow_remote_control: bool = False - remote_control_password: Union[bool, str] = False + remote_control_password: bool | str = False - def __init__(self, func: Callable[[list[str]], str], allow_remote_control: bool, remote_control_password: Union[bool, str]): + def __init__(self, func: Callable[[list[str]], str], allow_remote_control: bool, remote_control_password: bool | str): self.func = func self.allow_remote_control = allow_remote_control self.remote_control_password = remote_control_password @@ -94,7 +95,7 @@ class KittenUI: if self.password: os.environ.pop('KITTY_RC_PASSWORD', None) - def remote_control(self, cmd: Union[str, Sequence[str]], **kw: Any) -> Any: + def remote_control(self, cmd: str | Sequence[str], **kw: Any) -> Any: if not self.allow_remote_control: raise ValueError('Remote control is not enabled, remember to use allow_remote_control=True') prefix = [kitten_exe(), '@'] @@ -132,7 +133,7 @@ class KittenUI: def kitten_ui( allow_remote_control: bool = KittenUI.allow_remote_control, - remote_control_password: Union[bool, str] = KittenUI.allow_remote_control, + remote_control_password: bool | str = KittenUI.allow_remote_control, ) -> Callable[[Callable[[list[str]], str]], KittenUI]: def wrapper(impl: Callable[..., Any]) -> KittenUI: @@ -143,7 +144,7 @@ def kitten_ui( class Handler: - image_manager_class: Optional[Type[ImageManagerType]] = None + image_manager_class: type[ImageManagerType] | None = None use_alternate_screen = True mouse_tracking = MouseTracking.none terminal_io_ended = False @@ -156,7 +157,7 @@ class Handler: schedule_write: Callable[[bytes], None], tui_loop: LoopType, debug: Debug, - image_manager: Optional[ImageManagerType] = None + image_manager: ImageManagerType | None = None ) -> None: from .operations import commander self.screen_size = screen_size @@ -166,7 +167,7 @@ class Handler: self.debug = debug self.cmd = commander(self) self._image_manager = image_manager - self._button_events: Dict[MouseButton, Deque[ButtonEvent]] = {} + self._button_events: dict[MouseButton, Deque[ButtonEvent]] = {} @property def image_manager(self) -> ImageManagerType: @@ -177,15 +178,15 @@ class Handler: def asyncio_loop(self) -> AbstractEventLoop: return self._tui_loop.asyncio_loop - def add_shortcut(self, action: KeyActionType, spec: Union[str, ParsedShortcut]) -> None: + def add_shortcut(self, action: KeyActionType, spec: str | ParsedShortcut) -> None: if not hasattr(self, '_key_shortcuts'): - self._key_shortcuts: Dict[ParsedShortcut, KeyActionType] = {} + self._key_shortcuts: dict[ParsedShortcut, KeyActionType] = {} if isinstance(spec, str): from kitty.key_encoding import parse_shortcut spec = parse_shortcut(spec) self._key_shortcuts[spec] = action - def shortcut_action(self, key_event: KeyEventType) -> Optional[KeyActionType]: + def shortcut_action(self, key_event: KeyEventType) -> KeyActionType | None: for sc, action in self._key_shortcuts.items(): if key_event.matches(sc): return action @@ -213,7 +214,7 @@ class Handler: def on_resize(self, screen_size: ScreenSize) -> None: self.screen_size = screen_size - def quit_loop(self, return_code: Optional[int] = None) -> None: + def quit_loop(self, return_code: int | None = None) -> None: self._tui_loop.quit(return_code) def on_term(self) -> None: @@ -278,7 +279,7 @@ class Handler: def on_writing_finished(self) -> None: pass - def on_kitty_cmd_response(self, response: Dict[str, Any]) -> None: + def on_kitty_cmd_response(self, response: dict[str, Any]) -> None: pass def on_clipboard_response(self, text: str, from_primary: bool = False) -> None: @@ -290,7 +291,7 @@ class Handler: def on_capability_response(self, name: str, val: str) -> None: pass - def write(self, data: Union[bytes, str]) -> None: + def write(self, data: bytes | str) -> None: if isinstance(data, str): data = data.encode('utf-8') self._schedule_write(data) @@ -318,10 +319,10 @@ class Handler: class HandleResult: - type_of_input: Optional[str] = None + type_of_input: str | None = None no_ui: bool = False - def __init__(self, impl: Callable[..., Any], type_of_input: Optional[str], no_ui: bool, has_ready_notification: bool, open_url_handler: OpenUrlHandler): + def __init__(self, impl: Callable[..., Any], type_of_input: str | None, no_ui: bool, has_ready_notification: bool, open_url_handler: OpenUrlHandler): self.impl = impl self.no_ui = no_ui self.type_of_input = type_of_input @@ -334,7 +335,7 @@ class HandleResult: def result_handler( - type_of_input: Optional[str] = None, + type_of_input: str | None = None, no_ui: bool = False, has_ready_notification: bool = Handler.overlay_ready_report_needed, open_url_handler: OpenUrlHandler = None, diff --git a/kittens/tui/images.py b/kittens/tui/images.py index 11459b489..e3ef75a59 100644 --- a/kittens/tui/images.py +++ b/kittens/tui/images.py @@ -6,10 +6,11 @@ import os import sys from base64 import standard_b64encode from collections import defaultdict, deque +from collections.abc import Callable, Iterator, Sequence from contextlib import suppress from enum import IntEnum from itertools import count -from typing import Any, Callable, ClassVar, DefaultDict, Deque, Dict, Generic, Iterator, List, Optional, Sequence, Tuple, Type, TypeVar, Union, cast +from typing import Any, ClassVar, DefaultDict, Deque, Generic, Optional, TypeVar, Union, cast from kitty.conf.utils import positive_float, positive_int from kitty.fast_data_types import create_canvas @@ -49,7 +50,7 @@ class Frame: dispose: Dispose path: str = '' - def __init__(self, identify_data: Union['Frame', Dict[str, str]]): + def __init__(self, identify_data: Union['Frame', dict[str, str]]): if isinstance(identify_data, Frame): for k in Frame.__annotations__: setattr(self, k, getattr(identify_data, k)) @@ -78,7 +79,7 @@ class Frame: class ImageData: - def __init__(self, fmt: str, width: int, height: int, mode: str, frames: List[Frame]): + def __init__(self, fmt: str, width: int, height: int, mode: str, frames: list[Frame]): self.width, self.height, self.fmt, self.mode = width, height, fmt, mode self.transmit_fmt: GRT_f = (24 if self.mode == 'rgb' else 32) self.frames = frames @@ -291,9 +292,9 @@ def render_as_single_image( path: str, m: ImageData, available_width: int, available_height: int, scale_up: bool, - tdir: Optional[str] = None, + tdir: str | None = None, remove_alpha: str = '', flip: bool = False, flop: bool = False, -) -> Tuple[str, int, int]: +) -> tuple[str, int, int]: import tempfile fd, output = tempfile.mkstemp(prefix='tty-graphics-protocol-', suffix=f'.{m.mode}', dir=tdir) os.close(fd) @@ -305,15 +306,15 @@ def render_as_single_image( def can_display_images() -> bool: - ans: Optional[bool] = getattr(can_display_images, 'ans', None) + ans: bool | None = getattr(can_display_images, 'ans', None) if ans is None: ans = which('convert') is not None setattr(can_display_images, 'ans', ans) return ans -ImageKey = Tuple[str, int, int] -SentImageKey = Tuple[int, int, int] +ImageKey = tuple[str, int, int] +SentImageKey = tuple[int, int, int] T = TypeVar('T') @@ -325,7 +326,7 @@ class Alias(Generic[T]): self.name = '' self.defval = defval - def __get__(self, instance: Optional['GraphicsCommand'], cls: Optional[Type['GraphicsCommand']] = None) -> T: + def __get__(self, instance: Optional['GraphicsCommand'], cls: type['GraphicsCommand'] | None = None) -> T: if instance is None: return self.defval return cast(T, instance._actual_values.get(self.name, self.defval)) @@ -336,7 +337,7 @@ class Alias(Generic[T]): else: instance._actual_values[self.name] = val - def __set_name__(self, owner: Type['GraphicsCommand'], name: str) -> None: + def __set_name__(self, owner: type['GraphicsCommand'], name: str) -> None: if len(name) == 1: Alias.currently_processing = name self.name = Alias.currently_processing @@ -369,7 +370,7 @@ class GraphicsCommand: d = delete_action = Alias(cast(GRT_d, 'a')) def __init__(self) -> None: - self._actual_values: Dict[str, Any] = {} + self._actual_values: dict[str, Any] = {} def __repr__(self) -> str: return self.serialize().decode('ascii').replace('\033', '^]') @@ -379,12 +380,12 @@ class GraphicsCommand: ans._actual_values = self._actual_values.copy() return ans - def serialize(self, payload: Union[bytes, str] = b'') -> bytes: + def serialize(self, payload: bytes | str = b'') -> bytes: items = [] for k, val in self._actual_values.items(): items.append(f'{k}={val}') - ans: List[bytes] = [] + ans: list[bytes] = [] w = ans.append w(b'\033_G') w(','.join(items).encode('ascii')) @@ -399,7 +400,7 @@ class GraphicsCommand: def clear(self) -> None: self._actual_values = {} - def iter_transmission_chunks(self, data: Optional[bytes] = None, level: int = -1, compression_threshold: int = 1024) -> Iterator[bytes]: + def iter_transmission_chunks(self, data: bytes | None = None, level: int = -1, compression_threshold: int = 1024) -> Iterator[bytes]: if data is None: yield self.serialize() return @@ -436,16 +437,16 @@ class ImageManager: def __init__(self, handler: HandlerType): self.image_id_counter = count() self.handler = handler - self.filesystem_ok: Optional[bool] = None - self.image_data: Dict[str, ImageData] = {} - self.failed_images: Dict[str, Exception] = {} - self.converted_images: Dict[ImageKey, ImageKey] = {} - self.sent_images: Dict[ImageKey, int] = {} - self.image_id_to_image_data: Dict[int, ImageData] = {} - self.image_id_to_converted_data: Dict[int, ImageKey] = {} - self.transmission_status: Dict[int, Union[str, int]] = {} + self.filesystem_ok: bool | None = None + self.image_data: dict[str, ImageData] = {} + self.failed_images: dict[str, Exception] = {} + self.converted_images: dict[ImageKey, ImageKey] = {} + self.sent_images: dict[ImageKey, int] = {} + self.image_id_to_image_data: dict[int, ImageData] = {} + self.image_id_to_converted_data: dict[int, ImageKey] = {} + self.transmission_status: dict[int, str | int] = {} self.placements_in_flight: DefaultDict[int, Deque[Placement]] = defaultdict(deque) - self.update_image_placement_for_resend: Optional[Callable[[int, Placement], bool]] + self.update_image_placement_for_resend: Callable[[int, Placement], bool] | None @property def next_image_id(self) -> int: @@ -518,7 +519,7 @@ class ImageManager: self.handler.cmd.set_cursor_position(pl.x, pl.y) self.handler.cmd.gr_command(pl.cmd) - def send_image(self, path: str, max_cols: Optional[int] = None, max_rows: Optional[int] = None, scale_up: bool = False) -> SentImageKey: + def send_image(self, path: str, max_cols: int | None = None, max_rows: int | None = None, scale_up: bool = False) -> SentImageKey: path = os.path.abspath(path) if path in self.failed_images: raise self.failed_images[path] @@ -562,7 +563,7 @@ class ImageManager: gc.i = image_id self.handler.cmd.gr_command(gc) - def show_image(self, image_id: int, x: int, y: int, src_rect: Optional[Tuple[int, int, int, int]] = None) -> None: + def show_image(self, image_id: int, x: int, y: int, src_rect: tuple[int, int, int, int] | None = None) -> None: gc = GraphicsCommand() gc.a = 'p' gc.i = image_id diff --git a/kittens/tui/line_edit.py b/kittens/tui/line_edit.py index c48840b9e..d1f3f017f 100644 --- a/kittens/tui/line_edit.py +++ b/kittens/tui/line_edit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPL v3 Copyright: 2018, Kovid Goyal -from typing import Callable, Tuple +from collections.abc import Callable from kitty.fast_data_types import truncate_point_for_length, wcswidth from kitty.key_encoding import EventType, KeyEvent @@ -20,7 +20,7 @@ class LineEdit: self.cursor_pos = 0 self.pending_bell = False - def split_at_cursor(self, delta: int = 0) -> Tuple[str, str]: + 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:] diff --git a/kittens/tui/loop.py b/kittens/tui/loop.py index 069ad6956..9f9866f69 100644 --- a/kittens/tui/loop.py +++ b/kittens/tui/loop.py @@ -10,10 +10,11 @@ import selectors import signal import sys import termios +from collections.abc import Callable, Generator from contextlib import contextmanager, suppress from enum import Enum, IntFlag, auto from functools import partial -from typing import Any, Callable, Dict, Generator, List, NamedTuple, Optional +from typing import Any, NamedTuple from kitty.constants import is_macos from kitty.fast_data_types import FILE_TRANSFER_CODE, close_tty, normal_tty, open_tty, parse_input_from_terminal, raw_tty @@ -50,7 +51,7 @@ def debug_write(*a: Any, **kw: Any) -> None: class Debug: - fobj: Optional[BinaryWrite] = None + fobj: BinaryWrite | None = None def __call__(self, *a: Any, **kw: Any) -> None: kw['file'] = self.fobj or sys.stdout.buffer @@ -67,7 +68,7 @@ class TermManager: self, optional_actions: int = termios.TCSANOW, use_alternate_screen: bool = True, mouse_tracking: MouseTracking = MouseTracking.none ) -> None: - self.extra_finalize: Optional[str] = None + self.extra_finalize: str | None = None self.optional_actions = optional_actions self.use_alternate_screen = use_alternate_screen self.mouse_tracking = mouse_tracking @@ -377,7 +378,7 @@ class Loop: else: written = 0 if written >= total_size: - self.write_buf: List[bytes] = [] + self.write_buf: list[bytes] = [] self.asyncio_loop.remove_writer(fd) self.waiting_for_writes = False handler.on_writing_finished() @@ -394,12 +395,12 @@ class Loop: break del self.write_buf[:consumed] - def quit(self, return_code: Optional[int] = None) -> None: + def quit(self, return_code: int | None = None) -> None: if return_code is not None: self.return_code = return_code self.asyncio_loop.stop() - def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: Optional[ImageManagerType] = None) -> Optional[str]: + def loop_impl(self, handler: Handler, term_manager: TermManager, image_manager: ImageManagerType | None = None) -> str | None: self.write_buf = [] tty_fd = term_manager.tty_fd tb = None @@ -411,7 +412,7 @@ class Loop: self.asyncio_loop.add_writer(tty_fd, self._write_ready, handler, tty_fd) self.waiting_for_writes = True - def handle_exception(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None: + def handle_exception(loop: asyncio.AbstractEventLoop, context: dict[str, Any]) -> None: nonlocal tb loop.stop() tb = context['message'] @@ -436,7 +437,7 @@ class Loop: return tb def loop(self, handler: Handler) -> None: - tb: Optional[str] = None + tb: str | None = None def _on_sigwinch() -> None: self._get_screen_size.changed = True diff --git a/kittens/tui/operations.py b/kittens/tui/operations.py index bd833e739..026e5be66 100644 --- a/kittens/tui/operations.py +++ b/kittens/tui/operations.py @@ -3,10 +3,11 @@ import os import sys +from collections.abc import Callable, Generator from contextlib import contextmanager from enum import Enum, auto from functools import wraps -from typing import Any, Callable, Dict, Generator, Optional, TypeVar, Union +from typing import Any, Optional, TypeVar, Union from kitty.fast_data_types import Color from kitty.rgb import color_as_sharp, to_color @@ -22,7 +23,7 @@ RESTORE_PRIVATE_MODE_VALUES = '\033[?r' SAVE_COLORS = '\033[#P' RESTORE_COLORS = '\033[#Q' F = TypeVar('F') -all_cmds: Dict[str, Callable[..., Any]] = {} +all_cmds: dict[str, Callable[..., Any]] = {} class Mode(Enum): @@ -146,7 +147,7 @@ def set_cursor_shape(shape: str = 'block', blink: bool = True) -> str: @cmd -def set_scrolling_region(screen_size: Optional['ScreenSize'] = None, top: Optional[int] = None, bottom: Optional[int] = None) -> str: +def set_scrolling_region(screen_size: Optional['ScreenSize'] = None, top: int | None = None, bottom: int | None = None) -> str: if screen_size is None: return '\033[r' if top is None: @@ -192,7 +193,7 @@ def colored( text: str, color: ColorSpec, intense: bool = False, - reset_to: Optional[ColorSpec] = None, + reset_to: ColorSpec | None = None, reset_to_intense: bool = False ) -> str: e = color_code(color, intense) @@ -207,16 +208,16 @@ def faint(text: str) -> str: @cmd def styled( text: str, - fg: Optional[ColorSpec] = None, - bg: Optional[ColorSpec] = None, + fg: ColorSpec | None = None, + bg: ColorSpec | None = None, fg_intense: bool = False, bg_intense: bool = False, - italic: Optional[bool] = None, - bold: Optional[bool] = None, - underline: Optional[UnderlineLiteral] = None, - underline_color: Optional[ColorSpec] = None, - reverse: Optional[bool] = None, - dim: Optional[bool] = None, + italic: bool | None = None, + bold: bool | None = None, + underline: UnderlineLiteral | None = None, + underline_color: ColorSpec | None = None, + reverse: bool | None = None, + dim: bool | None = None, ) -> str: start, end = [], [] if fg is not None: @@ -254,7 +255,7 @@ def styled( return '\033[{}m{}\033[{}m'.format(';'.join(start), text, ';'.join(end)) -def serialize_gr_command(cmd: Dict[str, Union[int, str]], payload: Optional[bytes] = None) -> bytes: +def serialize_gr_command(cmd: dict[str, int | str], payload: bytes | None = None) -> bytes: from .images import GraphicsCommand gc = GraphicsCommand() for k, v in cmd.items(): @@ -263,7 +264,7 @@ def serialize_gr_command(cmd: Dict[str, Union[int, str]], payload: Optional[byte @cmd -def gr_command(cmd: Union[Dict[str, Union[int, str]], 'GraphicsCommandType'], payload: Optional[bytes] = None) -> str: +def gr_command(cmd: Union[dict[str, int | str], 'GraphicsCommandType'], payload: bytes | None = None) -> str: if isinstance(cmd, dict): raw = serialize_gr_command(cmd, payload) else: @@ -358,7 +359,7 @@ def alternate_screen() -> Generator[None, None, None]: @contextmanager -def raw_mode(fd: Optional[int] = None) -> Generator[None, None, None]: +def raw_mode(fd: int | None = None) -> Generator[None, None, None]: import termios import tty if fd is None: @@ -373,15 +374,15 @@ def raw_mode(fd: Optional[int] = None) -> Generator[None, None, None]: @cmd def set_default_colors( - fg: Optional[Union[Color, str]] = None, - bg: Optional[Union[Color, str]] = None, - cursor: Optional[Union[Color, str]] = None, - select_bg: Optional[Union[Color, str]] = None, - select_fg: Optional[Union[Color, str]] = None + fg: Color | str | None = None, + bg: Color | str | None = None, + cursor: Color | str | None = None, + select_bg: Color | str | None = None, + select_fg: Color | str | None = None ) -> str: ans = '' - def item(which: Optional[Union[Color, str]], num: int) -> None: + def item(which: Color | str | None, num: int) -> None: nonlocal ans if which is None: ans += f'\x1b]1{num}\x1b\\' @@ -418,7 +419,7 @@ def overlay_ready() -> str: @cmd -def write_to_clipboard(data: Union[str, bytes], use_primary: bool = False) -> str: +def write_to_clipboard(data: str | bytes, use_primary: bool = False) -> str: from base64 import standard_b64encode fmt = 'p' if use_primary else 'c' if isinstance(data, str): @@ -435,7 +436,7 @@ def request_from_clipboard(use_primary: bool = False) -> str: # Boilerplate to make operations available via Handler.cmd {{{ -def writer(handler: HandlerType, func: Callable[..., Union[bytes, str]]) -> Callable[..., None]: +def writer(handler: HandlerType, func: Callable[..., bytes | str]) -> Callable[..., None]: @wraps(func) def f(*a: Any, **kw: Any) -> None: handler.write(func(*a, **kw)) diff --git a/kittens/tui/path_completer.py b/kittens/tui/path_completer.py index 363dd7de1..a19208b4d 100644 --- a/kittens/tui/path_completer.py +++ b/kittens/tui/path_completer.py @@ -3,7 +3,8 @@ import os -from typing import Any, Callable, Dict, Generator, Optional, Sequence, Tuple +from collections.abc import Callable, Generator, Sequence +from typing import Any from kitty.fast_data_types import wcswidth from kitty.utils import ScreenSize, screen_size_function @@ -93,7 +94,7 @@ class PathCompleter: readline.set_completion_display_matches_hook(self.format_completions) self.original_completer = readline.get_completer() readline.set_completer(self) - self.cache: Dict[str, Tuple[str, ...]] = {} + self.cache: dict[str, tuple[str, ...]] = {} self.dircolors = Dircolors() return self @@ -126,7 +127,7 @@ class PathCompleter: print(f"\r\033[{pos}C", end='') print(sep='', end='', flush=True) - def __call__(self, text: str, state: int) -> Optional[str]: + def __call__(self, text: str, state: int) -> str | None: options = self.cache.get(text) if options is None: options = self.cache[text] = tuple(find_completions(text)) diff --git a/kittens/tui/spinners.py b/kittens/tui/spinners.py index 8b5c4cd9e..5b455c483 100644 --- a/kittens/tui/spinners.py +++ b/kittens/tui/spinners.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2021, Kovid Goyal -from typing import Dict, Sequence +from collections.abc import Sequence from kitty.fast_data_types import monotonic from kitty.typing import TypedDict @@ -14,7 +14,7 @@ class SpinnerDef(TypedDict): # Spinner definitions are from # https://raw.githubusercontent.com/sindresorhus/cli-spinners/main/spinners.json -spinners: Dict[str, SpinnerDef] = { # {{{ +spinners: dict[str, SpinnerDef] = { # {{{ "dots": { "interval": 80, "frames": ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] diff --git a/kittens/tui/utils.py b/kittens/tui/utils.py index be800052f..d45298ea5 100644 --- a/kittens/tui/utils.py +++ b/kittens/tui/utils.py @@ -3,8 +3,9 @@ import os import sys +from collections.abc import Sequence from contextlib import suppress -from typing import TYPE_CHECKING, Optional, Sequence, Tuple, cast +from typing import TYPE_CHECKING, Optional, cast from kitty.types import run_once @@ -46,7 +47,7 @@ def format_number(val: float, max_num_of_decimals: int = 2) -> str: def human_size( size: int, sep: str = ' ', max_num_of_decimals: int = 2, - unit_list: Tuple[str, ...] = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB') + unit_list: tuple[str, ...] = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB') ) -> str: """ Convert a size in bytes into a human readable form """ if size < 2: diff --git a/kittens/unicode_input/main.py b/kittens/unicode_input/main.py index 1c41b5d67..384acaa3e 100644 --- a/kittens/unicode_input/main.py +++ b/kittens/unicode_input/main.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # License: GPL v3 Copyright: 2018, Kovid Goyal -from typing import List, Optional from kitty.typing import BossType @@ -29,12 +28,12 @@ The initial tab to display. Defaults to using the tab from the previous kitten i @result_handler(has_ready_notification=True) -def handle_result(args: List[str], current_char: str, target_window_id: int, boss: BossType) -> None: +def handle_result(args: list[str], current_char: str, target_window_id: int, boss: BossType) -> None: w = boss.window_id_map.get(target_window_id) if w is not None: w.paste_text(current_char) -def main(args: List[str]) -> Optional[str]: +def main(args: list[str]) -> str | None: raise SystemExit('This should be run as kitten unicode_input') if __name__ == '__main__': diff --git a/kitty/boss.py b/kitty/boss.py index b7219ac51..183893d17 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -9,7 +9,7 @@ import re import socket import subprocess import sys -from collections.abc import Container, Generator, Iterable, Iterator, Sequence +from collections.abc import Callable, Container, Generator, Iterable, Iterator, Sequence from contextlib import contextmanager, suppress from functools import partial from gettext import gettext as _ @@ -18,7 +18,6 @@ from time import sleep from typing import ( TYPE_CHECKING, Any, - Callable, Optional, Union, ) @@ -155,7 +154,7 @@ RCResponse = Union[dict[str, Any], None, AsyncResponse] class OSWindowDict(TypedDict): id: int - platform_window_id: Optional[int] + platform_window_id: int | None is_focused: bool is_active: bool last_focused: bool @@ -168,7 +167,7 @@ class OSWindowDict(TypedDict): class Atexit: def __init__(self) -> None: - self.worker: Optional[subprocess.Popen[bytes]] = None + self.worker: subprocess.Popen[bytes] | None = None def _write_line(self, line: str) -> None: if '\n' in line: @@ -208,7 +207,7 @@ def listen_on(spec: str, robust_atexit: Atexit) -> tuple[int, str]: return s.fileno(), spec -def data_for_at(w: Optional[Window], arg: str, add_wrap_markers: bool = False) -> Optional[str]: +def data_for_at(w: Window | None, arg: str, add_wrap_markers: bool = False) -> str | None: if not w: return None @@ -284,10 +283,10 @@ class VisualSelect: self, tab_id: int, os_window_id: int, - prev_tab_id: Optional[int], - prev_os_window_id: Optional[int], + prev_tab_id: int | None, + prev_os_window_id: int | None, title: str, - callback: Callable[[Optional[Tab], Optional[Window]], None], + callback: Callable[[Tab | None, Window | None], None], reactivate_prev_tab: bool ) -> None: self.tab_id = tab_id @@ -358,19 +357,19 @@ class Boss: self.atexit = Atexit() set_layout_options(opts) self.clipboard = Clipboard() - self.window_for_dispatch: Optional[Window] = None + self.window_for_dispatch: Window | None = None self.primary_selection = Clipboard(ClipboardType.primary_selection) self.update_check_started = False - self.peer_data_map: dict[int, Optional[dict[str, Sequence[str]]]] = {} - self.background_process_death_notify_map: dict[int, Callable[[int, Optional[Exception]], None]] = {} + self.peer_data_map: dict[int, dict[str, Sequence[str]] | None] = {} + self.background_process_death_notify_map: dict[int, Callable[[int, Exception | None], None]] = {} self.encryption_key = EllipticCurveKey() self.encryption_public_key = f'{RC_ENCRYPTION_PROTOCOL_VERSION}:{base64.b85encode(self.encryption_key.public).decode("ascii")}' self.clipboard_buffers: dict[str, str] = {} self.update_check_process: Optional['PopenType[bytes]'] = None self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary() - self.color_settings_at_startup: dict[str, Optional[Color]] = { + self.color_settings_at_startup: dict[str, Color | None] = { k: opts[k] for k in opts if isinstance(opts[k], Color) or k in nullable_colors} - self.current_visual_select: Optional[VisualSelect] = None + self.current_visual_select: VisualSelect | None = None # A list of events received so far that are potentially part of a sequence keybinding. self.cached_values = cached_values self.os_window_map: dict[int, TabManager] = {} @@ -399,13 +398,13 @@ class Boss: talk_fd, listen_fd, self.listening_on.startswith('unix:') ) self.args: CLIOptions = args - self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None + self.mouse_handler: Callable[[WindowSystemMouseEvent], None] | None = None set_boss(self) self.mappings: Mappings = Mappings(global_shortcuts, self.refresh_active_tab_bar) self.notification_manager: NotificationManager = NotificationManager(debug=self.args.debug_keyboard or self.args.debug_rendering) self.atexit.unlink(store_effective_config()) - def startup_first_child(self, os_window_id: Optional[int], startup_sessions: Iterable[Session] = ()) -> None: + def startup_first_child(self, os_window_id: int | None, startup_sessions: Iterable[Session] = ()) -> None: si = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session) focused_os_window = wid = 0 token = os.environ.pop('XDG_ACTIVATION_TOKEN', '') @@ -426,14 +425,14 @@ class Boss: def add_os_window( self, - startup_session: Optional[Session] = None, - os_window_id: Optional[int] = None, - wclass: Optional[str] = None, - wname: Optional[str] = None, - window_state: Optional[str] = None, - opts_for_size: Optional[Options] = None, - startup_id: Optional[str] = None, - override_title: Optional[str] = None, + startup_session: Session | None = None, + os_window_id: int | None = None, + wclass: str | None = None, + wname: str | None = None, + window_state: str | None = None, + opts_for_size: Options | None = None, + startup_id: str | None = None, + override_title: str | None = None, ) -> int: if os_window_id is None: size_data = get_os_window_sizing_data(opts_for_size or get_options(), startup_session) @@ -455,9 +454,9 @@ class Boss: return os_window_id def list_os_windows( - self, self_window: Optional[Window] = None, - tab_filter: Optional[Callable[[Tab], bool]] = None, - window_filter: Optional[Callable[[Window], bool]] = None + self, self_window: Window | None = None, + tab_filter: Callable[[Tab], bool] | None = None, + window_filter: Callable[[Window], bool] | None = None ) -> Iterator[OSWindowDict]: with cached_process_data(): active_tab_manager = self.active_tab_manager @@ -522,7 +521,7 @@ class Boss: ), set(self.window_id_map), get_matches): yield self.window_id_map[wid] - def tab_for_window(self, window: Window) -> Optional[Tab]: + def tab_for_window(self, window: Window) -> Tab | None: for tab in self.all_tabs: for w in tab: if w.id == window.id: @@ -567,7 +566,7 @@ class Boss: def set_active_window( self, window: Window, switch_os_window_if_needed: bool = False, for_keep_focus: bool = False, activation_token: str = '' - ) -> Optional[int]: + ) -> int | None: for os_window_id, tm in self.os_window_map.items(): for tab in tm: for w in tab: @@ -580,9 +579,9 @@ class Boss: return os_window_id return None - def _new_os_window(self, args: Union[SpecialWindowInstance, Iterable[str]], cwd_from: Optional[CwdRequest] = None) -> int: + def _new_os_window(self, args: SpecialWindowInstance | Iterable[str], cwd_from: CwdRequest | None = None) -> int: if isinstance(args, SpecialWindowInstance): - sw: Optional[SpecialWindowInstance] = args + sw: SpecialWindowInstance | None = args else: sw = self.args_to_special_window(args, cwd_from) if args else None startup_session = next(create_sessions(get_options(), special_window=sw, cwd_from=cwd_from)) @@ -593,7 +592,7 @@ class Boss: self._new_os_window(args) @property - def active_window_for_cwd(self) -> Optional[Window]: + def active_window_for_cwd(self) -> Window | None: t = self.active_tab if t is not None: return t.active_window_for_cwd @@ -604,7 +603,7 @@ class Boss: w = self.window_for_dispatch or self.active_window_for_cwd self._new_os_window(args, CwdRequest(w)) - def new_os_window_with_wd(self, wd: Union[str, list[str]], str_is_multiple_paths: bool = False) -> None: + def new_os_window_with_wd(self, wd: str | list[str], str_is_multiple_paths: bool = False) -> None: if isinstance(wd, str): wd = wd.split(os.pathsep) if str_is_multiple_paths else [wd] for path in wd: @@ -616,7 +615,7 @@ class Boss: self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen) self.window_id_map[window.id] = window - def _handle_remote_command(self, cmd: memoryview, window: Optional[Window] = None, peer_id: int = 0) -> RCResponse: + def _handle_remote_command(self, cmd: memoryview, window: Window | None = None, peer_id: int = 0) -> RCResponse: from .remote_control import is_cmd_allowed, parse_cmd, remote_control_allowed response = None window = window or None @@ -635,7 +634,7 @@ class Boss: return response if not pcmd: return response - self_window: Optional[Window] = None + self_window: Window | None = None if window is not None: self_window = window else: @@ -674,7 +673,7 @@ class Boss: return response def ask_if_remote_cmd_is_allowed( - self, pcmd: dict[str, Any], window: Optional[Window] = None, peer_id: int = 0, self_window: Optional[Window] = None + self, pcmd: dict[str, Any], window: Window | None = None, peer_id: int = 0, self_window: Window | None = None ) -> bool: from kittens.tui.operations import styled in_flight = 0 @@ -703,7 +702,7 @@ class Boss: overlay_window.window_custom_type = 'remote_command_permission_dialog' return True - def remote_cmd_permission_received(self, pcmd: dict[str, Any], window_id: int, peer_id: int, self_window: Optional[Window], choice: str) -> None: + def remote_cmd_permission_received(self, pcmd: dict[str, Any], window_id: int, peer_id: int, self_window: Window | None, choice: str) -> None: from .remote_control import encode_response_for_peer, set_user_password_allowed response: RCResponse = None window = self.window_id_map.get(window_id) @@ -727,7 +726,7 @@ class Boss: send_data_to_peer(peer_id, encode_response_for_peer(response)) def _execute_remote_command( - self, pcmd: dict[str, Any], window: Optional[Window] = None, peer_id: int = 0, self_window: Optional[Window] = None + self, pcmd: dict[str, Any], window: Window | None = None, peer_id: int = 0, self_window: Window | None = None ) -> RCResponse: from .remote_control import handle_cmd try: @@ -771,7 +770,7 @@ class Boss: return self.run_background_process([path] + list(args), allow_remote_control=True) - def call_remote_control(self, self_window: Optional[Window], args: tuple[str, ...]) -> 'ResponseType': + def call_remote_control(self, self_window: Window | None, args: tuple[str, ...]) -> 'ResponseType': from .rc.base import PayloadGetter, command_for_name, parse_subcommand_cli from .remote_control import parse_rc_args aa = list(args) @@ -802,7 +801,7 @@ class Boss: return None raise - def peer_message_received(self, msg_bytes: bytes, peer_id: int, is_remote_control: bool) -> Union[bytes, bool, None]: + def peer_message_received(self, msg_bytes: bytes, peer_id: int, is_remote_control: bool) -> bytes | bool | None: if peer_id > 0 and msg_bytes == b'peer_death': self.peer_data_map.pop(peer_id, None) return False @@ -875,7 +874,7 @@ class Boss: log_error('Unknown message received over single instance socket, ignoring') return None - def handle_remote_cmd(self, cmd: memoryview, window: Optional[Window] = None) -> None: + def handle_remote_cmd(self, cmd: memoryview, window: Window | None = None) -> None: response = self._handle_remote_command(cmd, window) if response is not None and not isinstance(response, AsyncResponse) and window is not None: window.send_cmd_response(response) @@ -946,7 +945,7 @@ class Boss: if window is not None: window.focus_changed(True) - def mark_window_for_close(self, q: Union[Window, None, int] = None) -> None: + def mark_window_for_close(self, q: Window | None | int = None) -> None: if isinstance(q, int): window = self.window_id_map.get(q) if window is None: @@ -988,7 +987,7 @@ class Boss: self.mark_window_for_close(window_id) @ac('tab', 'Close the current tab') - def close_tab(self, tab: Optional[Tab] = None) -> None: + def close_tab(self, tab: Tab | None = None) -> None: if tab is None and self.window_for_dispatch: tab = self.window_for_dispatch.tabref() tab = tab or self.active_tab @@ -996,7 +995,7 @@ class Boss: self.confirm_tab_close(tab) @property - def active_tab_manager_with_dispatch(self) -> Optional[TabManager]: + def active_tab_manager_with_dispatch(self) -> TabManager | None: if self.window_for_dispatch: td = self.window_for_dispatch.tabref() tm = td.tab_manager_ref() if td else None @@ -1025,7 +1024,7 @@ class Boss: self, msg: str, # can contain newlines and ANSI formatting callback: Callable[..., None], # called with True or False and *args *args: Any, # passed to the callback function - window: Optional[Window] = None, # the window associated with the confirmation + window: Window | None = None, # the window associated with the confirmation confirm_on_cancel: bool = False, # on closing window confirm_on_accept: bool = True, # on pressing enter title: str = '' # window title @@ -1052,13 +1051,13 @@ class Boss: self, msg: str, # can contain newlines and ANSI formatting callback: Callable[..., None], # called with the choice or empty string when aborted *choices: str, # The choices, see the help for the ask kitten for format of a choice - window: Optional[Window] = None, # the window associated with the confirmation + window: Window | None = None, # the window associated with the confirmation default: str = '', # the default choice when the user presses Enter hidden_text: str = '', # text to hide in the message hidden_text_placeholder: str = 'HIDDEN_TEXT_PLACEHOLDER', # placeholder text to insert in to message unhide_key: str = 'u', # key to press to unhide hidden text title: str = '' # window title - ) -> Optional[Window]: + ) -> Window | None: result: str = '' def callback_(res: dict[str, Any], x: int, boss: Boss) -> None: @@ -1094,7 +1093,7 @@ class Boss: def get_line( self, msg: str, # can contain newlines and ANSI formatting callback: Callable[..., None], # called with the answer or empty string when aborted - window: Optional[Window] = None, # the window associated with the confirmation + window: Window | None = None, # the window associated with the confirmation prompt: str = '> ', is_password: bool = False, initial_value: str = '' @@ -1307,7 +1306,7 @@ class Boss: See :ref:`conf-kitty-shortcuts.fonts` for details. ''') - def change_font_size(self, all_windows: bool, increment_operation: Optional[str], amt: float) -> None: + def change_font_size(self, all_windows: bool, increment_operation: str | None, amt: float) -> None: def calc_new_size(old_size: float) -> float: new_size = old_size if amt == 0: @@ -1394,7 +1393,7 @@ class Boss: self._set_os_window_background_opacity(os_window_id, fin_opacity) @property - def active_tab_manager(self) -> Optional[TabManager]: + def active_tab_manager(self) -> TabManager | None: os_window_id = current_focused_os_window_id() if os_window_id <= 0: os_window_id = last_focused_os_window_id() @@ -1405,12 +1404,12 @@ class Boss: return self.os_window_map.get(os_window_id) @property - def active_tab(self) -> Optional[Tab]: + def active_tab(self) -> Tab | None: tm = self.active_tab_manager return None if tm is None else tm.active_tab @property - def active_window(self) -> Optional[Window]: + def active_window(self) -> Window | None: t = self.active_tab return None if t is None else t.active_window @@ -1444,14 +1443,14 @@ class Boss: def visual_window_select_action( self, tab: Tab, - callback: Callable[[Optional[Tab], Optional[Window]], None], + callback: Callable[[Tab | None, Window | None], None], choose_msg: str, only_window_ids: Container[int] = (), reactivate_prev_tab: bool = False ) -> None: import string self.cancel_current_visual_select() - initial_tab_id: Optional[int] = None + initial_tab_id: int | None = None initial_os_window_id = current_os_window() tm = tab.tab_manager_ref() if tm is not None: @@ -1520,8 +1519,8 @@ class Boss: ev = WindowSystemMouseEvent(in_tab_bar, window_id, action, modifiers, button, currently_pressed_button, x, y) self.mouse_handler(ev) - def select_window_in_tab_using_overlay(self, tab: Tab, msg: str, only_window_ids: Container[int] = ()) -> Optional[Window]: - windows: list[tuple[Optional[int], str]] = [] + def select_window_in_tab_using_overlay(self, tab: Tab, msg: str, only_window_ids: Container[int] = ()) -> Window | None: + windows: list[tuple[int | None, str]] = [] selectable_windows: list[tuple[int, str]] = [] for i, w in tab.windows.iter_windows_with_number(only_visible=False): if only_window_ids and w.id not in only_window_ids: @@ -1536,7 +1535,7 @@ class Boss: return None cvs = self.current_visual_select - def chosen(ans: Union[None, int, str]) -> None: + def chosen(ans: None | int | str) -> None: q = self.current_visual_select self.current_visual_select = None if cvs and q is cvs: @@ -1560,7 +1559,7 @@ class Boss: if overlay_window is not None: overlay_window.allow_remote_control = True - def resize_layout_window(self, window: Window, increment: float, is_horizontal: bool, reset: bool = False) -> Union[bool, None, str]: + def resize_layout_window(self, window: Window, increment: float, is_horizontal: bool, reset: bool = False) -> bool | None | str: tab = window.tabref() if tab is None or not increment: return False @@ -1579,7 +1578,7 @@ class Boss: w, h = get_new_os_window_size(metrics, width, height, unit, incremental, has_window_scaling) set_os_window_size(os_window_id, w, h) - def tab_for_id(self, tab_id: int) -> Optional[Tab]: + def tab_for_id(self, tab_id: int) -> Tab | None: for tm in self.os_window_map.values(): tab = tm.tab_for_id(tab_id) if tab is not None: @@ -1602,7 +1601,7 @@ class Boss: def dispatch_action( self, key_action: KeyAction, - window_for_dispatch: Optional[Window] = None, + window_for_dispatch: Window | None = None, dispatch_type: str = 'KeyPress' ) -> bool: @@ -1656,7 +1655,7 @@ class Boss: map kitty_mod+e combine : new_window : next_layout map kitty_mod+e combine | new_tab | goto_tab -1 ''') - def combine(self, action_definition: str, window_for_dispatch: Optional[Window] = None, dispatch_type: str = 'KeyPress', raise_error: bool = False) -> bool: + def combine(self, action_definition: str, window_for_dispatch: Window | None = None, dispatch_type: str = 'KeyPress', raise_error: bool = False) -> bool: consumed = False if action_definition: try: @@ -1875,7 +1874,7 @@ class Boss: s.shutdown(socket.SHUT_RDWR) s.close() - def display_scrollback(self, window: Window, data: Union[bytes, str], input_line_number: int = 0, title: str = '', report_cursor: bool = True) -> None: + def display_scrollback(self, window: Window, data: bytes | str, input_line_number: int = 0, title: str = '', report_cursor: bool = True) -> None: def prepare_arg(x: str) -> str: x = x.replace('INPUT_LINE_NUMBER', str(input_line_number)) @@ -1920,11 +1919,11 @@ class Boss: self, kitten: str, args: Iterable[str] = (), - input_data: Optional[Union[bytes, str]] = None, - window: Optional[Window] = None, - custom_callback: Optional[Callable[[dict[str, Any], int, 'Boss'], None]] = None, - action_on_removal: Optional[Callable[[int, 'Boss'], None]] = None, - default_data: Optional[dict[str, Any]] = None + input_data: bytes | str | None = None, + window: Window | None = None, + custom_callback: Callable[[dict[str, Any], int, 'Boss'], None] | None = None, + action_on_removal: Callable[[int, 'Boss'], None] | None = None, + default_data: dict[str, Any] | None = None ) -> Any: orig_args, args = list(args), list(args) from kittens.runner import CLIOnlyKitten, KittenMetadata, create_kitten_handler @@ -1950,7 +1949,7 @@ class Boss: type_of_input = end_kitten.type_of_input q = type_of_input.split('-') if type_of_input else [] if not q: - data: Optional[bytes] = None + data: bytes | None = None elif q[0] in ('text', 'history', 'ansi', 'screen'): data = w.as_text(as_ansi='ansi' in q, add_history='history' in q, add_wrap_markers='screen' in q).encode('utf-8') elif type_of_input == 'selection': @@ -1990,7 +1989,7 @@ class Boss: env['PYTHONWARNINGS'] = 'ignore' remote_control_fd = -1 if end_kitten.allow_remote_control: - remote_control_passwords: Optional[dict[str, Sequence[str]]] = None + remote_control_passwords: dict[str, Sequence[str]] | None = None initial_data = b'' if end_kitten.remote_control_password: from secrets import token_hex @@ -2036,7 +2035,7 @@ class Boss: def on_kitten_finish( self, target_window_id: int, end_kitten: Callable[[dict[str, Any], int, 'Boss'], None], source_window: Window, - default_data: Optional[dict[str, Any]] = None + default_data: dict[str, Any] | None = None ) -> None: data, source_window.kitten_result = source_window.kitten_result, None if data is None: @@ -2065,7 +2064,7 @@ class Boss: map f3 set_tab_title " " ''' ) - def set_tab_title(self, title: Optional[str] = None) -> None: + def set_tab_title(self, title: str | None = None) -> None: tab = self.window_for_dispatch.tabref() if self.window_for_dispatch else self.active_tab if tab: if title is not None and title not in ('" "', "' '"): @@ -2080,7 +2079,7 @@ class Boss: _('Enter the new title for this tab below. An empty title will cause the default title to be used.'), tab.set_title, window=tab.active_window, initial_value=prefilled) - def create_special_window_for_show_error(self, title: str, msg: str, overlay_for: Optional[int] = None) -> SpecialWindowInstance: + def create_special_window_for_show_error(self, title: str, msg: str, overlay_for: int | None = None) -> SpecialWindowInstance: ec = sys.exc_info() tb = '' if ec != (None, None, None): @@ -2173,7 +2172,7 @@ class Boss: self.open_url(website_url()) @ac('misc', 'Open the specified URL') - def open_url(self, url: str, program: Optional[Union[str, list[str]]] = None, cwd: Optional[str] = None) -> None: + def open_url(self, url: str, program: str | list[str] | None = None, cwd: str | None = None) -> None: if not url: return if isinstance(program, str): @@ -2210,9 +2209,9 @@ class Boss: def open_url_with_hints(self) -> None: self.run_kitten_with_metadata('hints', window=self.window_for_dispatch) - def drain_actions(self, actions: list[KeyAction], window_for_dispatch: Optional[Window] = None, dispatch_type: str = 'KeyPress') -> None: + def drain_actions(self, actions: list[KeyAction], window_for_dispatch: Window | None = None, dispatch_type: str = 'KeyPress') -> None: - def callback(timer_id: Optional[int]) -> None: + def callback(timer_id: int | None) -> None: self.dispatch_action(actions.pop(0), window_for_dispatch, dispatch_type) if actions: self.drain_actions(actions) @@ -2266,7 +2265,7 @@ class Boss: if get_options().copy_on_select: self.copy_to_buffer(get_options().copy_on_select) - def get_active_selection(self) -> Optional[str]: + def get_active_selection(self) -> str | None: w = self.active_window if w is not None and not w.destroyed: return w.text_for_selection() @@ -2278,14 +2277,14 @@ class Boss: return w.has_selection() return False - def set_clipboard_buffer(self, buffer_name: str, text: Optional[str] = None) -> None: + def set_clipboard_buffer(self, buffer_name: str, text: str | None = None) -> None: if buffer_name: if text is not None: self.clipboard_buffers[buffer_name] = text elif buffer_name in self.clipboard_buffers: del self.clipboard_buffers[buffer_name] - def get_clipboard_buffer(self, buffer_name: str) -> Optional[str]: + def get_clipboard_buffer(self, buffer_name: str) -> str | None: return self.clipboard_buffers.get(buffer_name) @ac('cp', ''' @@ -2312,7 +2311,7 @@ class Boss: ''') def paste_from_buffer(self, buffer_name: str) -> None: if buffer_name == 'clipboard': - text: Optional[str] = get_clipboard_string() + text: str | None = get_clipboard_string() elif buffer_name == 'primary': text = get_primary_selection() else: @@ -2355,9 +2354,9 @@ class Boss: prev_tab = previous_tab def process_stdin_source( - self, window: Optional[Window] = None, - stdin: Optional[str] = None, copy_pipe_data: Optional[dict[str, Any]] = None - ) -> tuple[Optional[dict[str, str]], Optional[bytes]]: + self, window: Window | None = None, + stdin: str | None = None, copy_pipe_data: dict[str, Any] | None = None + ) -> tuple[dict[str, str] | None, bytes | None]: w = window or self.active_window if not w: return None, None @@ -2380,7 +2379,7 @@ class Boss: input_data = stdin.encode('utf-8') return env, input_data - def data_for_at(self, which: str, window: Optional[Window] = None, add_wrap_markers: bool = False) -> Optional[str]: + def data_for_at(self, which: str, window: Window | None = None, add_wrap_markers: bool = False) -> str | None: window = window or self.active_window if not window: return None @@ -2388,9 +2387,9 @@ class Boss: def special_window_for_cmd( self, cmd: list[str], - window: Optional[Window] = None, - stdin: Optional[str] = None, - cwd_from: Optional[CwdRequest] = None, + window: Window | None = None, + stdin: str | None = None, + cwd_from: CwdRequest | None = None, as_overlay: bool = False ) -> SpecialWindowInstance: w = window or self.active_window @@ -2406,7 +2405,7 @@ class Boss: overlay_for = w.id if w and as_overlay else None return SpecialWindow(cmd, input_data, cwd_from=cwd_from, overlay_for=overlay_for, env=env) - def add_fd_based_remote_control(self, remote_control_passwords: Optional[dict[str, Sequence[str]]] = None, initial_data: bytes = b'') -> socket.socket: + def add_fd_based_remote_control(self, remote_control_passwords: dict[str, Sequence[str]] | None = None, initial_data: bytes = b'') -> socket.socket: local, remote = socket.socketpair() os.set_inheritable(remote.fileno(), True) if initial_data: @@ -2425,14 +2424,14 @@ class Boss: def run_background_process( self, cmd: list[str], - cwd: Optional[str] = None, - env: Optional[dict[str, str]] = None, - stdin: Optional[bytes] = None, - cwd_from: Optional[CwdRequest] = None, + cwd: str | None = None, + env: dict[str, str] | None = None, + stdin: bytes | None = None, + cwd_from: CwdRequest | None = None, allow_remote_control: bool = False, - remote_control_passwords: Optional[dict[str, Sequence[str]]] = None, - notify_on_death: Optional[Callable[[int, Optional[Exception]], None]] = None, # guaranteed to be called only after event loop tick - stdout: Optional[int] = None, stderr: Optional[int] = None, + remote_control_passwords: dict[str, Sequence[str]] | None = None, + notify_on_death: Callable[[int, Exception | None], None] | None = None, # guaranteed to be called only after event loop tick + stdout: int | None = None, stderr: int | None = None, ) -> None: env = env or None if env: @@ -2465,7 +2464,7 @@ class Boss: if stderr is not None and stderr > -1 and stderr not in pass_fds: pass_fds.append(stderr) - def run(stdin: Optional[int], stdout: Optional[int], stderr: Optional[int]) -> None: + def run(stdin: int | None, stdout: int | None, stderr: int | None) -> None: try: p = subprocess.Popen( cmd, env=env, cwd=cwd, preexec_fn=clear_handled_signals, pass_fds=pass_fds, stdin=stdin, stdout=stdout, stderr=stderr) @@ -2477,7 +2476,7 @@ class Boss: with suppress(OSError): os.close(fd) if notify_on_death: - def callback(err: Exception, timer_id: Optional[int]) -> None: + def callback(err: Exception, timer_id: int | None) -> None: notify_on_death(-1, err) add_timer(partial(callback, err), 0, False) else: @@ -2504,7 +2503,7 @@ class Boss: else: doit() - def pipe(self, source: str, dest: str, exe: str, *args: str) -> Optional[Window]: + def pipe(self, source: str, dest: str, exe: str, *args: str) -> Window | None: cmd = [exe] + list(args) window = self.active_window cwd_from = CwdRequest(window) if window else None @@ -2535,7 +2534,7 @@ class Boss: self.run_background_process(cmd, cwd_from=cwd_from, stdin=stdin, env=env) return None - def args_to_special_window(self, args: Iterable[str], cwd_from: Optional[CwdRequest] = None) -> SpecialWindowInstance: + def args_to_special_window(self, args: Iterable[str], cwd_from: CwdRequest | None = None) -> SpecialWindowInstance: args = list(args) stdin = None w = self.active_window @@ -2556,7 +2555,7 @@ class Boss: cmd.append(arg) return SpecialWindow(cmd, stdin, cwd_from=cwd_from) - def _new_tab(self, args: Union[SpecialWindowInstance, Iterable[str]], cwd_from: Optional[CwdRequest] = None, as_neighbor: bool = False) -> Optional[Tab]: + def _new_tab(self, args: SpecialWindowInstance | Iterable[str], cwd_from: CwdRequest | None = None, as_neighbor: bool = False) -> Tab | None: special_window = None if args: if isinstance(args, SpecialWindowInstance): @@ -2573,7 +2572,7 @@ class Boss: return tm.new_tab(special_window=special_window, cwd_from=cwd_from, as_neighbor=as_neighbor) return None - def _create_tab(self, args: list[str], cwd_from: Optional[CwdRequest] = None) -> None: + def _create_tab(self, args: list[str], cwd_from: CwdRequest | None = None) -> None: as_neighbor = False if args and args[0].startswith('!'): as_neighbor = 'neighbor' in args[0][1:].split(',') @@ -2588,14 +2587,14 @@ class Boss: def new_tab_with_cwd(self, *args: str) -> None: self._create_tab(list(args), cwd_from=CwdRequest(self.window_for_dispatch or self.active_window_for_cwd)) - def new_tab_with_wd(self, wd: Union[str, list[str]], str_is_multiple_paths: bool = False) -> None: + def new_tab_with_wd(self, wd: str | list[str], str_is_multiple_paths: bool = False) -> None: if isinstance(wd, str): wd = wd.split(os.pathsep) if str_is_multiple_paths else [wd] for path in wd: special_window = SpecialWindow(None, cwd=path) self._new_tab(special_window) - def _new_window(self, args: list[str], cwd_from: Optional[CwdRequest] = None) -> Optional[Window]: + def _new_window(self, args: list[str], cwd_from: CwdRequest | None = None) -> Window | None: if not self.os_window_map: os_window_id = self.add_os_window() tm = self.os_window_map.get(os_window_id) @@ -2657,7 +2656,7 @@ class Boss: See :opt:`disable_ligatures` for details ''') - def disable_ligatures_in(self, where: Union[str, Iterable[Window]], strategy: int) -> None: + def disable_ligatures_in(self, where: str | Iterable[Window], strategy: int) -> None: w = self.window_for_dispatch or self.active_window if isinstance(where, str): windows: list[Window] = [] @@ -2830,9 +2829,9 @@ class Boss: def _move_window_to( self, - window: Optional[Window] = None, - target_tab_id: Optional[Union[str, int]] = None, - target_os_window_id: Optional[Union[str, int]] = None + window: Window | None = None, + target_tab_id: str | int | None = None, + target_os_window_id: str | int | None = None ) -> None: window = window or self.active_window if not window: @@ -2873,7 +2872,7 @@ class Boss: self._cleanup_tab_after_window_removal(src_tab) target_tab.make_active() - def _move_tab_to(self, tab: Optional[Tab] = None, target_os_window_id: Optional[int] = None) -> None: + def _move_tab_to(self, tab: Tab | None = None, target_os_window_id: int | None = None) -> None: tab = tab or self.active_tab if tab is None: return @@ -2886,14 +2885,14 @@ class Boss: target_tab.make_active() def choose_entry( - self, title: str, entries: Iterable[tuple[Union[_T, str, None], str]], - callback: Callable[[Union[_T, str, None]], None], + self, title: str, entries: Iterable[tuple[_T | str | None, str]], + callback: Callable[[_T | str | None], None], subtitle: str = '', - hints_args: Optional[tuple[str, ...]] = None, - ) -> Optional[Window]: + hints_args: tuple[str, ...] | None = None, + ) -> Window | None: lines = [title, subtitle, ' '] if subtitle else [title, ' '] - idx_map: list[Union[_T, str, None]] = [] - ans: Union[str, _T, None] = None + idx_map: list[_T | str | None] = [] + ans: str | _T | None = None fmt = ': {1}' for obj, text in entries: @@ -2922,7 +2921,7 @@ class Boss: @ac('tab', 'Interactively select a tab to switch to') def select_tab(self) -> None: - def chosen(ans: Union[None, str, int]) -> None: + def chosen(ans: None | str | int) -> None: if isinstance(ans, int): for tab in self.all_tabs: if tab.id == ans: @@ -2960,12 +2959,12 @@ class Boss: return self._move_window_to(target_tab_id=where) w = self.window_for_dispatch or self.active_window ct = w.tabref() if w else None - items: list[tuple[Union[str, int], str]] = [(t.id, t.effective_title) for t in self.all_tabs if t is not ct] + items: list[tuple[str | int, str]] = [(t.id, t.effective_title) for t in self.all_tabs if t is not ct] items.append(('new_tab', 'New tab')) items.append(('new_os_window', 'New OS Window')) target_window = w - def chosen(ans: Union[None, str, int]) -> None: + def chosen(ans: None | str | int) -> None: if ans is not None: if isinstance(ans, str): if ans == 'new_os_window': @@ -2986,7 +2985,7 @@ class Boss: if not args or args[0] == 'new': return self._move_tab_to() - items: list[tuple[Union[str, int], str]] = [] + items: list[tuple[str | int, str]] = [] ct = self.active_tab_manager_with_dispatch for osw_id, tm in self.os_window_map.items(): if tm is not ct and tm.active_tab: @@ -2995,14 +2994,14 @@ class Boss: w = self.window_for_dispatch or self.active_window target_tab = w.tabref() if w else None - def chosen(ans: Union[None, int, str]) -> None: + def chosen(ans: None | int | str) -> None: if ans is not None: os_window_id = None if isinstance(ans, str) else ans self._move_tab_to(tab=target_tab, target_os_window_id=os_window_id) self.choose_entry('Choose an OS window to move the tab to', items, chosen) - def set_background_image(self, path: Optional[str], os_windows: tuple[int, ...], configured: bool, layout: Optional[str], png_data: bytes = b'') -> None: + def set_background_image(self, path: str | None, os_windows: tuple[int, ...], configured: bool, layout: str | None, png_data: bytes = b'') -> None: set_background_image(path, os_windows, configured, layout, png_data) for os_window_id in os_windows: self.default_bg_changed_for(os_window_id) diff --git a/kitty/child.py b/kitty/child.py index 9f6eb7edf..b8a0bdb03 100644 --- a/kitty/child.py +++ b/kitty/child.py @@ -84,12 +84,12 @@ else: @run_once -def checked_terminfo_dir() -> Optional[str]: +def checked_terminfo_dir() -> str | None: return terminfo_dir if os.path.isdir(terminfo_dir) else None def processes_in_group(grp: int) -> list[int]: - gmap: Optional[DefaultDict[int, list[int]]] = getattr(process_group_map, 'cached_map', None) + gmap: DefaultDict[int, list[int]] | None = getattr(process_group_map, 'cached_map', None) if gmap is None: try: gmap = process_group_map() @@ -148,13 +148,13 @@ def process_env() -> dict[str, str]: def default_env() -> dict[str, str]: - ans: Optional[dict[str, str]] = getattr(default_env, 'env', None) + ans: dict[str, str] | None = getattr(default_env, 'env', None) if ans is None: return process_env() return ans -def set_default_env(val: Optional[dict[str, str]] = None) -> None: +def set_default_env(val: dict[str, str] | None = None) -> None: env = process_env().copy() has_lctype = False if val: @@ -187,9 +187,9 @@ def base64_terminfo_data() -> str: class ProcessDesc(TypedDict): - cwd: Optional[str] + cwd: str | None pid: int - cmdline: Optional[Sequence[str]] + cmdline: Sequence[str] | None child_counter = count() @@ -197,16 +197,16 @@ child_counter = count() class Child: - child_fd: Optional[int] = None - pid: Optional[int] = None + child_fd: int | None = None + pid: int | None = None forked = False def __init__( self, argv: Sequence[str], cwd: str, - stdin: Optional[bytes] = None, - env: Optional[dict[str, str]] = None, + stdin: bytes | None = None, + env: dict[str, str] | None = None, cwd_from: Optional['CwdRequest'] = None, is_clone_launch: str = '', add_listen_on_env_var: bool = True, @@ -279,7 +279,7 @@ class Child: env.pop('KITTY_IS_CLONE_LAUNCH', None) return env - def fork(self) -> Optional[int]: + def fork(self) -> int | None: if self.forked: return None opts = fast_data_types.get_options() @@ -428,13 +428,13 @@ class Child: return self.final_env.copy() @property - def current_cwd(self) -> Optional[str]: + def current_cwd(self) -> str | None: with suppress(Exception): assert self.pid is not None return cwd_of_process(self.pid) return None - def get_pid_for_cwd(self, oldest: bool = False) -> Optional[int]: + def get_pid_for_cwd(self, oldest: bool = False) -> int | None: with suppress(Exception): assert self.child_fd is not None pgrp = os.tcgetpgrp(self.child_fd) @@ -454,17 +454,17 @@ class Child: return self.pid @property - def pid_for_cwd(self) -> Optional[int]: + def pid_for_cwd(self) -> int | None: return self.get_pid_for_cwd() - def get_foreground_cwd(self, oldest: bool = False) -> Optional[str]: + def get_foreground_cwd(self, oldest: bool = False) -> str | None: with suppress(Exception): pid = self.get_pid_for_cwd(oldest) if pid is not None: return cwd_of_process(pid) or None return None - def get_foreground_exe(self, oldest: bool = False) -> Optional[str]: + def get_foreground_exe(self, oldest: bool = False) -> str | None: with suppress(Exception): pid = self.get_pid_for_cwd(oldest) if pid is not None: @@ -474,7 +474,7 @@ class Child: return None @property - def foreground_cwd(self) -> Optional[str]: + def foreground_cwd(self) -> str | None: return self.get_foreground_cwd() @property diff --git a/kitty/cli.py b/kitty/cli.py index b4027c4bd..0166b7ea9 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -5,11 +5,11 @@ import os import re import sys from collections import deque -from collections.abc import Iterator, Sequence +from collections.abc import Callable, Iterator, Sequence from dataclasses import dataclass from enum import Enum, auto from re import Match -from typing import Any, Callable, Optional, TypeVar, Union, cast +from typing import Any, TypeVar, Union, cast from .cli_stub import CLIOptions from .conf.utils import resolve_config @@ -105,7 +105,7 @@ class OptionDict(TypedDict): help: str choices: frozenset[str] type: str - default: Optional[str] + default: str | None condition: bool completion: CompletionSpec @@ -378,7 +378,7 @@ def disc(x: str) -> str: OptionSpecSeq = list[Union[str, OptionDict]] -def parse_option_spec(spec: Optional[str] = None) -> tuple[OptionSpecSeq, OptionSpecSeq]: +def parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpecSeq]: if spec is None: spec = options_spec() NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP' @@ -569,7 +569,7 @@ class PrintHelpForSeq: allow_pager = True - def __call__(self, seq: OptionSpecSeq, usage: Optional[str], message: Optional[str], appname: str) -> None: + def __call__(self, seq: OptionSpecSeq, usage: str | None, message: str | None, appname: str) -> None: from kitty.utils import screen_size_function screen_size = screen_size_function() try: @@ -579,7 +579,7 @@ class PrintHelpForSeq: blocks: list[str] = [] a = blocks.append - def wa(text: str, indent: int = 0, leading_indent: Optional[int] = None) -> None: + def wa(text: str, indent: int = 0, leading_indent: int | None = None) -> None: if leading_indent is None: leading_indent = indent j = '\n' + (' ' * indent) @@ -644,9 +644,9 @@ print_help_for_seq = PrintHelpForSeq() def seq_as_rst( seq: OptionSpecSeq, - usage: Optional[str], - message: Optional[str], - appname: Optional[str], + usage: str | None, + message: str | None, + appname: str | None, heading_char: str = '-' ) -> str: import textwrap @@ -742,7 +742,7 @@ def defval_for_opt(opt: OptionDict) -> Any: class Options: - def __init__(self, seq: OptionSpecSeq, usage: Optional[str], message: Optional[str], appname: Optional[str]): + def __init__(self, seq: OptionSpecSeq, usage: str | None, message: str | None, appname: str | None): self.alias_map = {} self.seq = seq self.names_map: dict[str, OptionDict] = {} @@ -803,7 +803,7 @@ class Options: self.values_map[name] = val -def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: Optional[list[str]] = None) -> list[str]: +def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str] | None = None) -> list[str]: NORMAL, EXPECTING_ARG = 'NORMAL', 'EXPECTING_ARG' state = NORMAL dargs = deque(sys.argv[1:] if args is None else args) @@ -1033,7 +1033,7 @@ def options_for_completion() -> OptionSpecSeq: def option_spec_as_rst( ospec: Callable[[], str] = options_spec, - usage: Optional[str] = None, message: Optional[str] = None, appname: Optional[str] = None, + usage: str | None = None, message: str | None = None, appname: str | None = None, heading_char: str = '-' ) -> str: options = parse_option_spec(ospec()) @@ -1046,12 +1046,12 @@ T = TypeVar('T') def parse_args( - args: Optional[list[str]] = None, + args: list[str] | None = None, ospec: Callable[[], str] = options_spec, - usage: Optional[str] = None, - message: Optional[str] = None, - appname: Optional[str] = None, - result_class: Optional[type[T]] = None, + usage: str | None = None, + message: str | None = None, + appname: str | None = None, + result_class: type[T] | None = None, ) -> tuple[T, list[str]]: options = parse_option_spec(ospec()) seq, disabled = options @@ -1080,7 +1080,7 @@ def parse_override(x: str) -> str: return override_pat().sub(r'\1 ', x.lstrip()) -def create_opts(args: CLIOptions, accumulate_bad_lines: Optional[list[BadLineType]] = None) -> KittyOpts: +def create_opts(args: CLIOptions, accumulate_bad_lines: list[BadLineType] | None = None) -> KittyOpts: from .config import load_config config = default_config_paths(args.config) overrides = map(parse_override, args.override or ()) diff --git a/kitty/clipboard.py b/kitty/clipboard.py index 19d7a8bc9..74f638a81 100644 --- a/kitty/clipboard.py +++ b/kitty/clipboard.py @@ -3,11 +3,11 @@ import io import os -from collections.abc import Mapping +from collections.abc import Callable, Mapping from enum import Enum, IntEnum from gettext import gettext as _ from tempfile import TemporaryFile -from typing import IO, Callable, NamedTuple, Optional, Union +from typing import IO, NamedTuple, Union from .conf.utils import uniq from .constants import supports_primary_selection @@ -28,7 +28,7 @@ from .utils import log_error class Tempfile: def __init__(self, max_size: int) -> None: - self.file: Union[io.BytesIO, IO[bytes]] = io.BytesIO() + self.file: io.BytesIO | IO[bytes] = io.BytesIO() self.max_size = max_size def rollover_if_needed(self, sz: int) -> None: @@ -88,7 +88,7 @@ class Clipboard: self.clipboard_type = clipboard_type self.enabled = self.clipboard_type is ClipboardType.clipboard or supports_primary_selection - def set_text(self, x: Union[str, bytes]) -> None: + def set_text(self, x: str | bytes) -> None: if isinstance(x, str): x = x.encode('utf-8') self.set_mime({'text/plain': x}) @@ -153,7 +153,7 @@ class Clipboard: return data() -def set_clipboard_string(x: Union[str, bytes]) -> None: +def set_clipboard_string(x: str | bytes) -> None: get_boss().clipboard.set_text(x) @@ -161,7 +161,7 @@ def get_clipboard_string() -> str: return get_boss().clipboard.get_text() -def set_primary_selection(x: Union[str, bytes]) -> None: +def set_primary_selection(x: str | bytes) -> None: get_boss().primary_selection.set_text(x) @@ -270,7 +270,7 @@ class WriteRequest: x = {mime: self.tempfile.create_chunker(pos.start, pos.size) for mime, pos in self.mime_map.items()} cp.set_mime(x) - def add_base64_data(self, data: Union[str, bytes], mime: str = 'text/plain') -> None: + def add_base64_data(self, data: str | bytes, mime: str = 'text/plain') -> None: if isinstance(data, str): data = data.encode('ascii') if self.currently_writing_mime and self.currently_writing_mime != mime: @@ -314,8 +314,8 @@ class ClipboardRequestManager: def __init__(self, window_id: int) -> None: self.window_id = window_id - self.currently_asking_permission_for: Optional[ReadRequest] = None - self.in_flight_write_request: Optional[WriteRequest] = None + self.currently_asking_permission_for: ReadRequest | None = None + self.in_flight_write_request: WriteRequest | None = None def parse_osc_5522(self, data: memoryview) -> None: import base64 diff --git a/kitty/colors.py b/kitty/colors.py index 406a8e3ca..ec0e8a0de 100644 --- a/kitty/colors.py +++ b/kitty/colors.py @@ -2,9 +2,10 @@ # License: GPLv3 Copyright: 2024, Kovid Goyal import os +from collections.abc import Iterable, Sequence from contextlib import suppress from enum import Enum -from typing import Iterable, Literal, Optional, Sequence, Union +from typing import Literal, Optional from .config import parse_config from .constants import config_dir @@ -31,7 +32,7 @@ class ThemeColors: light_mtime: int = -1 no_preference_mtime: int = -1 applied_theme: Literal['light', 'dark', 'no_preference', ''] = '' - default_colors: Optional[ColorsSpec] = None + default_colors: ColorsSpec | None = None def get_default_colors(self) -> ColorsSpec: if self.default_colors is None: @@ -101,7 +102,7 @@ class ThemeColors: which = glfw_get_system_color_theme() if debug_rendering: log_error('Current system color scheme:', which) - cols: Optional[Colors] = None + cols: Colors | None = None if which == 'dark' and self.has_dark_theme: cols = self.dark_spec, self.dark_tbc elif which == 'light' and self.has_light_theme: @@ -148,9 +149,9 @@ class ThemeColors: theme_colors = ThemeColors() -def parse_colors(args: Iterable[Union[str, Iterable[str]]]) -> Colors: - colors: dict[str, Optional[Color]] = {} - nullable_color_map: dict[str, Optional[int]] = {} +def parse_colors(args: Iterable[str | Iterable[str]]) -> Colors: + colors: dict[str, Color | None] = {} + nullable_color_map: dict[str, int | None] = {} transparent_background_colors = () for spec in args: if isinstance(spec, str): @@ -168,7 +169,7 @@ def parse_colors(args: Iterable[Union[str, Iterable[str]]]) -> Colors: if q is not False: val = int(q) if isinstance(q, Color) else None nullable_color_map[k] = val - ans: dict[str, Optional[int]] = {k: int(v) for k, v in colors.items() if isinstance(v, Color)} + ans: dict[str, int | None] = {k: int(v) for k, v in colors.items() if isinstance(v, Color)} ans.update(nullable_color_map) return ans, transparent_background_colors @@ -186,7 +187,7 @@ def patch_options_with_color_spec(opts: Options, spec: ColorsSpec, transparent_b def patch_colors( spec: ColorsSpec, transparent_background_colors: TransparentBackgroundColors, configured: bool = False, - windows: Optional[Sequence[WindowType]] = None, notify_on_bg_change: bool = True, + windows: Sequence[WindowType] | None = None, notify_on_bg_change: bool = True, ) -> None: boss = get_boss() if windows is None: diff --git a/kitty/conf/generate.py b/kitty/conf/generate.py index 53e95f2ed..1276ee253 100644 --- a/kitty/conf/generate.py +++ b/kitty/conf/generate.py @@ -6,13 +6,14 @@ import inspect import os import re import textwrap -from typing import Any, Callable, Dict, Iterator, List, Set, Tuple, Union, get_type_hints +from collections.abc import Callable, Iterator +from typing import Any, get_type_hints from kitty.conf.types import Definition, MultiOption, Option, ParserFuncType, unset from kitty.types import _T -def chunks(lst: List[_T], n: int) -> Iterator[List[_T]]: +def chunks(lst: list[_T], n: int) -> Iterator[list[_T]]: for i in range(0, len(lst), n): yield lst[i:i + n] @@ -21,20 +22,20 @@ def atoi(text: str) -> str: return f'{int(text):08d}' if text.isdigit() else text -def natural_keys(text: str) -> Tuple[str, ...]: +def natural_keys(text: str) -> tuple[str, ...]: return tuple(atoi(c) for c in re.split(r'(\d+)', text)) -def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: - class_lines: List[str] = [] - tc_lines: List[str] = [] +def generate_class(defn: Definition, loc: str) -> tuple[str, str]: + class_lines: list[str] = [] + tc_lines: list[str] = [] a = class_lines.append t = tc_lines.append a('class Options:') t('class Parser:') choices = {} - imports: Set[Tuple[str, str]] = set() - tc_imports: Set[Tuple[str, str]] = set() + imports: set[tuple[str, str]] = set() + tc_imports: set[tuple[str, str]] = set() ki_imports: 're.Pattern[str]' = re.compile(r'\b((?:kittens|kitty).+?)[,\]]') def option_type_as_str(x: Any) -> str: @@ -49,7 +50,7 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: imports.add((x.__module__, x.__name__)) return ans - def option_type_data(option: Union[Option, MultiOption]) -> Tuple[Callable[[Any], Any], str]: + def option_type_data(option: Option | MultiOption) -> tuple[Callable[[Any], Any], str]: func = option.parser_func if func.__module__ == 'builtins': return func, func.__name__ @@ -69,8 +70,8 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: is_mutiple_vars = {} option_names = set() color_table = list(map(str, range(256))) - choice_dedup: Dict[str, str] = {} - choice_parser_dedup: Dict[str, str] = {} + choice_dedup: dict[str, str] = {} + choice_parser_dedup: dict[str, str] = {} def parser_function_declaration(option_name: str) -> None: t('') @@ -80,7 +81,7 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: option_names.add(option.name) parser_function_declaration(option.name) if isinstance(option, MultiOption): - mval: Dict[str, Dict[str, Any]] = {'macos': {}, 'linux': {}, '': {}} + mval: dict[str, dict[str, Any]] = {'macos': {}, 'linux': {}, '': {}} func, typ = option_type_data(option) for val in option: if val.add_to_default: @@ -293,7 +294,7 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: a('') for aname, func in action_parsers.items(): a(f'defaults.{aname} = [') - only: Dict[str, List[Tuple[str, Callable[..., Any]]]] = {} + only: dict[str, list[tuple[str, Callable[..., Any]]]] = {} for sc in defn.iter_all_maps(aname): if not sc.add_to_default: continue @@ -358,11 +359,11 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: preamble = ['# generated by gen-config.py DO NOT edit', ''] a = preamble.append - def output_imports(imports: Set[Tuple[str, str]], add_module_imports: bool = True) -> None: + def output_imports(imports: set[tuple[str, str]], add_module_imports: bool = True) -> None: a('# isort: skip_file') a('import typing') seen_mods = {'typing'} - mmap: Dict[str, List[str]] = {} + mmap: dict[str, list[str]] = {} for mod, name in imports: mmap.setdefault(mod, []).append(name) for mod in sorted(mmap): @@ -401,8 +402,8 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]: return class_def, '\n'.join(preamble + ['', ''] + tc_lines) -def generate_c_conversion(loc: str, ctypes: List[Union[Option, MultiOption]]) -> str: - lines: List[str] = [] +def generate_c_conversion(loc: str, ctypes: list[Option | MultiOption]) -> str: + lines: list[str] = [] basic_converters = { 'int': 'PyLong_AsLong', 'uint': 'PyLong_AsUnsignedLong', 'bool': 'PyObject_IsTrue', 'float': 'PyFloat_AsFloat', 'double': 'PyFloat_AsDouble', 'percent': 'percent', @@ -462,7 +463,7 @@ def write_output(loc: str, defn: Definition, extra_after_type_defn: str = '') -> f.write(f'{c}\n') -def go_type_data(parser_func: ParserFuncType, ctype: str, is_multiple: bool = False) -> Tuple[str, str]: +def go_type_data(parser_func: ParserFuncType, ctype: str, is_multiple: bool = False) -> tuple[str, str]: if ctype: if ctype == 'string': if is_multiple: diff --git a/kitty/conf/types.py b/kitty/conf/types.py index ee7de683c..bbc18177c 100644 --- a/kitty/conf/types.py +++ b/kitty/conf/types.py @@ -5,9 +5,11 @@ import builtins import re import textwrap import typing +from collections.abc import Callable, Iterable, Iterator from functools import lru_cache from importlib import import_module -from typing import Any, Callable, Dict, Iterable, Iterator, List, Match, Optional, Set, Tuple, Union, cast +from re import Match +from typing import Any, Optional, Union, cast import kitty.conf.utils as generic_parsers from kitty.constants import website_url @@ -42,11 +44,11 @@ def expand_opt_references(conf_name: str, text: str) -> str: @run_once -def ref_map() -> Dict[str, Dict[str, str]]: +def ref_map() -> dict[str, dict[str, str]]: import json from ..fast_data_types import get_docs_ref_map - ans: Dict[str, Dict[str, str]] = json.loads(get_docs_ref_map()) + ans: dict[str, dict[str, str]] = json.loads(get_docs_ref_map()) return ans @@ -87,7 +89,7 @@ def remove_markup(text: str) -> str: imap = {'iss': 'issues-', 'pull': 'pull-', 'disc': 'discussions-'} - def extract(m: 'Match[str]') -> Tuple[str, str]: + def extract(m: 'Match[str]') -> tuple[str, str]: parts = m.group(2).split('<') t = parts[0].strip() q = parts[-1].rstrip('>') @@ -123,8 +125,8 @@ def strip_inline_literal(text: str) -> str: return re.sub(r'``([^`]+)``', r'`\1`', text, flags=re.DOTALL) -def iter_blocks(lines: Iterable[str]) -> Iterator[Tuple[List[str], int]]: - current_block: List[str] = [] +def iter_blocks(lines: Iterable[str]) -> Iterator[tuple[list[str], int]]: + current_block: list[str] = [] prev_indent = 0 for line in lines: indent_size = len(line) - len(line.lstrip()) @@ -171,9 +173,9 @@ def render_block(text: str, comment_symbol: str = '#: ') -> str: class CoalescedIteratorData: - option_groups: Dict[int, List['Option']] = {} - action_groups: Dict[str, List['Mapping']] = {} - coalesced: Set[int] = set() + option_groups: dict[int, list['Option']] = {} + action_groups: dict[str, list['Mapping']] = {} + coalesced: set[int] = set() initialized: bool = False kitty_mod: str = 'kitty_mod' @@ -182,8 +184,8 @@ class CoalescedIteratorData: return self.root = root option_groups = self.option_groups = {} - current_group: List[Option] = [] - action_groups: Dict[str, List[Mapping]] = {} + current_group: list[Option] = [] + action_groups: dict[str, list[Mapping]] = {} self.action_groups = action_groups coalesced = self.coalesced = set() self.kitty_mod = 'kitty_mod' @@ -209,18 +211,18 @@ class CoalescedIteratorData: if current_group: option_groups[id(current_group[0])] = current_group[1:] - def option_group_for_option(self, opt: 'Option') -> List['Option']: + def option_group_for_option(self, opt: 'Option') -> list['Option']: return self.option_groups.get(id(opt), []) - def action_group_for_action(self, ac: 'Mapping') -> List['Mapping']: + def action_group_for_action(self, ac: 'Mapping') -> list['Mapping']: return self.action_groups.get(ac.name, []) class Option: def __init__( - self, name: str, defval: str, macos_default: Union[Unset, str], parser_func: ParserFuncType, - long_text: str, documented: bool, group: 'Group', choices: Tuple[str, ...], ctype: str, + self, name: str, defval: str, macos_default: Unset | str, parser_func: ParserFuncType, + long_text: str, documented: bool, group: 'Group', choices: tuple[str, ...], ctype: str, has_secret: bool = False, ): self.name = name @@ -242,8 +244,8 @@ class Option: def is_color_table_color(self) -> bool: return self.name.startswith('color') and self.name[5:].isdigit() - def as_conf(self, commented: bool = False, level: int = 0, option_group: List['Option'] = []) -> List[str]: - ans: List[str] = [] + def as_conf(self, commented: bool = False, level: int = 0, option_group: list['Option'] = []) -> list[str]: + ans: list[str] = [] a = ans.append if not self.documented: return ans @@ -261,10 +263,10 @@ class Option: return ans def as_rst( - self, conf_name: str, shortcut_slugs: Dict[str, Tuple[str, str]], - kitty_mod: str, level: int = 0, option_group: List['Option'] = [] - ) -> List[str]: - ans: List[str] = [] + self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]], + kitty_mod: str, level: int = 0, option_group: list['Option'] = [] + ) -> list[str]: + ans: list[str] = [] a = ans.append if not self.documented: return ans @@ -301,7 +303,7 @@ class MultiOption: self.long_text = long_text self.group = group self.has_secret = has_secret - self.items: List[MultiVal] = [] + self.items: list[MultiVal] = [] def add_value(self, val_as_str: str, add_to_default: bool, documented: bool, only: Only) -> None: self.items.append(MultiVal(val_as_str, add_to_default, documented, only)) @@ -309,8 +311,8 @@ class MultiOption: def __iter__(self) -> Iterator[MultiVal]: yield from self.items - def as_conf(self, commented: bool = False, level: int = 0) -> List[str]: - ans: List[str] = [] + def as_conf(self, commented: bool = False, level: int = 0) -> list[str]: + ans: list[str] = [] a = ans.append documented = False for k in self.items: @@ -330,8 +332,8 @@ class MultiOption: a('') return ans - def as_rst(self, conf_name: str, shortcut_slugs: Dict[str, Tuple[str, str]], kitty_mod: str, level: int = 0) -> List[str]: - ans: List[str] = [] + def as_rst(self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]], kitty_mod: str, level: int = 0) -> list[str]: + ans: list[str] = [] a = ans.append a(f'.. opt:: {conf_name}.{self.name}') documented = tuple(x for x in self.items if x.documented) @@ -374,8 +376,8 @@ class Mapping: def key_text(self) -> str: return '' - def as_conf(self, commented: bool = False, level: int = 0, action_group: List['Mapping'] = []) -> List[str]: - ans: List[str] = [] + def as_conf(self, commented: bool = False, level: int = 0, action_group: list['Mapping'] = []) -> list[str]: + ans: list[str] = [] if not self.documented: return ans a = ans.append @@ -391,10 +393,10 @@ class Mapping: return ans def as_rst( - self, conf_name: str, shortcut_slugs: Dict[str, Tuple[str, str]], - kitty_mod: str, level: int = 0, action_group: List['Mapping'] = [] - ) -> List[str]: - ans: List[str] = [] + self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]], + kitty_mod: str, level: int = 0, action_group: list['Mapping'] = [] + ) -> list[str]: + ans: list[str] = [] a = ans.append if not self.documented: return ans @@ -490,7 +492,7 @@ class Group: self.title = title self.start_text = start_text self.end_text = '' - self.items: List[GroupItem] = [] + self.items: list[GroupItem] = [] self.parent = parent def append(self, item: GroupItem) -> None: @@ -520,8 +522,8 @@ class Group: else: yield x - def as_rst(self, conf_name: str, shortcut_slugs: Dict[str, Tuple[str, str]], kitty_mod: str = 'kitty_mod', level: int = 0) -> List[str]: - ans: List[str] = [] + def as_rst(self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]], kitty_mod: str = 'kitty_mod', level: int = 0) -> list[str]: + ans: list[str] = [] a = ans.append if level: a('') @@ -553,8 +555,8 @@ class Group: a(self.end_text) return ans - def as_conf(self, commented: bool = False, level: int = 0) -> List[str]: - ans: List[str] = [] + def as_conf(self, commented: bool = False, level: int = 0) -> list[str]: + ans: list[str] = [] a = ans.append if level: a('#: ' + self.title + ' {{''{') @@ -582,8 +584,8 @@ class Group: a('') else: map_groups = [] - start: Optional[int] = None - count: Optional[int] = None + start: int | None = None + count: int | None = None for i, line in enumerate(ans): if line.startswith('map ') or line.startswith('mouse_map '): if start is None: @@ -632,7 +634,7 @@ def resolve_import(name: str, module: Any = None) -> ParserFuncType: class Action: - def __init__(self, name: str, option_type: str, fields: Dict[str, str], imports: Iterable[str]): + def __init__(self, name: str, option_type: str, fields: dict[str, str], imports: Iterable[str]): self.name = name self._parser_func = option_type self.fields = fields @@ -654,22 +656,22 @@ class Definition: self.coalesced_iterator_data = CoalescedIteratorData() self.root_group = Group('', '', self.coalesced_iterator_data) self.current_group = self.root_group - self.option_map: Dict[str, Option] = {} - self.multi_option_map: Dict[str, MultiOption] = {} - self.shortcut_map: Dict[str, List[ShortcutMapping]] = {} - self.mouse_map: Dict[str, List[MouseMapping]] = {} + self.option_map: dict[str, Option] = {} + self.multi_option_map: dict[str, MultiOption] = {} + self.shortcut_map: dict[str, list[ShortcutMapping]] = {} + self.mouse_map: dict[str, list[MouseMapping]] = {} self.actions = {a.name: a.resolve_imports(self.module_for_parsers) for a in actions} - self.deprecations: Dict[ParserFuncType, Tuple[str, ...]] = {} + self.deprecations: dict[ParserFuncType, tuple[str, ...]] = {} def iter_all_non_groups(self) -> Iterator[NonGroups]: yield from self.root_group.iter_all_non_groups() - def iter_all_options(self) -> Iterator[Union[Option, MultiOption]]: + def iter_all_options(self) -> Iterator[Option | MultiOption]: for x in self.iter_all_non_groups(): if isinstance(x, (Option, MultiOption)): yield x - def iter_all_maps(self, which: str = 'map') -> Iterator[Union[ShortcutMapping, MouseMapping]]: + def iter_all_maps(self, which: str = 'map') -> Iterator[ShortcutMapping | MouseMapping]: for x in self.iter_all_non_groups(): if isinstance(x, ShortcutMapping) and which in ('map', '*'): yield x @@ -699,11 +701,11 @@ class Definition: self.current_group = self.current_group.parent def add_option( - self, name: str, defval: Union[str, float, int, bool], + self, name: str, defval: str | float | int | bool, option_type: str = 'str', long_text: str = '', documented: bool = True, add_to_default: bool = False, - only: Only = '', macos_default: Union[Unset, str] = unset, - choices: Tuple[str, ...] = (), + only: Only = '', macos_default: Unset | str = unset, + choices: tuple[str, ...] = (), ctype: str = '', has_secret: bool = False, ) -> None: if isinstance(defval, bool): @@ -747,10 +749,10 @@ class Definition: def add_deprecation(self, parser_name: str, *aliases: str) -> None: self.deprecations[self.parser_func(parser_name)] = aliases - def as_conf(self, commented: bool = False) -> List[str]: + def as_conf(self, commented: bool = False) -> list[str]: self.coalesced_iterator_data.initialize(self.root_group) return self.root_group.as_conf(commented) - def as_rst(self, conf_name: str, shortcut_slugs: Dict[str, Tuple[str, str]]) -> List[str]: + def as_rst(self, conf_name: str, shortcut_slugs: dict[str, tuple[str, str]]) -> list[str]: self.coalesced_iterator_data.initialize(self.root_group) return self.root_group.as_rst(conf_name, shortcut_slugs) diff --git a/kitty/conf/utils.py b/kitty/conf/utils.py index 773b53790..ec6ed2157 100644 --- a/kitty/conf/utils.py +++ b/kitty/conf/utils.py @@ -4,24 +4,14 @@ import os import re import sys +from collections.abc import Callable, Generator, Iterable, Iterator, Sequence from contextlib import contextmanager from typing import ( Any, - Callable, - Dict, - Generator, Generic, - Iterable, - Iterator, - List, Literal, NamedTuple, - Optional, - Sequence, - Set, - Tuple, TypeVar, - Union, ) from ..constants import _plat, is_macos @@ -32,13 +22,13 @@ from ..typing import Protocol from ..utils import expandvars, log_error, shlex_split key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$') -ItemParser = Callable[[str, str, Dict[str, Any]], bool] +ItemParser = Callable[[str, str, dict[str, Any]], bool] T = TypeVar('T') class OptionsProtocol(Protocol): - def _asdict(self) -> Dict[str, Any]: + def _asdict(self) -> dict[str, Any]: pass @@ -68,7 +58,7 @@ def to_color(x: str) -> Color: return ans -def to_color_or_none(x: str) -> Optional[Color]: +def to_color_or_none(x: str) -> Color | None: return None if x.lower() == 'none' else to_color(x) @@ -83,7 +73,7 @@ def to_bool(x: str) -> bool: class ToCmdline: def __init__(self) -> None: - self.override_env: Optional[Dict[str, str]] = None + self.override_env: dict[str, str] | None = None def __enter__(self) -> 'ToCmdline': return self @@ -97,7 +87,7 @@ class ToCmdline: self.override_env.update(override) return self - def __call__(self, x: str, expand: bool = True) -> List[str]: + def __call__(self, x: str, expand: bool = True) -> list[str]: if expand: ans = list( map( @@ -117,7 +107,7 @@ class ToCmdline: to_cmdline_implementation = ToCmdline() -def to_cmdline(x: str, expand: bool = True) -> List[str]: +def to_cmdline(x: str, expand: bool = True) -> list[str]: return to_cmdline_implementation(x, expand) @@ -237,7 +227,7 @@ class RecursiveInclude(Exception): class Memory: - def __init__(self, accumulate_bad_lines: Optional[List[BadLine]]) -> None: + def __init__(self, accumulate_bad_lines: list[BadLine] | None) -> None: self.s: set[str] = set() if accumulate_bad_lines is None: accumulate_bad_lines = [] @@ -256,11 +246,11 @@ class Memory: def parse_line( line: str, parse_conf_item: ItemParser, - ans: Dict[str, Any], + ans: dict[str, Any], base_path_for_includes: str, effective_config_lines: Callable[[str, str], None], memory: Memory, - accumulate_bad_lines: Optional[List[BadLine]] = None, + accumulate_bad_lines: list[BadLine] | None = None, ) -> None: line = line.strip() if not line or line.startswith('#'): @@ -329,10 +319,10 @@ def parse_line( def _parse( lines: Iterable[str], parse_conf_item: ItemParser, - ans: Dict[str, Any], + ans: dict[str, Any], memory: Memory, - accumulate_bad_lines: Optional[List[BadLine]] = None, - effective_config_lines: Optional[Callable[[str, str], None]] = None, + accumulate_bad_lines: list[BadLine] | None = None, + effective_config_lines: Callable[[str, str], None] | None = None, ) -> None: name = getattr(lines, 'name', None) effective_config_lines = effective_config_lines or (lambda a, b: None) @@ -384,14 +374,14 @@ def _parse( def parse_config_base( lines: Iterable[str], parse_conf_item: ItemParser, - ans: Dict[str, Any], - accumulate_bad_lines: Optional[List[BadLine]] = None, - effective_config_lines: Optional[Callable[[str, str], None]] = None, + ans: dict[str, Any], + accumulate_bad_lines: list[BadLine] | None = None, + effective_config_lines: Callable[[str, str], None] | None = None, ) -> None: _parse(lines, parse_conf_item, ans, Memory(accumulate_bad_lines), accumulate_bad_lines, effective_config_lines) -def merge_dicts(defaults: Dict[str, Any], newvals: Dict[str, Any]) -> Dict[str, Any]: +def merge_dicts(defaults: dict[str, Any], newvals: dict[str, Any]) -> dict[str, Any]: ans = defaults.copy() ans.update(newvals) return ans @@ -409,12 +399,12 @@ def resolve_config(SYSTEM_CONF: str, defconf: str, config_files_on_cmd_line: Seq def load_config( defaults: OptionsProtocol, - parse_config: Callable[[Iterable[str]], Dict[str, Any]], - merge_configs: Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]], + parse_config: Callable[[Iterable[str]], dict[str, Any]], + merge_configs: Callable[[dict[str, Any], dict[str, Any]], dict[str, Any]], *paths: str, - overrides: Optional[Iterable[str]] = None, - initialize_defaults: Callable[[Dict[str, Any]], Dict[str, Any]] = lambda x: x, -) -> Tuple[Dict[str, Any], Tuple[str, ...]]: + overrides: Iterable[str] | None = None, + initialize_defaults: Callable[[dict[str, Any]], dict[str, Any]] = lambda x: x, +) -> tuple[dict[str, Any], tuple[str, ...]]: ans = initialize_defaults(defaults._asdict()) found_paths = [] for path in paths: @@ -446,7 +436,7 @@ KeyFunc = Callable[[str, str], ReturnType] class KeyFuncWrapper(Generic[ReturnType]): def __init__(self) -> None: - self.args_funcs: Dict[str, KeyFunc[ReturnType]] = {} + self.args_funcs: dict[str, KeyFunc[ReturnType]] = {} def __call__(self, *names: str) -> Callable[[KeyFunc[ReturnType]], KeyFunc[ReturnType]]: @@ -457,13 +447,13 @@ class KeyFuncWrapper(Generic[ReturnType]): return f return w - def get(self, name: str) -> Optional[KeyFunc[ReturnType]]: + def get(self, name: str) -> KeyFunc[ReturnType] | None: return self.args_funcs.get(name) class KeyAction(NamedTuple): func: str - args: Tuple[Union[str, float, bool, int, None], ...] = () + args: tuple[str | float | bool | int | None, ...] = () def __repr__(self) -> str: if self.args: @@ -477,7 +467,7 @@ class KeyAction(NamedTuple): return ans -def parse_kittens_func_args(action: str, args_funcs: Dict[str, KeyFunc[Tuple[str, Any]]]) -> KeyAction: +def parse_kittens_func_args(action: str, args_funcs: dict[str, KeyFunc[tuple[str, Any]]]) -> KeyAction: parts = action.strip().split(' ', 1) func = parts[0] if len(parts) == 1: @@ -502,13 +492,13 @@ def parse_kittens_func_args(action: str, args_funcs: Dict[str, KeyFunc[Tuple[str return KeyAction(func, tuple(args)) -KittensKeyDefinition = Tuple[ParsedShortcut, KeyAction] -KittensKeyMap = Dict[ParsedShortcut, KeyAction] +KittensKeyDefinition = tuple[ParsedShortcut, KeyAction] +KittensKeyMap = dict[ParsedShortcut, KeyAction] def parse_kittens_key( - val: str, funcs_with_args: Dict[str, KeyFunc[Tuple[str, Any]]] -) -> Optional[KittensKeyDefinition]: + val: str, funcs_with_args: dict[str, KeyFunc[tuple[str, Any]]] +) -> KittensKeyDefinition | None: from ..key_encoding import parse_shortcut sc, action = val.partition(' ')[::2] if not sc or not action: @@ -517,8 +507,8 @@ def parse_kittens_key( return parse_shortcut(sc), ans -def uniq(vals: Iterable[T]) -> List[T]: - seen: Set[T] = set() +def uniq(vals: Iterable[T]) -> list[T]: + seen: set[T] = set() seen_add = seen.add return [x for x in vals if x not in seen and not seen_add(x)] diff --git a/kitty/config.py b/kitty/config.py index 153ffcaa8..2bdc12144 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -3,10 +3,10 @@ import json import os -from collections.abc import Generator, Iterable +from collections.abc import Callable, Generator, Iterable from contextlib import contextmanager, suppress from functools import partial -from typing import Any, Callable, Optional +from typing import Any from .conf.utils import BadLine, parse_config_base from .conf.utils import load_config as _load_config @@ -78,7 +78,7 @@ def prepare_config_file_for_editing() -> str: return defconf -def finalize_keys(opts: Options, accumulate_bad_lines: Optional[list[BadLine]] = None) -> None: +def finalize_keys(opts: Options, accumulate_bad_lines: list[BadLine] | None = None) -> None: defns: list[KeyDefinition] = [] for d in opts.map: if d is None: # clear_all_shortcuts @@ -117,7 +117,7 @@ def finalize_keys(opts: Options, accumulate_bad_lines: Optional[list[BadLine]] = opts.keyboard_modes = modes -def finalize_mouse_mappings(opts: Options, accumulate_bad_lines: Optional[list[BadLine]] = None) -> None: +def finalize_mouse_mappings(opts: Options, accumulate_bad_lines: list[BadLine] | None = None) -> None: defns: list[MouseMapping] = [] for d in opts.mouse_map: if d is None: # clear_all_mouse_actions @@ -141,7 +141,7 @@ def finalize_mouse_mappings(opts: Options, accumulate_bad_lines: Optional[list[B def parse_config( - lines: Iterable[str], accumulate_bad_lines: Optional[list[BadLine]] = None, effective_config_lines: Optional[Callable[[str, str], None]] = None + lines: Iterable[str], accumulate_bad_lines: list[BadLine] | None = None, effective_config_lines: Callable[[str, str], None] | None = None ) -> dict[str, Any]: from .options.parse import create_result_dict, parse_conf_item ans: dict[str, Any] = create_result_dict() @@ -158,7 +158,7 @@ def parse_config( effective_config_lines: list[str] = [] -def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, accumulate_bad_lines: Optional[list[BadLine]] = None) -> Options: +def load_config(*paths: str, overrides: Iterable[str] | None = None, accumulate_bad_lines: list[BadLine] | None = None) -> Options: from .options.parse import merge_result_dicts from .options.types import secret_options del effective_config_lines[:] @@ -212,7 +212,7 @@ class KittyCommonOpts(TypedDict): url_prefixes: tuple[str, ...] -def common_opts_as_dict(opts: Optional[Options] = None) -> KittyCommonOpts: +def common_opts_as_dict(opts: Options | None = None) -> KittyCommonOpts: if opts is None: opts = defaults return { diff --git a/kitty/constants.py b/kitty/constants.py index 76d7ccce8..ef92f537d 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -247,7 +247,7 @@ def is_wayland(opts: Optional['Options'] = None) -> bool: supports_primary_selection = not is_macos -def running_in_kitty(set_val: Optional[bool] = None) -> bool: +def running_in_kitty(set_val: bool | None = None) -> bool: if set_val is not None: setattr(running_in_kitty, 'ans', set_val) return bool(getattr(running_in_kitty, 'ans', False)) diff --git a/kitty/debug_config.py b/kitty/debug_config.py index bfa379fa5..24668c26e 100644 --- a/kitty/debug_config.py +++ b/kitty/debug_config.py @@ -7,11 +7,11 @@ import socket import sys import termios import time -from collections.abc import Iterator, Sequence +from collections.abc import Callable, Iterator, Sequence from contextlib import suppress from functools import partial from pprint import pformat -from typing import IO, Callable, Optional, TypeVar +from typing import IO, TypeVar from kittens.tui.operations import colored, styled @@ -189,7 +189,7 @@ class IssueData: return char def parse_issue_file(self, issue_file: IO[str]) -> Iterator[str]: - last_char: Optional[str] = None + last_char: str | None = None while True: this_char = issue_file.read(1) if not this_char: diff --git a/kitty/file_transmission.py b/kitty/file_transmission.py index 4a2399276..499103c79 100644 --- a/kitty/file_transmission.py +++ b/kitty/file_transmission.py @@ -11,7 +11,7 @@ import stat import tempfile from base64 import b85decode from collections import defaultdict, deque -from collections.abc import Iterable, Iterator +from collections.abc import Callable, Iterable, Iterator from contextlib import suppress from dataclasses import Field, dataclass, field, fields from enum import Enum, auto @@ -19,7 +19,7 @@ from functools import partial from gettext import gettext as _ from itertools import count from time import time_ns -from typing import IO, Any, Callable, DefaultDict, Deque, Optional, Union +from typing import IO, Any, DefaultDict, Deque, Union from kittens.transfer.utils import IdentityCompressor, ZlibCompressor, abspath, expand_home, home_path from kitty.fast_data_types import ESC_OSC, FILE_TRANSFER_CODE, AES256GCMDecrypt, add_timer, base64_decode, base64_encode, get_boss, get_options, monotonic @@ -42,7 +42,7 @@ def safe_string(x: str) -> str: return safe_string_pat().sub('', x) -def as_unicode(x: Union[str, bytes]) -> str: +def as_unicode(x: str | bytes) -> str: if isinstance(x, bytes): x = x.decode('ascii') return x @@ -55,7 +55,7 @@ def encode_bypass(request_id: str, bypass: str) -> str: def split_for_transfer( - data: Union[bytes, bytearray, memoryview], + data: bytes | bytearray | memoryview, session_id: str = '', file_id: str = '', mark_last: bool = False, chunk_size: int = 4096 @@ -77,7 +77,7 @@ def iter_file_metadata(file_specs: Iterable[tuple[str, str]]) -> Iterator[Union[ def skey(sr: os.stat_result) -> tuple[int, int]: return sr.st_dev, sr.st_ino - def make_ftc(path: str, spec_id: str, sr: Optional[os.stat_result] = None, parent: str = '') -> FileTransmissionCommand: + def make_ftc(path: str, spec_id: str, sr: os.stat_result | None = None, parent: str = '') -> FileTransmissionCommand: if sr is None: sr = os.stat(path, follow_symlinks=False) if stat.S_ISLNK(sr.st_mode): @@ -209,7 +209,7 @@ ErrorCode = Enum('ErrorCode', 'OK STARTED CANCELED PROGRESS EINVAL EPERM EISDIR class TransmissionError(Exception): def __init__( - self, code: Union[ErrorCode, str] = ErrorCode.EINVAL, + self, code: ErrorCode | str = ErrorCode.EINVAL, msg: str = 'Generic error', transmit: bool = True, file_id: str = '', @@ -282,7 +282,7 @@ class FileTransmissionCommand: ans.append(f'data={len(self.data)} bytes') return 'FTC(' + ', '.join(ans) + ')' - def asdict(self, keep_defaults: bool = False) -> dict[str, Union[str, int, bytes]]: + def asdict(self, keep_defaults: bool = False) -> dict[str, str | int | bytes]: ans = {} for k in fields(self): val = getattr(self, k.name) @@ -293,7 +293,7 @@ class FileTransmissionCommand: ans[k.name] = val return ans - def get_serialized_fields(self, prefix_with_osc_code: bool = False) -> Iterator[Union[str, bytes]]: + def get_serialized_fields(self, prefix_with_osc_code: bool = False) -> Iterator[str | bytes]: nts = name_to_serialized_map() found = False if prefix_with_osc_code: @@ -329,7 +329,7 @@ class FileTransmissionCommand: return ''.join(map(as_unicode, self.get_serialized_fields(prefix_with_osc_code))) @classmethod - def deserialize(cls, data: Union[str, bytes, memoryview]) -> 'FileTransmissionCommand': + def deserialize(cls, data: str | bytes | memoryview) -> 'FileTransmissionCommand': ans = FileTransmissionCommand() fmap = serialized_to_field_map() from kittens.transfer.rsync import parse_ftc @@ -385,8 +385,8 @@ class PatchFile: self.block_buffer = memoryview(bytearray(self.patcher.block_size)) self.path = path self.signature_done = False - self.src_file: Optional[io.BufferedReader] = None - self._dest_file: Optional[IO[bytes]] = None + self.src_file: io.BufferedReader | None = None + self._dest_file: IO[bytes] | None = None self.closed = False @property @@ -450,7 +450,7 @@ class DestFile: if not os.path.isabs(self.name): self.name = abspath(self.name, use_home=True) try: - self.existing_stat: Optional[os.stat_result] = os.stat(self.name, follow_symlinks=False) + self.existing_stat: os.stat_result | None = os.stat(self.name, follow_symlinks=False) except OSError: self.existing_stat = None self.needs_unlink = self.existing_stat is not None and (self.existing_stat.st_nlink > 1 or stat.S_ISLNK(self.existing_stat.st_mode)) @@ -463,9 +463,9 @@ class DestFile: self.ttype = ftc.ttype self.link_target = b'' self.needs_data_sent = self.ttype is not TransmissionType.simple - self.decompressor: Union[ZlibDecompressor, IdentityDecompressor] = ZlibDecompressor() if ftc.compression is Compression.zlib else IdentityDecompressor() + self.decompressor: ZlibDecompressor | IdentityDecompressor = ZlibDecompressor() if ftc.compression is Compression.zlib else IdentityDecompressor() self.closed = self.ftype is FileType.directory - self.actual_file: Union[PatchFile, IO[bytes], None] = None + self.actual_file: PatchFile | IO[bytes] | None = None self.failed = False self.bytes_written = 0 @@ -590,7 +590,7 @@ class ActiveReceive: def __init__(self, request_id: str, quiet: int, bypass: str) -> None: self.id = request_id - self.bypass_ok: Optional[bool] = None + self.bypass_ok: bool | None = None if bypass: byp = get_options().file_transfer_confirmation_bypass self.bypass_ok = check_bypass(byp, request_id, bypass) @@ -658,9 +658,9 @@ class SourceFile: self.stat = os.stat(self.path, follow_symlinks=False) if stat.S_ISDIR(self.stat.st_mode): raise TransmissionError(ErrorCode.EINVAL, msg='Cannot send a directory', file_id=self.file_id) - self.compressor: Union[ZlibCompressor, IdentityCompressor] = IdentityCompressor() + self.compressor: ZlibCompressor | IdentityCompressor = IdentityCompressor() self.target = b'' - self.open_file: Optional[io.BufferedReader] = None + self.open_file: io.BufferedReader | None = None if stat.S_ISLNK(self.stat.st_mode): self.target = os.readlink(self.path).encode('utf-8') else: @@ -719,7 +719,7 @@ class ActiveSend: def __init__(self, request_id: str, quiet: int, bypass: str, num_of_args: int) -> None: self.id = request_id self.expected_num_of_args = num_of_args - self.bypass_ok: Optional[bool] = None + self.bypass_ok: bool | None = None if bypass: byp = get_options().file_transfer_confirmation_bypass self.bypass_ok = check_bypass(byp, request_id, bypass) @@ -730,7 +730,7 @@ class ActiveSend: self.last_activity_at = monotonic() self.file_specs: list[tuple[str, str]] = [] self.queued_files_map: dict[str, SourceFile] = {} - self.active_file: Optional[SourceFile] = None + self.active_file: SourceFile | None = None self.pending_chunks: Deque[FileTransmissionCommand] = deque() self.metadata_sent = False @@ -772,7 +772,7 @@ class ActiveSend: self.active_file.close() self.active_file = None - def next_chunk(self) -> Optional[FileTransmissionCommand]: + def next_chunk(self) -> FileTransmissionCommand | None: self.last_activity_at = monotonic() if self.pending_chunks: return self.pending_chunks.popleft() @@ -810,16 +810,16 @@ class FileTransmission: self.active_receives: dict[str, ActiveReceive] = {} self.active_sends: dict[str, ActiveSend] = {} self.pending_receive_responses: Deque[FileTransmissionCommand] = deque() - self.pending_timer: Optional[int] = None + self.pending_timer: int | None = None - def callback_after(self, callback: Callable[[Optional[int]], None], timeout: float = 0) -> Optional[int]: + def callback_after(self, callback: Callable[[int | None], None], timeout: float = 0) -> int | None: return add_timer(callback, timeout, False) def start_pending_timer(self) -> None: if self.pending_timer is None: self.pending_timer = self.callback_after(self.try_pending, 0.2) - def try_pending(self, timer_id: Optional[int]) -> None: + def try_pending(self, timer_id: int | None) -> None: self.pending_timer = None while self.pending_receive_responses: payload = self.pending_receive_responses.popleft() @@ -973,7 +973,7 @@ class FileTransmission: self.callback_after(self.pump_sends, 0.05) break - def pump_sends(self, timer_id: Optional[int]) -> None: + def pump_sends(self, timer_id: int | None) -> None: for asd in self.active_sends.values(): if asd.metadata_sent: self.pump_send_chunks(asd) @@ -1081,7 +1081,7 @@ class FileTransmission: else: log_error(f'Transmission receive command with unknown action: {cmd.action}, ignoring') - def transmit_rsync_signature(self, receive_id: str, timer_id: Optional[int] = None) -> None: + def transmit_rsync_signature(self, receive_id: str, timer_id: int | None = None) -> None: q = self.active_receives.get(receive_id) if q is None: return @@ -1132,7 +1132,7 @@ class FileTransmission: self.callback_after(partial(self.transmit_rsync_signature, receive_id)) def send_status_response( - self, code: Union[ErrorCode, str] = ErrorCode.EINVAL, + self, code: ErrorCode | str = ErrorCode.EINVAL, request_id: str = '', file_id: str = '', msg: str = '', name: str = '', size: int = -1, ttype: TransmissionType = TransmissionType.simple, @@ -1219,7 +1219,7 @@ class FileTransmission: if ar.send_errors: self.send_status_response(code=ErrorCode.EPERM, request_id=ar.id, msg='User refused the transfer') - def send_fail_on_os_error(self, err: OSError, msg: str, ar: Union[ActiveSend, ActiveReceive], file_id: str = '') -> None: + def send_fail_on_os_error(self, err: OSError, msg: str, ar: ActiveSend | ActiveReceive, file_id: str = '') -> None: if not ar.send_errors: return errname = errno.errorcode.get(err.errno, 'EFAIL') if err.errno is not None else 'EFAIL' @@ -1233,7 +1233,7 @@ class TestFileTransmission(FileTransmission): def __init__(self, allow: bool = True) -> None: super().__init__(0) - self.test_responses: list[dict[str, Union[str, int, bytes]]] = [] + self.test_responses: list[dict[str, str | int | bytes]] = [] self.allow = allow def write_ftc_to_child(self, payload: FileTransmissionCommand, appendleft: bool = False, use_pending: bool = True) -> bool: @@ -1246,6 +1246,6 @@ class TestFileTransmission(FileTransmission): def start_send(self, aid: str) -> None: self.handle_receive_confirmation(self.allow, aid) - def callback_after(self, callback: Callable[[Optional[int]], None], timeout: float = 0) -> Optional[int]: + def callback_after(self, callback: Callable[[int | None], None], timeout: float = 0) -> int | None: callback(None) return None diff --git a/kitty/fonts/__init__.py b/kitty/fonts/__init__.py index f04c76c5d..a0141ea53 100644 --- a/kitty/fonts/__init__.py +++ b/kitty/fonts/__init__.py @@ -1,6 +1,6 @@ from collections.abc import Sequence from enum import Enum, IntEnum, auto -from typing import TYPE_CHECKING, Literal, NamedTuple, Optional, TypedDict, TypeVar, Union +from typing import TYPE_CHECKING, Literal, NamedTuple, TypedDict, TypeVar, Union from kitty.fast_data_types import ParsedFontFeature from kitty.types import run_once @@ -18,7 +18,7 @@ class ListedFont(TypedDict): postscript_name: str is_monospace: bool is_variable: bool - descriptor: Union[FontConfigPattern, CoreTextFont] + descriptor: FontConfigPattern | CoreTextFont class VariableAxis(TypedDict): @@ -127,13 +127,13 @@ class FontModification(NamedTuple): class FontSpec(NamedTuple): - family: Optional[str] = None - style: Optional[str] = None - postscript_name: Optional[str] = None - full_name: Optional[str] = None - system: Optional[str] = None + family: str | None = None + style: str | None = None + postscript_name: str | None = None + full_name: str | None = None + system: str | None = None axes: tuple[tuple[str, float], ...] = () - variable_name: Optional[str] = None + variable_name: str | None = None features: tuple[ParsedFontFeature, ...] = () created_from_string: str = '' diff --git a/kitty/fonts/common.py b/kitty/fonts/common.py index 8b8462515..2292a346a 100644 --- a/kitty/fonts/common.py +++ b/kitty/fonts/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2024, Kovid Goyal -from typing import TYPE_CHECKING, Any, Literal, Optional, TypedDict, Union +from typing import TYPE_CHECKING, Any, Literal, TypedDict, Union from kitty.constants import is_macos from kitty.fast_data_types import ParsedFontFeature @@ -18,11 +18,11 @@ if TYPE_CHECKING: def all_fonts_map(monospaced: bool) -> FontMap: ... def create_scorer(bold: bool = False, italic: bool = False, monospaced: bool = True, prefer_variable: bool = False) -> Scorer: ... def find_best_match( - family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: Optional[Descriptor] = None, + family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: Descriptor | None = None, prefer_variable: bool = False, ) -> Descriptor: ... def find_last_resort_text_font(bold: bool = False, italic: bool = False, monospaced: bool = True) -> Descriptor: ... - def face_from_descriptor(descriptor: Descriptor, font_sz_in_pts: Optional[float] = None, dpi_x: Optional[float] = None, dpi_y: Optional[float] = None + def face_from_descriptor(descriptor: Descriptor, font_sz_in_pts: float | None = None, dpi_x: float | None = None, dpi_y: float | None = None ) -> Face: ... def is_monospace(descriptor: Descriptor) -> bool: ... def is_variable(descriptor: Descriptor) -> bool: ... @@ -73,29 +73,29 @@ class Event: class FamilyAxisValues: - regular_weight: Optional[float] = None - regular_slant: Optional[float] = None - regular_ital: Optional[float] = None - regular_width: Optional[float] = None + regular_weight: float | None = None + regular_slant: float | None = None + regular_ital: float | None = None + regular_width: float | None = None - bold_weight: Optional[float] = None + bold_weight: float | None = None - italic_slant: Optional[float] = None - italic_ital: Optional[float] = None + italic_slant: float | None = None + italic_ital: float | None = None - def get_wght(self, bold: bool, italic: bool) -> Optional[float]: + def get_wght(self, bold: bool, italic: bool) -> float | None: return self.bold_weight if bold else self.regular_weight - def get_ital(self, bold: bool, italic: bool) -> Optional[float]: + def get_ital(self, bold: bool, italic: bool) -> float | None: return self.italic_ital if italic else self.regular_ital - def get_slnt(self, bold: bool, italic: bool) -> Optional[float]: + def get_slnt(self, bold: bool, italic: bool) -> float | None: return self.italic_slant if italic else self.regular_slant - def get_wdth(self, bold: bool, italic: bool) -> Optional[float]: + def get_wdth(self, bold: bool, italic: bool) -> float | None: return self.regular_width - def get(self, tag: str, bold: bool, italic: bool) -> Optional[float]: + def get(self, tag: str, bold: bool, italic: bool) -> float | None: f = getattr(self, f'get_{tag}', None) return None if f is None else f(bold, italic) @@ -133,8 +133,8 @@ def get_variable_data_for_face(d: Face) -> VariableData: def find_best_match_in_candidates( - candidates: list[DescriptorVar], scorer: Scorer, is_medium_face: bool, ignore_face: Optional[DescriptorVar] = None -) -> Optional[DescriptorVar]: + candidates: list[DescriptorVar], scorer: Scorer, is_medium_face: bool, ignore_face: DescriptorVar | None = None +) -> DescriptorVar | None: if candidates: for x in scorer.sorted_candidates(candidates): if ignore_face is None or x != ignore_face: @@ -254,7 +254,7 @@ def find_best_variable_face(spec: FontSpec, bold: bool, italic: bool, monospaced def get_fine_grained_font( spec: FontSpec, bold: bool = False, italic: bool = False, family_axis_values: FamilyAxisValues = FamilyAxisValues(), - resolved_medium_font: Optional[Descriptor] = None, monospaced: bool = True, match_is_more_specific_than_family: Event = Event() + resolved_medium_font: Descriptor | None = None, monospaced: bool = True, match_is_more_specific_than_family: Event = Event() ) -> Descriptor: font_map = all_fonts_map(monospaced) is_medium_face = resolved_medium_font is None @@ -318,7 +318,7 @@ def apply_variation_to_pattern(pat: Descriptor, spec: FontSpec) -> tuple[Descrip def get_font_from_spec( spec: FontSpec, bold: bool = False, italic: bool = False, family_axis_values: FamilyAxisValues = FamilyAxisValues(), - resolved_medium_font: Optional[Descriptor] = None, match_is_more_specific_than_family: Event = Event() + resolved_medium_font: Descriptor | None = None, match_is_more_specific_than_family: Event = Event() ) -> Descriptor: if not spec.is_system: ans = get_fine_grained_font(spec, bold, italic, resolved_medium_font=resolved_medium_font, family_axis_values=family_axis_values, @@ -404,7 +404,7 @@ def axis_values_are_equal(defaults: dict[str, float], a: dict[str, float], b: di return ad == bd -def _get_named_style(axis_map: dict[str, float], vd: VariableData) -> Optional[NamedStyle]: +def _get_named_style(axis_map: dict[str, float], vd: VariableData) -> NamedStyle | None: defaults = {ax['tag']: ax['default'] for ax in vd['axes']} for ns in vd['named_styles']: if axis_values_are_equal(defaults, ns['axis_values'], axis_map): @@ -412,7 +412,7 @@ def _get_named_style(axis_map: dict[str, float], vd: VariableData) -> Optional[N return None -def get_named_style(face_or_descriptor: Union[Face, Descriptor]) -> Optional[NamedStyle]: +def get_named_style(face_or_descriptor: Face | Descriptor) -> NamedStyle | None: if isinstance(face_or_descriptor, dict): d: Descriptor = face_or_descriptor vd = get_variable_data_for_descriptor(d) @@ -437,7 +437,7 @@ def get_named_style(face_or_descriptor: Union[Face, Descriptor]) -> Optional[Nam return _get_named_style(axis_map, vd) -def get_axis_map(face_or_descriptor: Union[Face, Descriptor]) -> dict[str, float]: +def get_axis_map(face_or_descriptor: Face | Descriptor) -> dict[str, float]: base_axis_map = {} axis_map: dict[str, float] = {} if isinstance(face_or_descriptor, dict): diff --git a/kitty/fonts/core_text.py b/kitty/fonts/core_text.py index 1b381d840..1424281ed 100644 --- a/kitty/fonts/core_text.py +++ b/kitty/fonts/core_text.py @@ -6,7 +6,7 @@ import operator from collections import defaultdict from collections.abc import Generator, Iterable, Sequence from functools import lru_cache -from typing import NamedTuple, Optional +from typing import NamedTuple from kitty.fast_data_types import CTFace, coretext_all_fonts from kitty.typing import CoreTextFont @@ -109,7 +109,7 @@ def weight_range_for_family(family: str) -> WeightRange: class CTScorer(Scorer): - weight_range: Optional[WeightRange] = None + weight_range: WeightRange | None = None def score(self, candidate: Descriptor) -> Score: assert candidate['descriptor_type'] == 'core_text' @@ -167,7 +167,7 @@ def find_last_resort_text_font(bold: bool = False, italic: bool = False, monospa def find_best_match( - family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: Optional[CoreTextFont] = None, + family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, ignore_face: CoreTextFont | None = None, prefer_variable: bool = False ) -> CoreTextFont: q = family_name_to_key(family) diff --git a/kitty/fonts/fontconfig.py b/kitty/fonts/fontconfig.py index 59b0f4fc5..8b51fefd9 100644 --- a/kitty/fonts/fontconfig.py +++ b/kitty/fonts/fontconfig.py @@ -126,7 +126,7 @@ def weight_range_for_family(family: str) -> WeightRange: class FCScorer(Scorer): - weight_range: Optional[WeightRange] = None + weight_range: WeightRange | None = None def score(self, candidate: Descriptor) -> Score: assert candidate['descriptor_type'] == 'fontconfig' @@ -174,7 +174,7 @@ def find_last_resort_text_font(bold: bool = False, italic: bool = False, monospa def find_best_match( family: str, bold: bool = False, italic: bool = False, monospaced: bool = True, - ignore_face: Optional[FontConfigPattern] = None, prefer_variable: bool = False, + ignore_face: FontConfigPattern | None = None, prefer_variable: bool = False, ) -> FontConfigPattern: from .common import find_best_match_in_candidates q = family_name_to_key(family) @@ -203,7 +203,7 @@ def find_best_match( for possibility in tries: for key, map_key in (('postscript_name', 'ps_map'), ('full_name', 'full_map'), ('family', 'family_map')): map_key = cast(FontCollectionMapType, map_key) - val: Optional[str] = cast(Optional[str], possibility.get(key)) + val: str | None = cast(Optional[str], possibility.get(key)) if val: candidates = font_map[map_key].get(family_name_to_key(val)) if candidates: diff --git a/kitty/fonts/list.py b/kitty/fonts/list.py index d11a98631..33210460f 100644 --- a/kitty/fonts/list.py +++ b/kitty/fonts/list.py @@ -2,7 +2,6 @@ # License: GPL v3 Copyright: 2017, Kovid Goyal from collections.abc import Sequence -from typing import Optional from kitty.constants import is_macos @@ -23,7 +22,7 @@ def create_family_groups(monospaced: bool = True) -> dict[str, list[ListedFont]] return {k: prune_family_group(v) for k, v in g.items()} -def as_json(indent: Optional[int] = None) -> str: +def as_json(indent: int | None = None) -> str: import json groups = create_family_groups() for v in groups.values(): diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index 23cfe2a8f..7bbdda931 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -4,8 +4,8 @@ import ctypes import os import sys -from collections.abc import Generator -from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union +from collections.abc import Callable, Generator +from typing import TYPE_CHECKING, Any, Literal, Union from kitty.constants import fonts_dir, is_macos from kitty.fast_data_types import ( @@ -38,7 +38,7 @@ else: FontObject = Union[CoreTextFont, FontConfigPattern] current_faces: list[tuple[FontObject, bool, bool]] = [] -builtin_nerd_font_descriptor: Optional[FontObject] = None +builtin_nerd_font_descriptor: FontObject | None = None def font_for_family(family: str) -> tuple[FontObject, bool, bool]: @@ -162,7 +162,7 @@ def create_narrow_symbols(opts: Options) -> tuple[tuple[int, int, int], ...]: descriptor_overrides: dict[int, tuple[str, bool, bool]] = {} -def descriptor_for_idx(idx: int) -> tuple[Union[FontObject, str], bool, bool]: +def descriptor_for_idx(idx: int) -> tuple[FontObject | str, bool, bool]: ans = descriptor_overrides.get(idx) if ans is None: return current_faces[idx] @@ -181,7 +181,7 @@ def dump_font_debug() -> None: log_error(' ' + s.identify_for_debug()) -def set_font_family(opts: Optional[Options] = None, override_font_size: Optional[float] = None, add_builtin_nerd_font: bool = False) -> None: +def set_font_family(opts: Options | None = None, override_font_size: float | None = None, add_builtin_nerd_font: bool = False) -> None: global current_faces, builtin_nerd_font_descriptor opts = opts or defaults sz = override_font_size or opts.font_size @@ -274,7 +274,7 @@ def render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi: def shape_string( - text: str = "abcd", family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, path: Optional[str] = None + text: str = "abcd", family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, path: str | None = None ) -> list[tuple[int, int, int, tuple[int, ...]]]: with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height): s = Screen(None, 1, len(text)*2) @@ -283,7 +283,7 @@ def shape_string( return test_shape(line, path) -def show(rgba_data: Union[bytes, memoryview], width: int, height: int, fmt: int = 32) -> None: +def show(rgba_data: bytes | memoryview, width: int, height: int, fmt: int = 32) -> None: from base64 import standard_b64encode from kittens.tui.images import GraphicsCommand @@ -331,7 +331,7 @@ def test_render_string( print('\n') -def test_fallback_font(qtext: Optional[str] = None, bold: bool = False, italic: bool = False) -> None: +def test_fallback_font(qtext: str | None = None, bold: bool = False, italic: bool = False) -> None: with setup_for_testing(): if qtext: trials = [qtext] diff --git a/kitty/guess_mime_type.py b/kitty/guess_mime_type.py index b05238f7c..44f349ec2 100644 --- a/kitty/guess_mime_type.py +++ b/kitty/guess_mime_type.py @@ -4,7 +4,6 @@ import os import stat from contextlib import suppress -from typing import Optional known_extensions = { 'asciidoc': 'text/asciidoctor', @@ -41,7 +40,7 @@ text_mimes = ( ) -def is_special_file(path: str) -> Optional[str]: +def is_special_file(path: str) -> str | None: name = os.path.basename(path) lname = name.lower() if lname == 'makefile' or lname.startswith('makefile.'): @@ -74,7 +73,7 @@ def clear_mime_cache() -> None: delattr(initialize_mime_database, 'inited') -def guess_type(path: str, allow_filesystem_access: bool = False) -> Optional[str]: +def guess_type(path: str, allow_filesystem_access: bool = False) -> str | None: is_dir = is_exe = False if allow_filesystem_access: diff --git a/kitty/key_encoding.py b/kitty/key_encoding.py index 77ff90b4f..8fea44dba 100644 --- a/kitty/key_encoding.py +++ b/kitty/key_encoding.py @@ -3,7 +3,7 @@ from enum import IntEnum from functools import lru_cache -from typing import NamedTuple, Optional, Union +from typing import NamedTuple from . import fast_data_types as defines from .fast_data_types import KeyEvent as WindowSystemKeyEvent @@ -214,7 +214,7 @@ class KeyEvent(NamedTuple): caps_lock: bool = False num_lock: bool = False - def matches(self, spec: Union[str, ParsedShortcut], types: int = EventType.PRESS | EventType.REPEAT) -> bool: + def matches(self, spec: str | ParsedShortcut, types: int = EventType.PRESS | EventType.REPEAT) -> bool: mods = self.mods_without_locks if not self.type & types: return False @@ -227,7 +227,7 @@ class KeyEvent(NamedTuple): return True return False - def matches_without_mods(self, spec: Union[str, ParsedShortcut], types: int = EventType.PRESS | EventType.REPEAT) -> bool: + def matches_without_mods(self, spec: str | ParsedShortcut, types: int = EventType.PRESS | EventType.REPEAT) -> bool: if not self.type & types: return False if isinstance(spec, str): @@ -419,7 +419,7 @@ def encode_key_event(key_event: KeyEvent) -> str: return ans + trailer -def decode_key_event_as_window_system_key(text: str) -> Optional[WindowSystemKeyEvent]: +def decode_key_event_as_window_system_key(text: str) -> WindowSystemKeyEvent | None: csi, trailer = text[2:-1], text[-1] try: k = decode_key_event(csi, trailer) diff --git a/kitty/key_names.py b/kitty/key_names.py index b07b33b04..da5720b01 100644 --- a/kitty/key_names.py +++ b/kitty/key_names.py @@ -2,8 +2,9 @@ # License: GPLv3 Copyright: 2019, Kovid Goyal import sys +from collections.abc import Callable from contextlib import suppress -from typing import Callable, Optional +from typing import Optional from .constants import is_macos @@ -53,7 +54,7 @@ character_key_name_aliases: dict[str, str] = { LookupFunc = Callable[[str, bool], Optional[int]] -def null_lookup(name: str, case_sensitive: bool = False) -> Optional[int]: +def null_lookup(name: str, case_sensitive: bool = False) -> int | None: return None @@ -78,14 +79,14 @@ else: f.argtypes = [ctypes.c_char_p, ctypes.c_int] f.restype = ctypes.c_int - def xkb_lookup(name: str, case_sensitive: bool = False) -> Optional[int]: + def xkb_lookup(name: str, case_sensitive: bool = False) -> int | None: q = name.encode('utf-8') return f(q, int(case_sensitive)) or None return xkb_lookup def get_key_name_lookup() -> LookupFunc: - ans: Optional[LookupFunc] = getattr(get_key_name_lookup, 'ans', None) + ans: LookupFunc | None = getattr(get_key_name_lookup, 'ans', None) if ans is None: try: ans = load_libxkb_lookup() diff --git a/kitty/keys.py b/kitty/keys.py index fe114a41c..cccf4a617 100644 --- a/kitty/keys.py +++ b/kitty/keys.py @@ -1,9 +1,9 @@ #!/usr/bin/env python # License: GPL v3 Copyright: 2016, Kovid Goyal -from collections.abc import Iterable, Iterator +from collections.abc import Callable, Iterable, Iterator from gettext import gettext as _ -from typing import TYPE_CHECKING, Any, Callable, Optional +from typing import TYPE_CHECKING, Any, Optional from .constants import is_macos from .fast_data_types import ( @@ -38,7 +38,7 @@ def keyboard_mode_name(screen: ScreenType) -> str: return 'application' if screen.cursor_key_mode else 'normal' -def get_shortcut(keymap: KeyMap, ev: KeyEvent) -> Optional[list[KeyDefinition]]: +def get_shortcut(keymap: KeyMap, ev: KeyEvent) -> list[KeyDefinition] | None: mods = ev.mods & mod_mask ans = keymap.get(SingleKey(mods, False, ev.key)) if ans is None and ev.shifted_key and mods & GLFW_MOD_SHIFT: @@ -64,7 +64,7 @@ class Mappings: ' Manage all keyboard mappings ' - def __init__(self, global_shortcuts:Optional[dict[str, SingleKey]] = None, callback_on_mode_change: Callable[[], Any] = lambda: None) -> None: + def __init__(self, global_shortcuts:dict[str, SingleKey] | None = None, callback_on_mode_change: Callable[[], Any] = lambda: None) -> None: self.keyboard_mode_stack: list[KeyboardMode] = [] self.update_keymap(global_shortcuts) self.callback_on_mode_change = callback_on_mode_change @@ -73,7 +73,7 @@ class Mappings: def current_keyboard_mode_name(self) -> str: return self.keyboard_mode_stack[-1].name if self.keyboard_mode_stack else '' - def update_keymap(self, global_shortcuts: Optional[dict[str, SingleKey]] = None) -> None: + def update_keymap(self, global_shortcuts: dict[str, SingleKey] | None = None) -> None: if global_shortcuts is None: global_shortcuts = self.set_cocoa_global_shortcuts(self.get_options()) if is_macos else {} self.global_shortcuts_map: KeyMap = {v: [KeyDefinition(definition=k)] for k, v in global_shortcuts.items()} diff --git a/kitty/launch.py b/kitty/launch.py index ff2306a8b..3201183be 100644 --- a/kitty/launch.py +++ b/kitty/launch.py @@ -6,7 +6,7 @@ import os import shutil from collections.abc import Container, Iterable, Iterator, Sequence from contextlib import suppress -from typing import Any, NamedTuple, Optional, TypedDict +from typing import Any, NamedTuple, TypedDict from .boss import Boss from .child import Child @@ -360,7 +360,7 @@ Relative paths are resolved relative to the :ref:`kitty config directory ''' -def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec: +def parse_launch_args(args: Sequence[str] | None = None) -> LaunchSpec: args = list(args or ()) try: opts, args = parse_args(result_class=LaunchCLIOptions, args=args, ospec=options_spec) @@ -369,7 +369,7 @@ def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec: return LaunchSpec(opts, args) -def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None, base_env: Optional[dict[str,str]] = None) -> dict[str, str]: +def get_env(opts: LaunchCLIOptions, active_child: Child | None = None, base_env: dict[str,str] | None = None) -> dict[str, str]: env: dict[str, str] = {} if opts.copy_env and active_child: env.update(active_child.foreground_environ) @@ -381,9 +381,9 @@ def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None, base_e return env -def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Optional[Tab] = None) -> Optional[Tab]: +def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Tab | None = None) -> Tab | None: - def create_tab(tm: Optional[TabManager] = None) -> Tab: + def create_tab(tm: TabManager | None = None) -> Tab: if tm is None: oswid = boss.add_os_window( wclass=opts.os_window_class, @@ -413,7 +413,7 @@ def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Optional[Tab] watcher_modules: dict[str, Any] = {} -def load_watch_modules(watchers: Iterable[str]) -> Optional[Watchers]: +def load_watch_modules(watchers: Iterable[str]) -> Watchers | None: if not watchers: return None import runpy @@ -469,18 +469,18 @@ def load_watch_modules(watchers: Iterable[str]) -> Optional[Watchers]: class LaunchKwds(TypedDict): allow_remote_control: bool - remote_control_passwords: Optional[dict[str, Sequence[str]]] - cwd_from: Optional[CwdRequest] - cwd: Optional[str] - location: Optional[str] - override_title: Optional[str] - copy_colors_from: Optional[Window] - marker: Optional[str] - cmd: Optional[list[str]] - overlay_for: Optional[int] - stdin: Optional[bytes] + remote_control_passwords: dict[str, Sequence[str]] | None + cwd_from: CwdRequest | None + cwd: str | None + location: str | None + override_title: str | None + copy_colors_from: Window | None + marker: str | None + cmd: list[str] | None + overlay_for: int | None + stdin: bytes | None hold: bool - bias: Optional[float] + bias: float | None def apply_colors(window: Window, spec: Sequence[str]) -> None: @@ -519,8 +519,8 @@ force_window_launch = ForceWindowLaunch() non_window_launch_types = 'background', 'clipboard', 'primary' -def parse_remote_control_passwords(allow_remote_control: bool, passwords: Sequence[str]) -> Optional[dict[str, Sequence[str]]]: - remote_control_restrictions: Optional[dict[str, Sequence[str]]] = None +def parse_remote_control_passwords(allow_remote_control: bool, passwords: Sequence[str]) -> dict[str, Sequence[str]] | None: + remote_control_restrictions: dict[str, Sequence[str]] | None = None if allow_remote_control and passwords: from kitty.options.utils import remote_control_password remote_control_restrictions = {} @@ -534,13 +534,13 @@ def _launch( boss: Boss, opts: LaunchCLIOptions, args: list[str], - target_tab: Optional[Tab] = None, + target_tab: Tab | None = None, force_target_tab: bool = False, - active: Optional[Window] = None, + active: Window | None = None, is_clone_launch: str = '', - rc_from_window: Optional[Window] = None, - base_env: Optional[dict[str, str]] = None, -) -> Optional[Window]: + rc_from_window: Window | None = None, + base_env: dict[str, str] | None = None, +) -> Window | None: active = active or boss.active_window_for_cwd if active: active_child = active.child @@ -708,13 +708,13 @@ def launch( boss: Boss, opts: LaunchCLIOptions, args: list[str], - target_tab: Optional[Tab] = None, + target_tab: Tab | None = None, force_target_tab: bool = False, - active: Optional[Window] = None, + active: Window | None = None, is_clone_launch: str = '', - rc_from_window: Optional[Window] = None, - base_env: Optional[dict[str, str]] = None, -) -> Optional[Window]: + rc_from_window: Window | None = None, + base_env: dict[str, str] | None = None, +) -> Window | None: active = active or boss.active_window_for_cwd if opts.keep_focus and active: orig, active.ignore_focus_changes = active.ignore_focus_changes, True @@ -844,7 +844,7 @@ class EditCmd: def on_edit_window_close(self, window: Window) -> None: self.check_status() - def check_status(self, timer_id: Optional[int] = None) -> None: + def check_status(self, timer_id: int | None = None) -> None: if self.abort_signaled: return boss = get_boss() @@ -880,7 +880,7 @@ class CloneCmd: def __init__(self, msg: str) -> None: self.args: list[str] = [] - self.env: Optional[dict[str, str]] = None + self.env: dict[str, str] | None = None self.cwd = '' self.shell = '' self.envfmt = 'default' diff --git a/kitty/layout/base.py b/kitty/layout/base.py index cfb160ea6..64e7fa8ac 100644 --- a/kitty/layout/base.py +++ b/kitty/layout/base.py @@ -1,9 +1,10 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal +from collections.abc import Generator, Iterable, Iterator, Sequence from functools import partial from itertools import repeat -from typing import Any, Dict, Generator, Iterable, Iterator, List, NamedTuple, Optional, Sequence, Tuple, Union +from typing import Any, NamedTuple from kitty.borders import BorderColor from kitty.fast_data_types import Region, set_active_window, viewport_for_window @@ -20,10 +21,10 @@ class BorderLine(NamedTuple): class LayoutOpts: - def __init__(self, data: Dict[str, str]): + def __init__(self, data: dict[str, str]): pass - def serialized(self) -> Dict[str, Any]: + def serialized(self) -> dict[str, Any]: return {} @@ -35,16 +36,16 @@ class LayoutData(NamedTuple): content_size: int = 0 -DecorationPairs = Sequence[Tuple[int, int]] +DecorationPairs = Sequence[tuple[int, int]] LayoutDimension = Generator[LayoutData, None, None] -ListOfWindows = List[WindowType] +ListOfWindows = list[WindowType] class NeighborsMap(TypedDict): - left: List[int] - top: List[int] - right: List[int] - bottom: List[int] + left: list[int] + top: list[int] + right: list[int] + bottom: list[int] class LayoutGlobalData: @@ -61,7 +62,7 @@ class LayoutGlobalData: lgd = LayoutGlobalData() -def idx_for_id(win_id: int, windows: Iterable[WindowType]) -> Optional[int]: +def idx_for_id(win_id: int, windows: Iterable[WindowType]) -> int | None: for i, w in enumerate(windows): if w.id == win_id: return i @@ -75,7 +76,7 @@ def set_layout_options(opts: Options) -> None: lgd.alignment_y = -1 if opts.placement_strategy.startswith('top') else 1 if opts.placement_strategy.startswith('bottom') else 0 -def convert_bias_map(bias: Dict[int, float], number_of_windows: int, number_of_cells: int) -> Sequence[float]: +def convert_bias_map(bias: dict[int, float], number_of_windows: int, number_of_cells: int) -> Sequence[float]: cells_per_window, extra = divmod(number_of_cells, number_of_windows) cell_map = list(repeat(cells_per_window, number_of_windows)) cell_map[-1] += extra @@ -84,9 +85,9 @@ def convert_bias_map(bias: Dict[int, float], number_of_windows: int, number_of_c def calculate_cells_map( - bias: Union[None, Sequence[float], Dict[int, float]], + bias: None | Sequence[float] | dict[int, float], number_of_windows: int, number_of_cells: int -) -> List[int]: +) -> list[int]: if isinstance(bias, dict): bias = convert_bias_map(bias, number_of_windows, number_of_cells) cells_per_window = number_of_cells // number_of_windows @@ -110,7 +111,7 @@ def layout_dimension( start_at: int, length: int, cell_length: int, decoration_pairs: DecorationPairs, alignment: int = 0, - bias: Union[None, Sequence[float], Dict[int, float]] = None + bias: None | Sequence[float] | dict[int, float] = None ) -> LayoutDimension: number_of_windows = len(decoration_pairs) number_of_cells = length // cell_length @@ -194,14 +195,14 @@ def safe_increment_bias(old_val: float, increment: float = 0) -> float: return max(0.1, min(old_val + increment, 0.9)) -def normalize_biases(biases: List[float]) -> List[float]: +def normalize_biases(biases: list[float]) -> list[float]: s = sum(biases) if s == 1.0: return biases return [x/s for x in biases] -def distribute_indexed_bias(base_bias: Sequence[float], index_bias_map: Dict[int, float]) -> Sequence[float]: +def distribute_indexed_bias(base_bias: Sequence[float], index_bias_map: dict[int, float]) -> Sequence[float]: if not index_bias_map: return base_bias ans = list(base_bias) @@ -226,7 +227,7 @@ class Layout: self.set_owner(os_window_id, tab_id) # A set of rectangles corresponding to the blank spaces at the edges of # this layout, i.e. spaces that are not covered by any window - self.blank_rects: List[Rect] = [] + self.blank_rects: list[Rect] = [] self.layout_opts = self.parse_layout_opts(layout_opts) assert self.name is not None self.full_name = f'{self.name}:{layout_opts}' if layout_opts else self.name @@ -259,8 +260,8 @@ class Layout: return False return self.apply_bias(idx, increment, all_windows, is_horizontal) - def parse_layout_opts(self, layout_opts: Optional[str] = None) -> LayoutOpts: - data: Dict[str, str] = {} + def parse_layout_opts(self, layout_opts: str | None = None) -> LayoutOpts: + data: dict[str, str] = {} if layout_opts: for x in layout_opts.split(';'): k, v = x.partition('=')[::2] @@ -268,7 +269,7 @@ class Layout: data[k] = v return type(self.layout_opts)(data) - def nth_window(self, all_windows: WindowList, num: int) -> Optional[WindowType]: + def nth_window(self, all_windows: WindowList, num: int) -> WindowType | None: return all_windows.active_window_in_nth_group(num, clamp=True) def activate_nth_window(self, all_windows: WindowList, num: int) -> None: @@ -292,9 +293,9 @@ class Layout: return all_windows.move_window_group(to_group=group) def add_window( - self, all_windows: WindowList, window: WindowType, location: Optional[str] = None, - overlay_for: Optional[int] = None, put_overlay_behind: bool = False, bias: Optional[float] = None, - ) -> Optional[WindowType]: + self, all_windows: WindowList, window: WindowType, location: str | None = None, + overlay_for: int | None = None, put_overlay_behind: bool = False, bias: float | None = None, + ) -> WindowType | None: if overlay_for is not None: underlay = all_windows.id_map.get(overlay_for) if underlay is not None: @@ -306,8 +307,8 @@ class Layout: self.add_non_overlay_window(all_windows, window, location, bias) return None - def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: Optional[str], bias: Optional[float] = None) -> None: - next_to: Optional[WindowType] = None + def add_non_overlay_window(self, all_windows: WindowList, window: WindowType, location: str | None, bias: float | None = None) -> None: + next_to: WindowType | None = None before = False next_to = all_windows.active_window if location is not None: @@ -370,9 +371,9 @@ class Layout: def xlayout( self, groups: Iterator[WindowGroup], - bias: Union[None, Sequence[float], Dict[int, float]] = None, - start: Optional[int] = None, - size: Optional[int] = None, + bias: None | Sequence[float] | dict[int, float] = None, + start: int | None = None, + size: int | None = None, offset: int = 0, border_mult: int = 1 ) -> LayoutDimension: @@ -389,9 +390,9 @@ class Layout: def ylayout( self, groups: Iterator[WindowGroup], - bias: Union[None, Sequence[float], Dict[int, float]] = None, - start: Optional[int] = None, - size: Optional[int] = None, + bias: None | Sequence[float] | dict[int, float] = None, + start: int | None = None, + size: int | None = None, offset: int = 0, border_mult: int = 1 ) -> LayoutDimension: @@ -417,7 +418,7 @@ class Layout: def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap: return {'left': [], 'right': [], 'top': [], 'bottom': []} - def compute_needs_borders_map(self, all_windows: WindowList) -> Dict[int, bool]: + def compute_needs_borders_map(self, all_windows: WindowList) -> dict[int, bool]: return all_windows.compute_needs_borders_map(lgd.draw_active_borders) def get_minimal_borders(self, windows: WindowList) -> Generator[BorderLine, None, None]: @@ -428,8 +429,8 @@ class Layout: return yield BorderLine() # type: ignore - def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]: + def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> bool | None: pass - def layout_state(self) -> Dict[str, Any]: + def layout_state(self) -> dict[str, Any]: return {} diff --git a/kitty/layout/grid.py b/kitty/layout/grid.py index 65032d6ed..32cf0b38a 100644 --- a/kitty/layout/grid.py +++ b/kitty/layout/grid.py @@ -1,10 +1,11 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal +from collections.abc import Callable, Generator, Sequence from functools import lru_cache from itertools import repeat from math import ceil, floor -from typing import Any, Callable, Dict, Generator, List, Optional, Sequence, Set, Tuple +from typing import Any from kitty.borders import BorderColor from kitty.types import Edges @@ -15,8 +16,8 @@ from .base import BorderLine, Layout, LayoutData, LayoutDimension, ListOfWindows from .tall import neighbors_for_tall_window -@lru_cache() -def calc_grid_size(n: int) -> Tuple[int, int, int, int]: +@lru_cache +def calc_grid_size(n: int) -> tuple[int, int, int, int]: if n <= 5: ncols = 1 if n == 1 else 2 else: @@ -35,14 +36,14 @@ class Grid(Layout): no_minimal_window_borders = True def remove_all_biases(self) -> bool: - self.biased_rows: Dict[int, float] = {} - self.biased_cols: Dict[int, float] = {} + self.biased_rows: dict[int, float] = {} + self.biased_cols: dict[int, float] = {} return True def column_layout( self, num: int, - bias: Optional[Sequence[float]] = None, + bias: Sequence[float] | None = None, ) -> LayoutDimension: decoration_pairs = tuple(repeat((0, 0), num)) return layout_dimension(lgd.central.left, lgd.central.width, lgd.cell_width, decoration_pairs, bias=bias, alignment=lgd.alignment_x) @@ -50,18 +51,18 @@ class Grid(Layout): def row_layout( self, num: int, - bias: Optional[Sequence[float]] = None, + bias: Sequence[float] | None = None, ) -> LayoutDimension: decoration_pairs = tuple(repeat((0, 0), num)) return layout_dimension(lgd.central.top, lgd.central.height, lgd.cell_height, decoration_pairs, bias=bias, alignment=lgd.alignment_y) - def variable_layout(self, layout_func: Callable[..., LayoutDimension], num_windows: int, biased_map: Dict[int, float]) -> LayoutDimension: + def variable_layout(self, layout_func: Callable[..., LayoutDimension], num_windows: int, biased_map: dict[int, float]) -> LayoutDimension: return layout_func(num_windows, bias=biased_map if num_windows > 1 else None) - def position_for_window_idx(self, idx: int, num_windows: int, ncols:int , nrows: int, special_rows: int, special_col: int) -> Tuple[int, int]: + def position_for_window_idx(self, idx: int, num_windows: int, ncols:int , nrows: int, special_rows: int, special_col: int) -> tuple[int, int]: row_num = col_num = 0 - def on_col_done(col_windows: List[int]) -> None: + def on_col_done(col_windows: list[int]) -> None: nonlocal col_num, row_num row_num = 0 col_num += 1 @@ -103,7 +104,7 @@ class Grid(Layout): bias_idx = col_num attr = 'biased_cols' - def layout_func(windows: ListOfWindows, bias: Optional[Sequence[float]] = None) -> LayoutDimension: + def layout_func(windows: ListOfWindows, bias: Sequence[float] | None = None) -> LayoutDimension: return self.column_layout(num_windows, bias=bias) else: @@ -113,7 +114,7 @@ class Grid(Layout): bias_idx = row_num attr = 'biased_rows' - def layout_func(windows: ListOfWindows, bias: Optional[Sequence[float]] = None) -> LayoutDimension: + def layout_func(windows: ListOfWindows, bias: Sequence[float] | None = None) -> LayoutDimension: return self.row_layout(num_windows, bias=bias) before_layout = tuple(self.variable_layout(layout_func, num_windows, b)) @@ -130,8 +131,8 @@ class Grid(Layout): num_windows: int, nrows: int, ncols: int, special_rows: int, special_col: int, - on_col_done: Callable[[List[int]], None] = lambda col_windows: None - ) -> Generator[Tuple[int, LayoutData, LayoutData], None, None]: + on_col_done: Callable[[list[int]], None] = lambda col_windows: None + ) -> Generator[tuple[int, LayoutData, LayoutData], None, None]: # Distribute windows top-to-bottom, left-to-right (i.e. in columns) xlayout = self.variable_layout(self.column_layout, ncols, self.biased_cols) yvals_normal = tuple(self.variable_layout(self.row_layout, nrows, self.biased_rows)) @@ -156,13 +157,13 @@ class Grid(Layout): return ncols, nrows, special_rows, special_col = calc_grid_size(n) groups = tuple(all_windows.iter_all_layoutable_groups()) - win_col_map: List[List[WindowGroup]] = [] + win_col_map: list[list[WindowGroup]] = [] - def on_col_done(col_windows: List[int]) -> None: + def on_col_done(col_windows: list[int]) -> None: col_windows_w = [groups[i] for i in col_windows] win_col_map.append(col_windows_w) - def extents(ld: LayoutData) -> Tuple[int, int]: + def extents(ld: LayoutData) -> tuple[int, int]: start = ld.content_pos - ld.space_before size = ld.space_before + ld.space_after + ld.content_size return start, size @@ -197,20 +198,20 @@ class Grid(Layout): return needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders) ncols, nrows, special_rows, special_col = calc_grid_size(n) - is_first_row: Set[int] = set() - is_last_row: Set[int] = set() - is_first_column: Set[int] = set() - is_last_column: Set[int] = set() + is_first_row: set[int] = set() + is_last_row: set[int] = set() + is_first_column: set[int] = set() + is_last_column: set[int] = set() groups = tuple(all_windows.iter_all_layoutable_groups()) bw = groups[0].effective_border() if not bw: return xl: LayoutData = LayoutData() yl: LayoutData = LayoutData() - prev_col_windows: List[int] = [] - layout_data_map: Dict[int, Tuple[LayoutData, LayoutData]] = {} + prev_col_windows: list[int] = [] + layout_data_map: dict[int, tuple[LayoutData, LayoutData]] = {} - def on_col_done(col_windows: List[int]) -> None: + def on_col_done(col_windows: list[int]) -> None: nonlocal prev_col_windows, is_first_column if col_windows: is_first_row.add(groups[col_windows[0]].id) @@ -219,7 +220,7 @@ class Grid(Layout): is_first_column = {groups[x].id for x in col_windows} prev_col_windows = col_windows - all_groups_in_order: List[WindowGroup] = [] + all_groups_in_order: list[WindowGroup] = [] for window_idx, xl, yl in self.layout_windows(n, nrows, ncols, special_rows, special_col, on_col_done): wg = groups[window_idx] all_groups_in_order.append(wg) @@ -227,7 +228,7 @@ class Grid(Layout): is_last_column = {groups[x].id for x in prev_col_windows} active_group = all_windows.active_group - def ends(yl: LayoutData) -> Tuple[int, int]: + def ends(yl: LayoutData) -> tuple[int, int]: return yl.content_pos - yl.space_before, yl.content_pos + yl.content_size + yl.space_after def borders_for_window(gid: int) -> Generator[Edges, None, None]: @@ -266,11 +267,11 @@ class Grid(Layout): wg = all_windows.group_for_window(window) assert wg is not None ncols, nrows, special_rows, special_col = calc_grid_size(n) - blank_row: List[Optional[int]] = [None for i in range(ncols)] + blank_row: list[int | None] = [None for i in range(ncols)] matrix = tuple(blank_row[:] for j in range(max(nrows, special_rows))) wi = all_windows.iter_all_layoutable_groups() - pos_map: Dict[int, Tuple[int, int]] = {} - col_counts: List[int] = [] + pos_map: dict[int, tuple[int, int]] = {} + col_counts: list[int] = [] for col in range(ncols): rows = special_rows if col == special_col else nrows for row in range(rows): @@ -280,14 +281,14 @@ class Grid(Layout): col_counts.append(rows) row, col = pos_map[wg.id] - def neighbors(row: int, col: int) -> List[int]: + def neighbors(row: int, col: int) -> list[int]: try: ans = matrix[row][col] except IndexError: ans = None return [] if ans is None else [ans] - def side(row: int, col: int, delta: int) -> List[int]: + def side(row: int, col: int, delta: int) -> list[int]: neighbor_col = col + delta neighbor_nrows = col_counts[neighbor_col] nrows = col_counts[col] @@ -308,7 +309,7 @@ class Grid(Layout): 'right': side(row, col, 1) if col < ncols - 1 else [], } - def layout_state(self) -> Dict[str, Any]: + def layout_state(self) -> dict[str, Any]: return { 'biased_cols': self.biased_cols, 'biased_rows': self.biased_rows diff --git a/kitty/layout/interface.py b/kitty/layout/interface.py index 6c196e3ac..784126124 100644 --- a/kitty/layout/interface.py +++ b/kitty/layout/interface.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import Dict, Tuple, Type from .base import Layout from .grid import Grid @@ -10,7 +9,7 @@ from .stack import Stack from .tall import Fat, Tall from .vertical import Horizontal, Vertical -all_layouts: Dict[str, Type[Layout]] = { +all_layouts: dict[str, type[Layout]] = { Stack.name: Stack, Tall.name: Tall, Fat.name: Fat, @@ -20,11 +19,11 @@ all_layouts: Dict[str, Type[Layout]] = { Splits.name: Splits, } -KeyType = Tuple[str, int, int, str] +KeyType = tuple[str, int, int, str] class CreateLayoutObjectFor: - cache: Dict[KeyType, Layout] = {} + cache: dict[KeyType, Layout] = {} def __call__( self, diff --git a/kitty/layout/splits.py b/kitty/layout/splits.py index 6ab6de710..ba6e71003 100644 --- a/kitty/layout/splits.py +++ b/kitty/layout/splits.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import Any, Collection, Dict, Generator, List, NamedTuple, Optional, Sequence, Tuple, Union +from collections.abc import Collection, Generator, Sequence +from typing import Any, NamedTuple, Optional, Union from kitty.borders import BorderColor from kitty.types import Edges, WindowGeometry @@ -20,11 +21,11 @@ class Pair: def __init__(self, horizontal: bool = True): self.horizontal = horizontal - self.one: Optional[Union[Pair, int]] = None - self.two: Optional[Union[Pair, int]] = None + self.one: Pair | int | None = None + self.two: Pair | int | None = None self.bias = 0.5 self.top = self.left = self.width = self.height = 0 - self.between_borders: List[Edges] = [] + self.between_borders: list[Edges] = [] self.first_extent = self.second_extent = Extent() def __repr__(self) -> str: @@ -158,19 +159,19 @@ class Pair: def apply_window_geometry( self, window_id: int, window_geometry: WindowGeometry, - id_window_map: Dict[int, WindowGroup], + id_window_map: dict[int, WindowGroup], layout_object: Layout ) -> None: wg = id_window_map[window_id] wg.set_geometry(window_geometry) layout_object.blank_rects.extend(blank_rects_for_window(window_geometry)) - def effective_border(self, id_window_map: Dict[int, WindowGroup]) -> int: + def effective_border(self, id_window_map: dict[int, WindowGroup]) -> int: for wid in self.all_window_ids(): return id_window_map[wid].effective_border() return 0 - def minimum_width(self, id_window_map: Dict[int, WindowGroup]) -> int: + def minimum_width(self, id_window_map: dict[int, WindowGroup]) -> int: if self.one is None or self.two is None or not self.horizontal: return lgd.cell_width bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0 @@ -185,7 +186,7 @@ class Pair: ans += lgd.cell_width return ans - def minimum_height(self, id_window_map: Dict[int, WindowGroup]) -> int: + def minimum_height(self, id_window_map: dict[int, WindowGroup]) -> int: if self.one is None or self.two is None or self.horizontal: return lgd.cell_height bw = self.effective_border(id_window_map) if lgd.draw_minimal_borders else 0 @@ -203,7 +204,7 @@ class Pair: def layout_pair( self, left: int, top: int, width: int, height: int, - id_window_map: Dict[int, WindowGroup], + id_window_map: dict[int, WindowGroup], layout_object: Layout ) -> None: self.between_borders = [] @@ -358,7 +359,7 @@ class Pair: def neighbors_for_window(self, window_id: int, ans: NeighborsMap, layout_object: 'Splits', all_windows: WindowList) -> None: - def quadrant(is_horizontal: bool, is_first: bool) -> Tuple[EdgeLiteral, EdgeLiteral]: + def quadrant(is_horizontal: bool, is_first: bool) -> tuple[EdgeLiteral, EdgeLiteral]: if is_horizontal: if is_first: return 'left', 'right' @@ -380,7 +381,7 @@ class Pair: ans[which].append(other) def is_neighbouring_geometry(a: WindowGeometry, b: WindowGeometry, direction: str) -> bool: - def edges(g: WindowGeometry) -> Tuple[int, int]: + def edges(g: WindowGeometry) -> tuple[int, int]: return (g.top, g.bottom) if direction in ['left', 'right'] else (g.left, g.right) a1, a2 = edges(a) @@ -427,16 +428,16 @@ class Pair: class SplitsLayoutOpts(LayoutOpts): - default_axis_is_horizontal: Optional[bool] = True + default_axis_is_horizontal: bool | None = True - def __init__(self, data: Dict[str, str]): + def __init__(self, data: dict[str, str]): q = data.get('split_axis', 'horizontal') if q == 'auto': self.default_axis_is_horizontal = None else: self.default_axis_is_horizontal = q == 'horizontal' - def serialized(self) -> Dict[str, Any]: + def serialized(self) -> dict[str, Any]: return {'default_axis_is_horizontal': self.default_axis_is_horizontal} @@ -447,12 +448,12 @@ class Splits(Layout): no_minimal_window_borders = True @property - def default_axis_is_horizontal(self) -> Optional[bool]: + def default_axis_is_horizontal(self) -> bool | None: return self.layout_opts.default_axis_is_horizontal @property def pairs_root(self) -> Pair: - root: Optional[Pair] = getattr(self, '_pairs_root', None) + root: Pair | None = getattr(self, '_pairs_root', None) if root is None: horizontal = self.default_axis_is_horizontal if horizontal is None: @@ -499,8 +500,8 @@ class Splits(Layout): self, all_windows: WindowList, window: WindowType, - location: Optional[str], - bias: Optional[float] = None, + location: str | None, + bias: float | None = None, ) -> None: horizontal = self.default_axis_is_horizontal after = True @@ -604,7 +605,7 @@ class Splits(Layout): self.pairs_root.swap_windows(before.id, after.id) return moved - def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]: + def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> bool | None: if action_name == 'rotate': args = args or ('90',) try: @@ -671,10 +672,10 @@ class Splits(Layout): return None - def layout_state(self) -> Dict[str, Any]: + def layout_state(self) -> dict[str, Any]: - def add_pair(p: Pair) -> Dict[str, Any]: - ans: Dict[str, Any] = {} + def add_pair(p: Pair) -> dict[str, Any]: + ans: dict[str, Any] = {} ans['horizontal'] = p.horizontal ans['bias'] = p.bias if isinstance(p.one, Pair): diff --git a/kitty/layout/tall.py b/kitty/layout/tall.py index e0133815d..bcec562cc 100644 --- a/kitty/layout/tall.py +++ b/kitty/layout/tall.py @@ -1,8 +1,9 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal +from collections.abc import Generator, Sequence from itertools import islice, repeat -from typing import Any, Dict, Generator, List, Optional, Sequence, Tuple +from typing import Any from kitty.borders import BorderColor from kitty.conf.utils import to_bool @@ -65,7 +66,7 @@ class TallLayoutOpts(LayoutOpts): full_size: int = 1 mirrored: bool = False - def __init__(self, data: Dict[str, str]): + def __init__(self, data: dict[str, str]): try: self.full_size = int(data.get('full_size', 1)) except Exception: @@ -77,16 +78,16 @@ class TallLayoutOpts(LayoutOpts): self.bias = 50 self.mirrored = to_bool(data.get('mirrored', 'false')) - def serialized(self) -> Dict[str, Any]: + def serialized(self) -> dict[str, Any]: return {'full_size': self.full_size, 'bias': self.bias, 'mirrored': self.mirrored} - def build_bias_list(self) -> Tuple[float, ...]: + def build_bias_list(self) -> tuple[float, ...]: b = self.bias / 100 b = max(0.1, min(b, 0.9)) return tuple(repeat(b / self.full_size, self.full_size)) + (1.0 - b,) -def set_bias(biases: Sequence[float], idx: int, target: float) -> List[float]: +def set_bias(biases: Sequence[float], idx: int, target: float) -> list[float]: remainder = 1 - target previous_remainder = sum(x for i, x in enumerate(biases) if i != idx) ans = [1. for i in range(len(biases))] @@ -112,11 +113,11 @@ class Tall(Layout): return self.layout_opts.full_size def remove_all_biases(self) -> bool: - self.main_bias: List[float] = list(self.layout_opts.build_bias_list()) - self.biased_map: Dict[int, float] = {} + self.main_bias: list[float] = list(self.layout_opts.build_bias_list()) + self.biased_map: dict[int, float] = {} return True - def variable_layout(self, all_windows: WindowList, biased_map: Dict[int, float]) -> LayoutDimension: + def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension: num = all_windows.num_groups - self.num_full_size_windows bias = biased_map if num > 1 else None return self.perp_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias, offset=self.num_full_size_windows) @@ -157,7 +158,7 @@ class Tall(Layout): self.biased_map = candidate return before != after - def simple_layout(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]: + def simple_layout(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]: num = all_windows.num_groups is_fat = not self.main_is_horizontal mirrored = self.layout_opts.mirrored @@ -173,7 +174,7 @@ class Tall(Layout): xl, yl = yl, xl yield wg, xl, yl, True - def full_layout(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]: + def full_layout(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData, bool], None, None]: is_fat = not self.main_is_horizontal mirrored = self.layout_opts.mirrored groups = tuple(all_windows.iter_all_layoutable_groups()) @@ -228,7 +229,7 @@ class Tall(Layout): def neighbors_for_window(self, window: WindowType, windows: WindowList) -> NeighborsMap: return neighbors_for_tall_window(self.num_full_size_windows, window, windows, self.layout_opts.mirrored, self.main_is_horizontal) - def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> Optional[bool]: + def layout_action(self, action_name: str, args: Sequence[str], all_windows: WindowList) -> bool | None: if action_name == 'increase_num_full_size_windows': self.layout_opts.full_size += 1 self.main_bias = list(self.layout_opts.build_bias_list()) @@ -282,8 +283,8 @@ class Tall(Layout): layout = (x[:3] for x in self.simple_layout(all_windows)) yield from borders(layout, self.main_is_horizontal, all_windows) return - main_layouts: List[Tuple[WindowGroup, LayoutData, LayoutData]] = [] - perp_borders: List[BorderLine] = [] + main_layouts: list[tuple[WindowGroup, LayoutData, LayoutData]] = [] + perp_borders: list[BorderLine] = [] layouts = (self.simple_layout if num <= self.num_full_size_windows else self.full_layout)(all_windows) needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders) active_group = all_windows.active_group @@ -344,7 +345,7 @@ class Tall(Layout): ) yield from perp_borders[1:-1] - def layout_state(self) -> Dict[str, Any]: + def layout_state(self) -> dict[str, Any]: return { 'num_full_size_windows': self.num_full_size_windows, 'main_bias': self.main_bias, diff --git a/kitty/layout/vertical.py b/kitty/layout/vertical.py index 0549d83d7..2a5f667eb 100644 --- a/kitty/layout/vertical.py +++ b/kitty/layout/vertical.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import Any, Dict, Generator, Iterable, List, Tuple +from collections.abc import Generator, Iterable +from typing import Any from kitty.borders import BorderColor from kitty.types import Edges @@ -12,12 +13,12 @@ from .base import BorderLine, Layout, LayoutData, LayoutDimension, NeighborsMap, def borders( - data: Iterable[Tuple[WindowGroup, LayoutData, LayoutData]], + data: Iterable[tuple[WindowGroup, LayoutData, LayoutData]], is_horizontal: bool, all_windows: WindowList, start_offset: int = 1, end_offset: int = 1 ) -> Generator[BorderLine, None, None]: - borders: List[BorderLine] = [] + borders: list[BorderLine] = [] active_group = all_windows.active_group needs_borders_map = all_windows.compute_needs_borders_map(lgd.draw_active_borders) try: @@ -66,7 +67,7 @@ class Vertical(Layout): main_axis_layout = Layout.ylayout perp_axis_layout = Layout.xlayout - def variable_layout(self, all_windows: WindowList, biased_map: Dict[int, float]) -> LayoutDimension: + def variable_layout(self, all_windows: WindowList, biased_map: dict[int, float]) -> LayoutDimension: num_windows = all_windows.num_groups bias = biased_map if num_windows > 1 else None return self.main_axis_layout(all_windows.iter_all_layoutable_groups(), bias=bias) @@ -75,7 +76,7 @@ class Vertical(Layout): return self.perp_axis_layout(iter((wg,)), border_mult=0 if lgd.draw_minimal_borders else 1) def remove_all_biases(self) -> bool: - self.biased_map: Dict[int, float] = {} + self.biased_map: dict[int, float] = {} return True def apply_bias(self, idx: int, increment: float, all_windows: WindowList, is_horizontal: bool = True) -> bool: @@ -99,7 +100,7 @@ class Vertical(Layout): after_layout = tuple(self.variable_layout(all_windows, self.biased_map)) return before_layout == after_layout - def generate_layout_data(self, all_windows: WindowList) -> Generator[Tuple[WindowGroup, LayoutData, LayoutData], None, None]: + def generate_layout_data(self, all_windows: WindowList) -> Generator[tuple[WindowGroup, LayoutData, LayoutData], None, None]: ylayout = self.variable_layout(all_windows, self.biased_map) for wg, yl in zip(all_windows.iter_all_layoutable_groups(), ylayout): xl = next(self.fixed_layout(wg)) @@ -136,7 +137,7 @@ class Vertical(Layout): return {'left': before, 'right': after, 'top': [], 'bottom': []} return {'top': before, 'bottom': after, 'left': [], 'right': []} - def layout_state(self) -> Dict[str, Any]: + def layout_state(self) -> dict[str, Any]: return {'biased_map': self.biased_map} diff --git a/kitty/main.py b/kitty/main.py index 162d46b21..83e2f2f4e 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -7,7 +7,6 @@ import shutil import sys from collections.abc import Generator, Sequence from contextlib import contextmanager, suppress -from typing import Optional from .borders import load_borders_program from .boss import Boss @@ -101,7 +100,7 @@ def init_glfw(opts: Options, debug_keyboard: bool = False, debug_rendering: bool def get_macos_shortcut_for( func_map: dict[tuple[str, ...], list[SingleKey]], defn: str = 'new_os_window', lookup_name: str = '' -) -> Optional[SingleKey]: +) -> SingleKey | None: # for maximum robustness we should use opts.alias_map to resolve # aliases however this requires parsing everything on startup which could # be potentially slow. Lets just hope the user doesn't alias these @@ -248,7 +247,7 @@ class AppRunner: def __init__(self) -> None: self.cached_values_name = 'main' self.first_window_callback = lambda window_handle: None - self.layer_shell_config: Optional[LayerShellConfig] = None + self.layer_shell_config: LayerShellConfig | None = None self.initial_window_size_func = initial_window_size_func def __call__(self, opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (), talk_fd: int = -1) -> None: @@ -471,9 +470,9 @@ def _main() -> None: if not cwd_ok: os.chdir(os.path.expanduser('~')) if getattr(sys, 'cmdline_args_for_open', False): - usage: Optional[str] = 'file_or_url ...' - appname: Optional[str] = 'kitty +open' - msg: Optional[str] = ( + usage: str | None = 'file_or_url ...' + appname: str | None = 'kitty +open' + msg: str | None = ( 'Run kitty and open the specified files or URLs in it, using launch-actions.conf. For details' ' see https://sw.kovidgoyal.net/kitty/open_actions/#scripting-the-opening-of-files-with-kitty-on-macos' '\n\nAll the normal kitty options can be used.') diff --git a/kitty/marks.py b/kitty/marks.py index 293d97251..a35461213 100644 --- a/kitty/marks.py +++ b/kitty/marks.py @@ -2,10 +2,10 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal import re -from collections.abc import Generator, Iterable, Sequence +from collections.abc import Callable, Generator, Iterable, Sequence from ctypes import POINTER, c_uint, c_void_p, cast from re import Pattern -from typing import Callable, Union +from typing import Union from .utils import resolve_custom_file @@ -79,7 +79,7 @@ def marker_from_function(func: Callable[[str], Iterable[tuple[int, int, int]]]) return marker -def marker_from_spec(ftype: str, spec: Union[str, Sequence[tuple[int, str]]], flags: int) -> MarkerFunc: +def marker_from_spec(ftype: str, spec: str | Sequence[tuple[int, str]], flags: int) -> MarkerFunc: if ftype == 'regex': assert not isinstance(spec, str) if len(spec) == 1: diff --git a/kitty/multiprocessing.py b/kitty/multiprocessing.py index a419dd2ec..357cfe82e 100644 --- a/kitty/multiprocessing.py +++ b/kitty/multiprocessing.py @@ -6,10 +6,10 @@ import os -from collections.abc import Sequence +from collections.abc import Callable, Sequence from concurrent.futures import ProcessPoolExecutor from multiprocessing import context, get_all_start_methods, get_context, spawn, util -from typing import TYPE_CHECKING, Any, Callable, Optional, Union +from typing import TYPE_CHECKING, Any, Union from .constants import kitty_exe @@ -51,12 +51,12 @@ def unmonkey_patch_multiprocessing() -> None: def get_process_pool_executor( prefer_fork: bool = False, - max_workers: Optional[int] = None, - initializer: Optional[Callable[..., None]] = None, + max_workers: int | None = None, + initializer: Callable[..., None] | None = None, initargs: tuple[Any, ...] = () ) -> ProcessPoolExecutor: if prefer_fork and 'fork' in get_all_start_methods(): - ctx: Union[context.DefaultContext, context.ForkContext] = get_context('fork') + ctx: context.DefaultContext | context.ForkContext = get_context('fork') else: monkey_patch_multiprocessing() ctx = get_context() diff --git a/kitty/notifications.py b/kitty/notifications.py index 208a61f4e..87769d19f 100644 --- a/kitty/notifications.py +++ b/kitty/notifications.py @@ -4,12 +4,12 @@ import os import re from collections import OrderedDict -from collections.abc import Iterator, Sequence +from collections.abc import Callable, Iterator, Sequence from contextlib import suppress from enum import Enum from functools import partial from itertools import count -from typing import Any, Callable, NamedTuple, Optional, Set, Union +from typing import Any, NamedTuple, Set from weakref import ReferenceType, ref from .constants import cache_dir, config_dir, is_macos, logo_png_file, standard_icon_names, standard_sound_names @@ -183,13 +183,13 @@ class EncodedDataStore: def truncated(self) -> int: return self.data_store.truncated - def add_unencoded_data(self, data: Union[str, bytes]) -> None: + def add_unencoded_data(self, data: str | bytes) -> None: if isinstance(data, str): data = data.encode('utf-8') self.flush_encoded_data() self.data_store(data) - def add_base64_data(self, data: Union[str, bytes]) -> None: + def add_base64_data(self, data: str | bytes) -> None: if isinstance(data, str): data = data.encode('ascii') try: @@ -222,7 +222,7 @@ class NotificationCommand: body: str = '' actions: frozenset[Action] = frozenset((Action.focus,)) only_when: OnlyWhen = OnlyWhen.unset - urgency: Optional[Urgency] = None + urgency: Urgency | None = None icon_data_key: str = '' icon_names: tuple[str, ...] = () application_name: str = '' @@ -232,21 +232,21 @@ class NotificationCommand: sound_name: str = '' # event callbacks - on_activation: Optional[Callable[['NotificationCommand', int], None]] = None - on_close: Optional[Callable[['NotificationCommand'], None]] = None - on_update: Optional[Callable[['NotificationCommand', 'NotificationCommand'], None]] = None + on_activation: Callable[['NotificationCommand', int], None] | None = None + on_close: Callable[['NotificationCommand'], None] | None = None + on_update: Callable[['NotificationCommand', 'NotificationCommand'], None] | None = None # metadata identifier: str = '' done: bool = True channel_id: int = 0 desktop_notification_id: int = -1 - close_response_requested: Optional[bool] = None + close_response_requested: bool | None = None icon_path: str = '' # payload handling current_payload_type: PayloadType = PayloadType.title - current_payload_buffer: Optional[EncodedDataStore] = None + current_payload_buffer: EncodedDataStore | None = None # desktop integration specific fields created_by_desktop: bool = False @@ -476,7 +476,7 @@ class DesktopIntegration: def close_notification(self, desktop_notification_id: int) -> bool: raise NotImplementedError('Implement me in subclass') - def notify(self, nc: NotificationCommand, existing_desktop_notification_id: Optional[int]) -> int: + def notify(self, nc: NotificationCommand, existing_desktop_notification_id: int | None) -> int: raise NotImplementedError('Implement me in subclass') def on_new_version_notification_activation(self, cmd: NotificationCommand, which: int) -> None: @@ -580,7 +580,7 @@ class MacOSIntegration(DesktopIntegration): self.category_cache.popitem(False) return ans - def notify(self, nc: NotificationCommand, existing_desktop_notification_id: Optional[int]) -> int: + def notify(self, nc: NotificationCommand, existing_desktop_notification_id: int | None) -> int: desktop_notification_id = existing_desktop_notification_id or next(self.id_counter) from .fast_data_types import cocoa_send_notification # If the body is not set macos makes the title the body and uses @@ -680,14 +680,14 @@ class FreeDesktopIntegration(DesktopIntegration): log_error(f'Close request for {desktop_notification_id=} {"succeeded" if close_succeeded else "failed"}') return close_succeeded - def get_desktop_notification_id(self, dbus_notification_id: int, event: str) -> Optional[int]: + def get_desktop_notification_id(self, dbus_notification_id: int, event: str) -> int | None: q = self.dbus_to_desktop.get(dbus_notification_id) if q is None: if debug_desktop_integration: log_error(f'Could not find desktop_notification_id for {dbus_notification_id=} for event {event}') return q - def get_dbus_notification_id(self, desktop_notification_id: int, event: str) ->Optional[int]: + def get_dbus_notification_id(self, desktop_notification_id: int, event: str) ->int | None: q = self.desktop_to_dbus.get(desktop_notification_id) if q is None: if debug_desktop_integration: @@ -709,7 +709,7 @@ class FreeDesktopIntegration(DesktopIntegration): from .fast_data_types import play_desktop_sound_async play_desktop_sound_async(sn, event_id='desktop notification') - def dispatch_event_from_desktop(self, event_type: str, dbus_notification_id: int, extra: Union[int, str]) -> None: + def dispatch_event_from_desktop(self, event_type: str, dbus_notification_id: int, extra: int | str) -> None: if event_type == 'capabilities': capabilities = frozenset(str(extra).splitlines()) self.supports_body = 'body' in capabilities @@ -733,7 +733,7 @@ class FreeDesktopIntegration(DesktopIntegration): elif event_type == 'closed': self.notification_manager.notification_closed(desktop_notification_id) - def notify(self, nc: NotificationCommand, existing_desktop_notification_id: Optional[int]) -> int: + def notify(self, nc: NotificationCommand, existing_desktop_notification_id: int | None) -> int: from .fast_data_types import dbus_send_notification from .xdg import icon_exists, icon_for_appname app_icon = '' @@ -782,7 +782,7 @@ class UIState(NamedTuple): class Channel: - def window_for_id(self, channel_id: int) -> Optional[WindowType]: + def window_for_id(self, channel_id: int) -> WindowType | None: boss = get_boss() if channel_id: return boss.window_id_map.get(channel_id) @@ -832,7 +832,7 @@ class NotificationManager: def __init__( self, - desktop_integration: Union[MacOSIntegration, FreeDesktopIntegration, None] = None, + desktop_integration: MacOSIntegration | FreeDesktopIntegration | None = None, channel: Channel = Channel(), log: Log = Log(), debug: bool = False, @@ -865,7 +865,7 @@ class NotificationManager: self.in_progress_notification_commands_by_client_id: dict[str, NotificationCommand] = {} self.pending_commands: dict[int, NotificationCommand] = {} - def notification_created(self, desktop_notification_id: int) -> Optional[NotificationCommand]: + def notification_created(self, desktop_notification_id: int) -> NotificationCommand | None: if n := self.in_progress_notification_commands.get(desktop_notification_id): n.created_by_desktop = True if n.timeout > 0 and not self.desktop_integration.supports_timeout_natively: @@ -955,12 +955,12 @@ class NotificationManager: return True return False - def notify_with_command(self, cmd: NotificationCommand, channel_id: int) -> Optional[int]: + def notify_with_command(self, cmd: NotificationCommand, channel_id: int) -> int | None: cmd.channel_id = channel_id cmd.finalise() if not cmd.title or not self.is_notification_allowed(cmd, channel_id) or self.is_notification_filtered(cmd): return None - existing_desktop_notification_id: Optional[int] = None + existing_desktop_notification_id: int | None = None existing_cmd = self.in_progress_notification_commands_by_client_id.get(cmd.identifier) if cmd.identifier else None if existing_cmd: existing_desktop_notification_id = existing_cmd.desktop_notification_id @@ -988,7 +988,7 @@ class NotificationManager: def parse_notification_cmd( self, prev_cmd: NotificationCommand, channel_id: int, raw: str - ) -> Optional[NotificationCommand]: + ) -> NotificationCommand | None: metadata, payload = raw.partition(';')[::2] cmd = self.create_notification_cmd() try: diff --git a/kitty/open_actions.py b/kitty/open_actions.py index 61d269065..e4d58b81e 100644 --- a/kitty/open_actions.py +++ b/kitty/open_actions.py @@ -7,7 +7,7 @@ import posixpath import shlex from collections.abc import Iterable, Iterator from contextlib import suppress -from typing import Any, NamedTuple, Optional, cast +from typing import Any, NamedTuple, cast from urllib.parse import ParseResult, unquote, urlparse from .conf.utils import KeyAction, to_cmdline_implementation @@ -276,7 +276,7 @@ action launch --type=os-window ssh -- $URL '''.splitlines())) -def actions_for_url(url: str, actions_spec: Optional[str] = None) -> Iterator[KeyAction]: +def actions_for_url(url: str, actions_spec: str | None = None) -> Iterator[KeyAction]: if actions_spec is None: actions = load_open_actions() else: diff --git a/kitty/options/parse.py b/kitty/options/parse.py index b7ff1b9a0..02ac32c7d 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -27,26 +27,26 @@ from kitty.options.utils import ( class Parser: - def action_alias(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def action_alias(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in action_alias(val): ans["action_alias"][k] = v - def active_border_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def active_border_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_border_color'] = to_color_or_none(val) - def active_tab_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def active_tab_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_tab_background'] = to_color(val) - def active_tab_font_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def active_tab_font_style(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_tab_font_style'] = tab_font_style(val) - def active_tab_foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def active_tab_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_tab_foreground'] = to_color(val) - def active_tab_title_template(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def active_tab_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_tab_title_template'] = active_tab_title_template(val) - def allow_cloning(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def allow_cloning(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_allow_cloning: raise ValueError(f"The value {val} is not a valid choice for allow_cloning") @@ -54,10 +54,10 @@ class Parser: choices_for_allow_cloning = frozenset(('yes', 'y', 'true', 'no', 'n', 'false', 'ask')) - def allow_hyperlinks(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def allow_hyperlinks(self, val: str, ans: dict[str, typing.Any]) -> None: ans['allow_hyperlinks'] = allow_hyperlinks(val) - def allow_remote_control(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def allow_remote_control(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_allow_remote_control: raise ValueError(f"The value {val} is not a valid choice for allow_remote_control") @@ -65,16 +65,16 @@ class Parser: choices_for_allow_remote_control = frozenset(('password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true')) - def background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background'] = to_color(val) - def background_blur(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_blur(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background_blur'] = int(val) - def background_image(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_image(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background_image'] = config_or_absolute_path(val) - def background_image_layout(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_image_layout(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_background_image_layout: raise ValueError(f"The value {val} is not a valid choice for background_image_layout") @@ -82,868 +82,868 @@ class Parser: choices_for_background_image_layout = frozenset(('mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered', 'cscaled')) - def background_image_linear(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_image_linear(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background_image_linear'] = to_bool(val) - def background_opacity(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_opacity(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background_opacity'] = unit_float(val) - def background_tint(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_tint(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background_tint'] = unit_float(val) - def background_tint_gaps(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def background_tint_gaps(self, val: str, ans: dict[str, typing.Any]) -> None: ans['background_tint_gaps'] = unit_float(val) - def bell_border_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def bell_border_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['bell_border_color'] = to_color(val) - def bell_on_tab(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def bell_on_tab(self, val: str, ans: dict[str, typing.Any]) -> None: ans['bell_on_tab'] = bell_on_tab(val) - def bell_path(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def bell_path(self, val: str, ans: dict[str, typing.Any]) -> None: ans['bell_path'] = config_or_absolute_path(val) - def bold_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def bold_font(self, val: str, ans: dict[str, typing.Any]) -> None: ans['bold_font'] = parse_font_spec(val) - def bold_italic_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def bold_italic_font(self, val: str, ans: dict[str, typing.Any]) -> None: ans['bold_italic_font'] = parse_font_spec(val) - def box_drawing_scale(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def box_drawing_scale(self, val: str, ans: dict[str, typing.Any]) -> None: ans['box_drawing_scale'] = box_drawing_scale(val) - def clear_all_mouse_actions(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def clear_all_mouse_actions(self, val: str, ans: dict[str, typing.Any]) -> None: clear_all_mouse_actions(val, ans) - def clear_all_shortcuts(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def clear_all_shortcuts(self, val: str, ans: dict[str, typing.Any]) -> None: clear_all_shortcuts(val, ans) - def click_interval(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def click_interval(self, val: str, ans: dict[str, typing.Any]) -> None: ans['click_interval'] = float(val) - def clipboard_control(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def clipboard_control(self, val: str, ans: dict[str, typing.Any]) -> None: ans['clipboard_control'] = clipboard_control(val) - def clipboard_max_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def clipboard_max_size(self, val: str, ans: dict[str, typing.Any]) -> None: ans['clipboard_max_size'] = positive_float(val) - def clone_source_strategies(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def clone_source_strategies(self, val: str, ans: dict[str, typing.Any]) -> None: ans['clone_source_strategies'] = clone_source_strategies(val) - def close_on_child_death(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def close_on_child_death(self, val: str, ans: dict[str, typing.Any]) -> None: ans['close_on_child_death'] = to_bool(val) - def color0(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color0(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color0'] = to_color(val) - def color1(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color1(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color1'] = to_color(val) - def color2(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color2(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color2'] = to_color(val) - def color3(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color3(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color3'] = to_color(val) - def color4(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color4(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color4'] = to_color(val) - def color5(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color5(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color5'] = to_color(val) - def color6(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color6(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color6'] = to_color(val) - def color7(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color7(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color7'] = to_color(val) - def color8(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color8(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color8'] = to_color(val) - def color9(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color9(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color9'] = to_color(val) - def color10(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color10(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color10'] = to_color(val) - def color11(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color11(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color11'] = to_color(val) - def color12(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color12(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color12'] = to_color(val) - def color13(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color13(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color13'] = to_color(val) - def color14(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color14(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color14'] = to_color(val) - def color15(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color15(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color15'] = to_color(val) - def color16(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color16(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color16'] = to_color(val) - def color17(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color17(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color17'] = to_color(val) - def color18(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color18(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color18'] = to_color(val) - def color19(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color19(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color19'] = to_color(val) - def color20(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color20(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color20'] = to_color(val) - def color21(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color21(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color21'] = to_color(val) - def color22(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color22(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color22'] = to_color(val) - def color23(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color23(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color23'] = to_color(val) - def color24(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color24(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color24'] = to_color(val) - def color25(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color25(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color25'] = to_color(val) - def color26(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color26(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color26'] = to_color(val) - def color27(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color27(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color27'] = to_color(val) - def color28(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color28(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color28'] = to_color(val) - def color29(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color29(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color29'] = to_color(val) - def color30(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color30(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color30'] = to_color(val) - def color31(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color31(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color31'] = to_color(val) - def color32(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color32(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color32'] = to_color(val) - def color33(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color33(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color33'] = to_color(val) - def color34(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color34(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color34'] = to_color(val) - def color35(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color35(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color35'] = to_color(val) - def color36(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color36(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color36'] = to_color(val) - def color37(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color37(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color37'] = to_color(val) - def color38(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color38(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color38'] = to_color(val) - def color39(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color39(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color39'] = to_color(val) - def color40(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color40(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color40'] = to_color(val) - def color41(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color41(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color41'] = to_color(val) - def color42(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color42(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color42'] = to_color(val) - def color43(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color43(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color43'] = to_color(val) - def color44(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color44(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color44'] = to_color(val) - def color45(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color45(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color45'] = to_color(val) - def color46(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color46(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color46'] = to_color(val) - def color47(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color47(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color47'] = to_color(val) - def color48(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color48(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color48'] = to_color(val) - def color49(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color49(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color49'] = to_color(val) - def color50(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color50(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color50'] = to_color(val) - def color51(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color51(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color51'] = to_color(val) - def color52(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color52(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color52'] = to_color(val) - def color53(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color53(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color53'] = to_color(val) - def color54(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color54(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color54'] = to_color(val) - def color55(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color55(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color55'] = to_color(val) - def color56(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color56(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color56'] = to_color(val) - def color57(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color57(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color57'] = to_color(val) - def color58(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color58(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color58'] = to_color(val) - def color59(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color59(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color59'] = to_color(val) - def color60(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color60(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color60'] = to_color(val) - def color61(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color61(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color61'] = to_color(val) - def color62(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color62(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color62'] = to_color(val) - def color63(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color63(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color63'] = to_color(val) - def color64(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color64(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color64'] = to_color(val) - def color65(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color65(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color65'] = to_color(val) - def color66(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color66(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color66'] = to_color(val) - def color67(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color67(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color67'] = to_color(val) - def color68(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color68(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color68'] = to_color(val) - def color69(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color69(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color69'] = to_color(val) - def color70(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color70(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color70'] = to_color(val) - def color71(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color71(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color71'] = to_color(val) - def color72(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color72(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color72'] = to_color(val) - def color73(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color73(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color73'] = to_color(val) - def color74(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color74(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color74'] = to_color(val) - def color75(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color75(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color75'] = to_color(val) - def color76(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color76(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color76'] = to_color(val) - def color77(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color77(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color77'] = to_color(val) - def color78(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color78(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color78'] = to_color(val) - def color79(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color79(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color79'] = to_color(val) - def color80(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color80(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color80'] = to_color(val) - def color81(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color81(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color81'] = to_color(val) - def color82(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color82(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color82'] = to_color(val) - def color83(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color83(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color83'] = to_color(val) - def color84(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color84(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color84'] = to_color(val) - def color85(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color85(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color85'] = to_color(val) - def color86(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color86(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color86'] = to_color(val) - def color87(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color87(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color87'] = to_color(val) - def color88(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color88(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color88'] = to_color(val) - def color89(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color89(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color89'] = to_color(val) - def color90(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color90(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color90'] = to_color(val) - def color91(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color91(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color91'] = to_color(val) - def color92(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color92(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color92'] = to_color(val) - def color93(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color93(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color93'] = to_color(val) - def color94(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color94(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color94'] = to_color(val) - def color95(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color95(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color95'] = to_color(val) - def color96(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color96(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color96'] = to_color(val) - def color97(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color97(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color97'] = to_color(val) - def color98(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color98(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color98'] = to_color(val) - def color99(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color99(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color99'] = to_color(val) - def color100(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color100(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color100'] = to_color(val) - def color101(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color101(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color101'] = to_color(val) - def color102(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color102(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color102'] = to_color(val) - def color103(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color103(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color103'] = to_color(val) - def color104(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color104(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color104'] = to_color(val) - def color105(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color105(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color105'] = to_color(val) - def color106(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color106(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color106'] = to_color(val) - def color107(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color107(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color107'] = to_color(val) - def color108(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color108(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color108'] = to_color(val) - def color109(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color109(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color109'] = to_color(val) - def color110(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color110(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color110'] = to_color(val) - def color111(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color111(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color111'] = to_color(val) - def color112(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color112(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color112'] = to_color(val) - def color113(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color113(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color113'] = to_color(val) - def color114(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color114(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color114'] = to_color(val) - def color115(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color115(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color115'] = to_color(val) - def color116(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color116(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color116'] = to_color(val) - def color117(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color117(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color117'] = to_color(val) - def color118(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color118(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color118'] = to_color(val) - def color119(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color119(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color119'] = to_color(val) - def color120(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color120(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color120'] = to_color(val) - def color121(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color121(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color121'] = to_color(val) - def color122(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color122(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color122'] = to_color(val) - def color123(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color123(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color123'] = to_color(val) - def color124(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color124(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color124'] = to_color(val) - def color125(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color125(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color125'] = to_color(val) - def color126(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color126(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color126'] = to_color(val) - def color127(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color127(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color127'] = to_color(val) - def color128(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color128(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color128'] = to_color(val) - def color129(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color129(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color129'] = to_color(val) - def color130(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color130(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color130'] = to_color(val) - def color131(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color131(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color131'] = to_color(val) - def color132(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color132(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color132'] = to_color(val) - def color133(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color133(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color133'] = to_color(val) - def color134(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color134(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color134'] = to_color(val) - def color135(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color135(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color135'] = to_color(val) - def color136(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color136(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color136'] = to_color(val) - def color137(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color137(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color137'] = to_color(val) - def color138(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color138(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color138'] = to_color(val) - def color139(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color139(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color139'] = to_color(val) - def color140(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color140(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color140'] = to_color(val) - def color141(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color141(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color141'] = to_color(val) - def color142(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color142(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color142'] = to_color(val) - def color143(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color143(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color143'] = to_color(val) - def color144(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color144(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color144'] = to_color(val) - def color145(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color145(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color145'] = to_color(val) - def color146(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color146(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color146'] = to_color(val) - def color147(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color147(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color147'] = to_color(val) - def color148(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color148(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color148'] = to_color(val) - def color149(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color149(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color149'] = to_color(val) - def color150(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color150(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color150'] = to_color(val) - def color151(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color151(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color151'] = to_color(val) - def color152(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color152(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color152'] = to_color(val) - def color153(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color153(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color153'] = to_color(val) - def color154(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color154(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color154'] = to_color(val) - def color155(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color155(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color155'] = to_color(val) - def color156(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color156(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color156'] = to_color(val) - def color157(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color157(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color157'] = to_color(val) - def color158(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color158(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color158'] = to_color(val) - def color159(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color159(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color159'] = to_color(val) - def color160(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color160(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color160'] = to_color(val) - def color161(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color161(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color161'] = to_color(val) - def color162(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color162(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color162'] = to_color(val) - def color163(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color163(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color163'] = to_color(val) - def color164(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color164(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color164'] = to_color(val) - def color165(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color165(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color165'] = to_color(val) - def color166(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color166(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color166'] = to_color(val) - def color167(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color167(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color167'] = to_color(val) - def color168(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color168(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color168'] = to_color(val) - def color169(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color169(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color169'] = to_color(val) - def color170(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color170(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color170'] = to_color(val) - def color171(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color171(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color171'] = to_color(val) - def color172(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color172(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color172'] = to_color(val) - def color173(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color173(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color173'] = to_color(val) - def color174(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color174(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color174'] = to_color(val) - def color175(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color175(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color175'] = to_color(val) - def color176(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color176(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color176'] = to_color(val) - def color177(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color177(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color177'] = to_color(val) - def color178(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color178(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color178'] = to_color(val) - def color179(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color179(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color179'] = to_color(val) - def color180(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color180(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color180'] = to_color(val) - def color181(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color181(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color181'] = to_color(val) - def color182(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color182(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color182'] = to_color(val) - def color183(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color183(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color183'] = to_color(val) - def color184(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color184(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color184'] = to_color(val) - def color185(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color185(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color185'] = to_color(val) - def color186(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color186(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color186'] = to_color(val) - def color187(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color187(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color187'] = to_color(val) - def color188(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color188(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color188'] = to_color(val) - def color189(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color189(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color189'] = to_color(val) - def color190(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color190(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color190'] = to_color(val) - def color191(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color191(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color191'] = to_color(val) - def color192(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color192(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color192'] = to_color(val) - def color193(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color193(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color193'] = to_color(val) - def color194(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color194(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color194'] = to_color(val) - def color195(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color195(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color195'] = to_color(val) - def color196(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color196(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color196'] = to_color(val) - def color197(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color197(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color197'] = to_color(val) - def color198(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color198(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color198'] = to_color(val) - def color199(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color199(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color199'] = to_color(val) - def color200(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color200(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color200'] = to_color(val) - def color201(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color201(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color201'] = to_color(val) - def color202(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color202(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color202'] = to_color(val) - def color203(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color203(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color203'] = to_color(val) - def color204(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color204(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color204'] = to_color(val) - def color205(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color205(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color205'] = to_color(val) - def color206(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color206(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color206'] = to_color(val) - def color207(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color207(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color207'] = to_color(val) - def color208(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color208(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color208'] = to_color(val) - def color209(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color209(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color209'] = to_color(val) - def color210(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color210(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color210'] = to_color(val) - def color211(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color211(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color211'] = to_color(val) - def color212(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color212(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color212'] = to_color(val) - def color213(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color213(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color213'] = to_color(val) - def color214(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color214(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color214'] = to_color(val) - def color215(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color215(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color215'] = to_color(val) - def color216(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color216(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color216'] = to_color(val) - def color217(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color217(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color217'] = to_color(val) - def color218(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color218(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color218'] = to_color(val) - def color219(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color219(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color219'] = to_color(val) - def color220(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color220(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color220'] = to_color(val) - def color221(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color221(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color221'] = to_color(val) - def color222(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color222(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color222'] = to_color(val) - def color223(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color223(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color223'] = to_color(val) - def color224(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color224(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color224'] = to_color(val) - def color225(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color225(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color225'] = to_color(val) - def color226(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color226(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color226'] = to_color(val) - def color227(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color227(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color227'] = to_color(val) - def color228(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color228(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color228'] = to_color(val) - def color229(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color229(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color229'] = to_color(val) - def color230(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color230(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color230'] = to_color(val) - def color231(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color231(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color231'] = to_color(val) - def color232(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color232(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color232'] = to_color(val) - def color233(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color233(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color233'] = to_color(val) - def color234(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color234(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color234'] = to_color(val) - def color235(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color235(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color235'] = to_color(val) - def color236(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color236(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color236'] = to_color(val) - def color237(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color237(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color237'] = to_color(val) - def color238(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color238(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color238'] = to_color(val) - def color239(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color239(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color239'] = to_color(val) - def color240(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color240(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color240'] = to_color(val) - def color241(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color241(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color241'] = to_color(val) - def color242(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color242(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color242'] = to_color(val) - def color243(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color243(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color243'] = to_color(val) - def color244(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color244(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color244'] = to_color(val) - def color245(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color245(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color245'] = to_color(val) - def color246(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color246(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color246'] = to_color(val) - def color247(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color247(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color247'] = to_color(val) - def color248(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color248(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color248'] = to_color(val) - def color249(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color249(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color249'] = to_color(val) - def color250(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color250(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color250'] = to_color(val) - def color251(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color251(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color251'] = to_color(val) - def color252(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color252(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color252'] = to_color(val) - def color253(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color253(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color253'] = to_color(val) - def color254(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color254(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color254'] = to_color(val) - def color255(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def color255(self, val: str, ans: dict[str, typing.Any]) -> None: ans['color255'] = to_color(val) - def command_on_bell(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def command_on_bell(self, val: str, ans: dict[str, typing.Any]) -> None: ans['command_on_bell'] = to_cmdline(val) - def confirm_os_window_close(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def confirm_os_window_close(self, val: str, ans: dict[str, typing.Any]) -> None: ans['confirm_os_window_close'] = int(val) - def copy_on_select(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def copy_on_select(self, val: str, ans: dict[str, typing.Any]) -> None: ans['copy_on_select'] = copy_on_select(val) - def cursor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor'] = to_color_or_none(val) - def cursor_beam_thickness(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_beam_thickness(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_beam_thickness'] = positive_float(val) - def cursor_blink_interval(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_blink_interval(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_blink_interval'] = cursor_blink_interval(val) - def cursor_shape(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_shape(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_shape'] = to_cursor_shape(val) - def cursor_shape_unfocused(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_shape_unfocused(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_shape_unfocused'] = to_cursor_unfocused_shape(val) - def cursor_stop_blinking_after(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_stop_blinking_after(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_stop_blinking_after'] = positive_float(val) - def cursor_text_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_text_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_text_color'] = cursor_text_color(val) - def cursor_trail(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_trail(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_trail'] = positive_int(val) - def cursor_trail_decay(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_trail_decay(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_trail_decay'] = cursor_trail_decay(val) - def cursor_trail_start_threshold(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_trail_start_threshold(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_trail_start_threshold'] = positive_int(val) - def cursor_underline_thickness(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def cursor_underline_thickness(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_underline_thickness'] = positive_float(val) - def default_pointer_shape(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def default_pointer_shape(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_default_pointer_shape: raise ValueError(f"The value {val} is not a valid choice for default_pointer_shape") @@ -951,108 +951,108 @@ class Parser: choices_for_default_pointer_shape = frozenset(('arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'cell', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing')) - def detect_urls(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def detect_urls(self, val: str, ans: dict[str, typing.Any]) -> None: ans['detect_urls'] = to_bool(val) - def dim_opacity(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def dim_opacity(self, val: str, ans: dict[str, typing.Any]) -> None: ans['dim_opacity'] = unit_float(val) - def disable_ligatures(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def disable_ligatures(self, val: str, ans: dict[str, typing.Any]) -> None: ans['disable_ligatures'] = disable_ligatures(val) - def draw_minimal_borders(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def draw_minimal_borders(self, val: str, ans: dict[str, typing.Any]) -> None: ans['draw_minimal_borders'] = to_bool(val) - def dynamic_background_opacity(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def dynamic_background_opacity(self, val: str, ans: dict[str, typing.Any]) -> None: ans['dynamic_background_opacity'] = to_bool(val) - def editor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def editor(self, val: str, ans: dict[str, typing.Any]) -> None: ans['editor'] = str(val) - def enable_audio_bell(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def enable_audio_bell(self, val: str, ans: dict[str, typing.Any]) -> None: ans['enable_audio_bell'] = to_bool(val) - def enabled_layouts(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def enabled_layouts(self, val: str, ans: dict[str, typing.Any]) -> None: ans['enabled_layouts'] = to_layout_names(val) - def env(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def env(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in env(val, ans["env"]): ans["env"][k] = v - def exe_search_path(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def exe_search_path(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in store_multiple(val, ans["exe_search_path"]): ans["exe_search_path"][k] = v - def file_transfer_confirmation_bypass(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def file_transfer_confirmation_bypass(self, val: str, ans: dict[str, typing.Any]) -> None: ans['file_transfer_confirmation_bypass'] = str(val) - def filter_notification(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def filter_notification(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in filter_notification(val, ans["filter_notification"]): ans["filter_notification"][k] = v - def focus_follows_mouse(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def focus_follows_mouse(self, val: str, ans: dict[str, typing.Any]) -> None: ans['focus_follows_mouse'] = to_bool(val) - def font_family(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def font_family(self, val: str, ans: dict[str, typing.Any]) -> None: ans['font_family'] = parse_font_spec(val) - def font_features(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def font_features(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in font_features(val): ans["font_features"][k] = v - def font_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def font_size(self, val: str, ans: dict[str, typing.Any]) -> None: ans['font_size'] = to_font_size(val) - def force_ltr(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def force_ltr(self, val: str, ans: dict[str, typing.Any]) -> None: ans['force_ltr'] = to_bool(val) - def foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['foreground'] = to_color(val) - def forward_stdio(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def forward_stdio(self, val: str, ans: dict[str, typing.Any]) -> None: ans['forward_stdio'] = to_bool(val) - def hide_window_decorations(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def hide_window_decorations(self, val: str, ans: dict[str, typing.Any]) -> None: ans['hide_window_decorations'] = hide_window_decorations(val) - def inactive_border_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def inactive_border_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['inactive_border_color'] = to_color(val) - def inactive_tab_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def inactive_tab_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['inactive_tab_background'] = to_color(val) - def inactive_tab_font_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def inactive_tab_font_style(self, val: str, ans: dict[str, typing.Any]) -> None: ans['inactive_tab_font_style'] = tab_font_style(val) - def inactive_tab_foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def inactive_tab_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['inactive_tab_foreground'] = to_color(val) - def inactive_text_alpha(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def inactive_text_alpha(self, val: str, ans: dict[str, typing.Any]) -> None: ans['inactive_text_alpha'] = unit_float(val) - def initial_window_height(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def initial_window_height(self, val: str, ans: dict[str, typing.Any]) -> None: ans['initial_window_height'] = window_size(val) - def initial_window_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def initial_window_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['initial_window_width'] = window_size(val) - def input_delay(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def input_delay(self, val: str, ans: dict[str, typing.Any]) -> None: ans['input_delay'] = positive_int(val) - def italic_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def italic_font(self, val: str, ans: dict[str, typing.Any]) -> None: ans['italic_font'] = parse_font_spec(val) - def kitten_alias(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def kitten_alias(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in action_alias(val): ans["kitten_alias"][k] = v - def kitty_mod(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def kitty_mod(self, val: str, ans: dict[str, typing.Any]) -> None: ans['kitty_mod'] = to_modifiers(val) - def linux_bell_theme(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def linux_bell_theme(self, val: str, ans: dict[str, typing.Any]) -> None: ans['linux_bell_theme'] = str(val) - def linux_display_server(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def linux_display_server(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_linux_display_server: raise ValueError(f"The value {val} is not a valid choice for linux_display_server") @@ -1060,10 +1060,10 @@ class Parser: choices_for_linux_display_server = frozenset(('auto', 'wayland', 'x11')) - def listen_on(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def listen_on(self, val: str, ans: dict[str, typing.Any]) -> None: ans['listen_on'] = str(val) - def macos_colorspace(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_colorspace(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_macos_colorspace: raise ValueError(f"The value {val} is not a valid choice for macos_colorspace") @@ -1071,22 +1071,22 @@ class Parser: choices_for_macos_colorspace = frozenset(('srgb', 'default', 'displayp3')) - def macos_custom_beam_cursor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_custom_beam_cursor(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_custom_beam_cursor'] = to_bool(val) - def macos_hide_from_tasks(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_hide_from_tasks(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_hide_from_tasks'] = to_bool(val) - def macos_menubar_title_max_length(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_menubar_title_max_length(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_menubar_title_max_length'] = positive_int(val) - def macos_option_as_alt(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_option_as_alt(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_option_as_alt'] = macos_option_as_alt(val) - def macos_quit_when_last_window_closed(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_quit_when_last_window_closed(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_quit_when_last_window_closed'] = to_bool(val) - def macos_show_window_title_in(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_show_window_title_in(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_macos_show_window_title_in: raise ValueError(f"The value {val} is not a valid choice for macos_show_window_title_in") @@ -1094,61 +1094,61 @@ class Parser: choices_for_macos_show_window_title_in = frozenset(('all', 'menubar', 'none', 'window')) - def macos_thicken_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_thicken_font(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_thicken_font'] = positive_float(val) - def macos_titlebar_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_titlebar_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_titlebar_color'] = macos_titlebar_color(val) - def macos_traditional_fullscreen(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_traditional_fullscreen(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_traditional_fullscreen'] = to_bool(val) - def macos_window_resizable(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_window_resizable(self, val: str, ans: dict[str, typing.Any]) -> None: ans['macos_window_resizable'] = to_bool(val) - def mark1_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mark1_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mark1_background'] = to_color(val) - def mark1_foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mark1_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mark1_foreground'] = to_color(val) - def mark2_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mark2_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mark2_background'] = to_color(val) - def mark2_foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mark2_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mark2_foreground'] = to_color(val) - def mark3_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mark3_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mark3_background'] = to_color(val) - def mark3_foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mark3_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mark3_foreground'] = to_color(val) - def menu_map(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def menu_map(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in menu_map(val, ans["menu_map"]): ans["menu_map"][k] = v - def modify_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def modify_font(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in modify_font(val): ans["modify_font"][k] = v - def mouse_hide_wait(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mouse_hide_wait(self, val: str, ans: dict[str, typing.Any]) -> None: ans['mouse_hide_wait'] = float(val) - def narrow_symbols(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def narrow_symbols(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in narrow_symbols(val): ans["narrow_symbols"][k] = v - def notify_on_cmd_finish(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def notify_on_cmd_finish(self, val: str, ans: dict[str, typing.Any]) -> None: ans['notify_on_cmd_finish'] = notify_on_cmd_finish(val) - def open_url_with(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def open_url_with(self, val: str, ans: dict[str, typing.Any]) -> None: ans['open_url_with'] = to_cmdline(val) - def paste_actions(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def paste_actions(self, val: str, ans: dict[str, typing.Any]) -> None: ans['paste_actions'] = paste_actions(val) - def placement_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def placement_strategy(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_placement_strategy: raise ValueError(f"The value {val} is not a valid choice for placement_strategy") @@ -1156,7 +1156,7 @@ class Parser: choices_for_placement_strategy = frozenset(('top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right')) - def pointer_shape_when_dragging(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def pointer_shape_when_dragging(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_pointer_shape_when_dragging: raise ValueError(f"The value {val} is not a valid choice for pointer_shape_when_dragging") @@ -1164,7 +1164,7 @@ class Parser: choices_for_pointer_shape_when_dragging = choices_for_default_pointer_shape - def pointer_shape_when_grabbed(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def pointer_shape_when_grabbed(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_pointer_shape_when_grabbed: raise ValueError(f"The value {val} is not a valid choice for pointer_shape_when_grabbed") @@ -1172,68 +1172,68 @@ class Parser: choices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape - def remember_window_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def remember_window_size(self, val: str, ans: dict[str, typing.Any]) -> None: ans['remember_window_size'] = to_bool(val) - def remote_control_password(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def remote_control_password(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in remote_control_password(val, ans["remote_control_password"]): ans["remote_control_password"][k] = v - def repaint_delay(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def repaint_delay(self, val: str, ans: dict[str, typing.Any]) -> None: ans['repaint_delay'] = positive_int(val) - def resize_debounce_time(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def resize_debounce_time(self, val: str, ans: dict[str, typing.Any]) -> None: ans['resize_debounce_time'] = resize_debounce_time(val) - def resize_in_steps(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def resize_in_steps(self, val: str, ans: dict[str, typing.Any]) -> None: ans['resize_in_steps'] = to_bool(val) - def scrollback_fill_enlarged_window(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def scrollback_fill_enlarged_window(self, val: str, ans: dict[str, typing.Any]) -> None: ans['scrollback_fill_enlarged_window'] = to_bool(val) - def scrollback_indicator_opacity(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def scrollback_indicator_opacity(self, val: str, ans: dict[str, typing.Any]) -> None: ans['scrollback_indicator_opacity'] = unit_float(val) - def scrollback_lines(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def scrollback_lines(self, val: str, ans: dict[str, typing.Any]) -> None: ans['scrollback_lines'] = scrollback_lines(val) - def scrollback_pager(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def scrollback_pager(self, val: str, ans: dict[str, typing.Any]) -> None: ans['scrollback_pager'] = to_cmdline(val) - def scrollback_pager_history_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def scrollback_pager_history_size(self, val: str, ans: dict[str, typing.Any]) -> None: ans['scrollback_pager_history_size'] = scrollback_pager_history_size(val) - def select_by_word_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def select_by_word_characters(self, val: str, ans: dict[str, typing.Any]) -> None: ans['select_by_word_characters'] = str(val) - def select_by_word_characters_forward(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def select_by_word_characters_forward(self, val: str, ans: dict[str, typing.Any]) -> None: ans['select_by_word_characters_forward'] = str(val) - def selection_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def selection_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['selection_background'] = to_color_or_none(val) - def selection_foreground(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def selection_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: ans['selection_foreground'] = to_color_or_none(val) - def shell(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def shell(self, val: str, ans: dict[str, typing.Any]) -> None: ans['shell'] = str(val) - def shell_integration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def shell_integration(self, val: str, ans: dict[str, typing.Any]) -> None: ans['shell_integration'] = shell_integration(val) - def show_hyperlink_targets(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def show_hyperlink_targets(self, val: str, ans: dict[str, typing.Any]) -> None: ans['show_hyperlink_targets'] = to_bool(val) - def single_window_margin_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def single_window_margin_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['single_window_margin_width'] = optional_edge_width(val) - def single_window_padding_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def single_window_padding_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['single_window_padding_width'] = optional_edge_width(val) - def startup_session(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def startup_session(self, val: str, ans: dict[str, typing.Any]) -> None: ans['startup_session'] = config_or_absolute_path(val) - def strip_trailing_spaces(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def strip_trailing_spaces(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_strip_trailing_spaces: raise ValueError(f"The value {val} is not a valid choice for strip_trailing_spaces") @@ -1241,17 +1241,17 @@ class Parser: choices_for_strip_trailing_spaces = frozenset(('always', 'never', 'smart')) - def symbol_map(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def symbol_map(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in symbol_map(val): ans["symbol_map"][k] = v - def sync_to_monitor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def sync_to_monitor(self, val: str, ans: dict[str, typing.Any]) -> None: ans['sync_to_monitor'] = to_bool(val) - def tab_activity_symbol(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_activity_symbol(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_activity_symbol'] = tab_activity_symbol(val) - def tab_bar_align(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_tab_bar_align: raise ValueError(f"The value {val} is not a valid choice for tab_bar_align") @@ -1259,25 +1259,25 @@ class Parser: choices_for_tab_bar_align = frozenset(('left', 'center', 'right')) - def tab_bar_background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_background'] = to_color_or_none(val) - def tab_bar_edge(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_edge(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_edge'] = tab_bar_edge(val) - def tab_bar_margin_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_margin_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_margin_color'] = to_color_or_none(val) - def tab_bar_margin_height(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_margin_height(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_margin_height'] = tab_bar_margin_height(val) - def tab_bar_margin_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_margin_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_margin_width'] = positive_float(val) - def tab_bar_min_tabs(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_min_tabs(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_min_tabs'] = tab_bar_min_tabs(val) - def tab_bar_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_bar_style(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_tab_bar_style: raise ValueError(f"The value {val} is not a valid choice for tab_bar_style") @@ -1285,10 +1285,10 @@ class Parser: choices_for_tab_bar_style = frozenset(('fade', 'hidden', 'powerline', 'separator', 'slant', 'custom')) - def tab_fade(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_fade(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_fade'] = tab_fade(val) - def tab_powerline_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_powerline_style(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_tab_powerline_style: raise ValueError(f"The value {val} is not a valid choice for tab_powerline_style") @@ -1296,10 +1296,10 @@ class Parser: choices_for_tab_powerline_style = frozenset(('angled', 'round', 'slanted')) - def tab_separator(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_separator(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_separator'] = tab_separator(val) - def tab_switch_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_switch_strategy(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_tab_switch_strategy: raise ValueError(f"The value {val} is not a valid choice for tab_switch_strategy") @@ -1307,16 +1307,16 @@ class Parser: choices_for_tab_switch_strategy = frozenset(('last', 'left', 'previous', 'right')) - def tab_title_max_length(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_title_max_length(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_title_max_length'] = positive_int(val) - def tab_title_template(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def tab_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_title_template'] = tab_title_template(val) - def term(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def term(self, val: str, ans: dict[str, typing.Any]) -> None: ans['term'] = str(val) - def terminfo_type(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def terminfo_type(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_terminfo_type: raise ValueError(f"The value {val} is not a valid choice for terminfo_type") @@ -1324,19 +1324,19 @@ class Parser: choices_for_terminfo_type = frozenset(('path', 'direct', 'none')) - def text_composition_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def text_composition_strategy(self, val: str, ans: dict[str, typing.Any]) -> None: ans['text_composition_strategy'] = str(val) - def text_fg_override_threshold(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def text_fg_override_threshold(self, val: str, ans: dict[str, typing.Any]) -> None: ans['text_fg_override_threshold'] = float(val) - def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def touch_scroll_multiplier(self, val: str, ans: dict[str, typing.Any]) -> None: ans['touch_scroll_multiplier'] = float(val) - def transparent_background_colors(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def transparent_background_colors(self, val: str, ans: dict[str, typing.Any]) -> None: ans['transparent_background_colors'] = transparent_background_colors(val) - def undercurl_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def undercurl_style(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_undercurl_style: raise ValueError(f"The value {val} is not a valid choice for undercurl_style") @@ -1344,10 +1344,10 @@ class Parser: choices_for_undercurl_style = frozenset(('thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense')) - def underline_exclusion(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def underline_exclusion(self, val: str, ans: dict[str, typing.Any]) -> None: ans['underline_exclusion'] = underline_exclusion(val) - def underline_hyperlinks(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def underline_hyperlinks(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_underline_hyperlinks: raise ValueError(f"The value {val} is not a valid choice for underline_hyperlinks") @@ -1355,59 +1355,59 @@ class Parser: choices_for_underline_hyperlinks = frozenset(('hover', 'always', 'never')) - def update_check_interval(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def update_check_interval(self, val: str, ans: dict[str, typing.Any]) -> None: ans['update_check_interval'] = float(val) - def url_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def url_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['url_color'] = to_color(val) - def url_excluded_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def url_excluded_characters(self, val: str, ans: dict[str, typing.Any]) -> None: ans['url_excluded_characters'] = python_string(val) - def url_prefixes(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def url_prefixes(self, val: str, ans: dict[str, typing.Any]) -> None: ans['url_prefixes'] = url_prefixes(val) - def url_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def url_style(self, val: str, ans: dict[str, typing.Any]) -> None: ans['url_style'] = url_style(val) - def visual_bell_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def visual_bell_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['visual_bell_color'] = to_color_or_none(val) - def visual_bell_duration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def visual_bell_duration(self, val: str, ans: dict[str, typing.Any]) -> None: ans['visual_bell_duration'] = visual_bell_duration(val) - def visual_window_select_characters(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def visual_window_select_characters(self, val: str, ans: dict[str, typing.Any]) -> None: ans['visual_window_select_characters'] = visual_window_select_characters(val) - def watcher(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def watcher(self, val: str, ans: dict[str, typing.Any]) -> None: for k, v in store_multiple(val, ans["watcher"]): ans["watcher"][k] = v - def wayland_enable_ime(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def wayland_enable_ime(self, val: str, ans: dict[str, typing.Any]) -> None: ans['wayland_enable_ime'] = to_bool(val) - def wayland_titlebar_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def wayland_titlebar_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['wayland_titlebar_color'] = titlebar_color(val) - def wheel_scroll_min_lines(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def wheel_scroll_min_lines(self, val: str, ans: dict[str, typing.Any]) -> None: ans['wheel_scroll_min_lines'] = int(val) - def wheel_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def wheel_scroll_multiplier(self, val: str, ans: dict[str, typing.Any]) -> None: ans['wheel_scroll_multiplier'] = float(val) - def window_alert_on_bell(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_alert_on_bell(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_alert_on_bell'] = to_bool(val) - def window_border_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_border_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_border_width'] = window_border_width(val) - def window_logo_alpha(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_logo_alpha(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_logo_alpha'] = unit_float(val) - def window_logo_path(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_logo_path(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_logo_path'] = config_or_absolute_path(val) - def window_logo_position(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_logo_position(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_window_logo_position: raise ValueError(f"The value {val} is not a valid choice for window_logo_position") @@ -1415,52 +1415,52 @@ class Parser: choices_for_window_logo_position = choices_for_placement_strategy - def window_logo_scale(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_logo_scale(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_logo_scale'] = window_logo_scale(val) - def window_margin_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_margin_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_margin_width'] = edge_width(val) - def window_padding_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_padding_width(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_padding_width'] = edge_width(val) - def window_resize_step_cells(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_resize_step_cells(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_resize_step_cells'] = positive_int(val) - def window_resize_step_lines(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def window_resize_step_lines(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_resize_step_lines'] = positive_int(val) - def x11_hide_window_decorations(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def x11_hide_window_decorations(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_hide_window_decorations_aliases('x11_hide_window_decorations', val, ans) - def macos_hide_titlebar(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_hide_titlebar(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_hide_window_decorations_aliases('macos_hide_titlebar', val, ans) - def macos_show_window_title_in_menubar(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def macos_show_window_title_in_menubar(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_macos_show_window_title_in_menubar_alias('macos_show_window_title_in_menubar', val, ans) - def send_text(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def send_text(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_send_text('send_text', val, ans) - def adjust_line_height(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def adjust_line_height(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_adjust_line_height('adjust_line_height', val, ans) - def adjust_column_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def adjust_column_width(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_adjust_line_height('adjust_column_width', val, ans) - def adjust_baseline(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def adjust_baseline(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_adjust_line_height('adjust_baseline', val, ans) - def map(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def map(self, val: str, ans: dict[str, typing.Any]) -> None: for k in parse_map(val): ans['map'].append(k) - def mouse_map(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + def mouse_map(self, val: str, ans: dict[str, typing.Any]) -> None: for k in parse_mouse_map(val): ans['mouse_map'].append(k) -def create_result_dict() -> typing.Dict[str, typing.Any]: +def create_result_dict() -> dict[str, typing.Any]: return { 'action_alias': {}, 'env': {}, @@ -1479,10 +1479,10 @@ def create_result_dict() -> typing.Dict[str, typing.Any]: } -actions: typing.FrozenSet[str] = frozenset(('map', 'mouse_map')) +actions: frozenset[str] = frozenset(('map', 'mouse_map')) -def merge_result_dicts(defaults: typing.Dict[str, typing.Any], vals: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: +def merge_result_dicts(defaults: dict[str, typing.Any], vals: dict[str, typing.Any]) -> dict[str, typing.Any]: ans = {} for k, v in defaults.items(): if isinstance(v, dict): @@ -1497,7 +1497,7 @@ def merge_result_dicts(defaults: typing.Dict[str, typing.Any], vals: typing.Dict parser = Parser() -def parse_conf_item(key: str, val: str, ans: typing.Dict[str, typing.Any]) -> bool: +def parse_conf_item(key: str, val: str, ans: dict[str, typing.Any]) -> bool: func = getattr(parser, key, None) if func is not None: func(val, ans) diff --git a/kitty/options/types.py b/kitty/options/types.py index bfc03e77c..e599e5d80 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -476,17 +476,17 @@ option_names = ( class Options: - active_border_color: typing.Optional[kitty.fast_data_types.Color] = Color(0, 255, 0) + active_border_color: kitty.fast_data_types.Color | None = Color(0, 255, 0) active_tab_background: Color = Color(238, 238, 238) - active_tab_font_style: typing.Tuple[bool, bool] = (True, True) + active_tab_font_style: tuple[bool, bool] = (True, True) active_tab_foreground: Color = Color(0, 0, 0) - active_tab_title_template: typing.Optional[str] = None + active_tab_title_template: str | None = None allow_cloning: choices_for_allow_cloning = 'ask' allow_hyperlinks: int = 1 allow_remote_control: choices_for_allow_remote_control = 'no' background: Color = Color(0, 0, 0) background_blur: int = 0 - background_image: typing.Optional[str] = None + background_image: str | None = None background_image_layout: choices_for_background_image_layout = 'tiled' background_image_linear: bool = False background_opacity: float = 1.0 @@ -494,29 +494,29 @@ class Options: background_tint_gaps: float = 1.0 bell_border_color: Color = Color(255, 90, 0) bell_on_tab: str = '🔔 ' - bell_path: typing.Optional[str] = None + bell_path: str | None = None bold_font: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='auto', axes=(), variable_name=None, features=(), created_from_string='auto') bold_italic_font: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='auto', axes=(), variable_name=None, features=(), created_from_string='auto') - box_drawing_scale: typing.Tuple[float, float, float, float] = (0.001, 1.0, 1.5, 2.0) + box_drawing_scale: tuple[float, float, float, float] = (0.001, 1.0, 1.5, 2.0) clear_all_mouse_actions: bool = False clear_all_shortcuts: bool = False click_interval: float = -1.0 - clipboard_control: typing.Tuple[str, ...] = ('write-clipboard', 'write-primary', 'read-clipboard-ask', 'read-primary-ask') + clipboard_control: tuple[str, ...] = ('write-clipboard', 'write-primary', 'read-clipboard-ask', 'read-primary-ask') clipboard_max_size: float = 512.0 - clone_source_strategies: typing.FrozenSet[str] = frozenset({'conda', 'env_var', 'path', 'venv'}) + clone_source_strategies: frozenset[str] = frozenset({'conda', 'env_var', 'path', 'venv'}) close_on_child_death: bool = False - command_on_bell: typing.List[str] = ['none'] + command_on_bell: list[str] = ['none'] confirm_os_window_close: int = -1 copy_on_select: str = '' - cursor: typing.Optional[kitty.fast_data_types.Color] = Color(204, 204, 204) + cursor: kitty.fast_data_types.Color | None = Color(204, 204, 204) cursor_beam_thickness: float = 1.5 - cursor_blink_interval: typing.Tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (-1.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction()) + cursor_blink_interval: tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (-1.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction()) cursor_shape: int = 1 cursor_shape_unfocused: int = 4 cursor_stop_blinking_after: float = 15.0 - cursor_text_color: typing.Optional[kitty.fast_data_types.Color] = Color(17, 17, 17) + cursor_text_color: kitty.fast_data_types.Color | None = Color(17, 17, 17) cursor_trail: int = 0 - cursor_trail_decay: typing.Tuple[float, float] = (0.1, 0.4) + cursor_trail_decay: tuple[float, float] = (0.1, 0.4) cursor_trail_start_threshold: int = 2 cursor_underline_thickness: float = 2.0 default_pointer_shape: choices_for_default_pointer_shape = 'beam' @@ -527,7 +527,7 @@ class Options: dynamic_background_opacity: bool = False editor: str = '.' enable_audio_bell: bool = True - enabled_layouts: typing.List[str] = ['fat', 'grid', 'horizontal', 'splits', 'stack', 'tall', 'vertical'] + enabled_layouts: list[str] = ['fat', 'grid', 'horizontal', 'splits', 'stack', 'tall', 'vertical'] file_transfer_confirmation_bypass: str = '' focus_follows_mouse: bool = False font_family: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='monospace', axes=(), variable_name=None, features=(), created_from_string='monospace') @@ -538,11 +538,11 @@ class Options: hide_window_decorations: int = 0 inactive_border_color: Color = Color(204, 204, 204) inactive_tab_background: Color = Color(153, 153, 153) - inactive_tab_font_style: typing.Tuple[bool, bool] = (False, False) + inactive_tab_font_style: tuple[bool, bool] = (False, False) inactive_tab_foreground: Color = Color(68, 68, 68) inactive_text_alpha: float = 1.0 - initial_window_height: typing.Tuple[int, str] = (400, 'px') - initial_window_width: typing.Tuple[int, str] = (640, 'px') + initial_window_height: tuple[int, str] = (400, 'px') + initial_window_width: tuple[int, str] = (640, 'px') input_delay: int = 3 italic_font: FontSpec = FontSpec(family=None, style=None, postscript_name=None, full_name=None, system='auto', axes=(), variable_name=None, features=(), created_from_string='auto') kitty_mod: int = 5 @@ -568,42 +568,42 @@ class Options: mark3_foreground: Color = Color(0, 0, 0) mouse_hide_wait: float = 0.0 if is_macos else 3.0 notify_on_cmd_finish: NotifyOnCmdFinish = NotifyOnCmdFinish(when='never', duration=5.0, action='notify', cmdline=(), clear_on=('focus', 'next')) - open_url_with: typing.List[str] = ['default'] - paste_actions: typing.FrozenSet[str] = frozenset({'confirm', 'quote-urls-at-prompt'}) + open_url_with: list[str] = ['default'] + paste_actions: frozenset[str] = frozenset({'confirm', 'quote-urls-at-prompt'}) placement_strategy: choices_for_placement_strategy = 'center' pointer_shape_when_dragging: choices_for_pointer_shape_when_dragging = 'beam' pointer_shape_when_grabbed: choices_for_pointer_shape_when_grabbed = 'arrow' remember_window_size: bool = True repaint_delay: int = 10 - resize_debounce_time: typing.Tuple[float, float] = (0.1, 0.5) + resize_debounce_time: tuple[float, float] = (0.1, 0.5) resize_in_steps: bool = False scrollback_fill_enlarged_window: bool = False scrollback_indicator_opacity: float = 1.0 scrollback_lines: int = 2000 - scrollback_pager: typing.List[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER'] + scrollback_pager: list[str] = ['less', '--chop-long-lines', '--RAW-CONTROL-CHARS', '+INPUT_LINE_NUMBER'] scrollback_pager_history_size: int = 0 select_by_word_characters: str = '@-./_~?&=%+#' select_by_word_characters_forward: str = '' - selection_background: typing.Optional[kitty.fast_data_types.Color] = Color(255, 250, 205) - selection_foreground: typing.Optional[kitty.fast_data_types.Color] = Color(0, 0, 0) + selection_background: kitty.fast_data_types.Color | None = Color(255, 250, 205) + selection_foreground: kitty.fast_data_types.Color | None = Color(0, 0, 0) shell: str = '.' - shell_integration: typing.FrozenSet[str] = frozenset({'enabled'}) + shell_integration: frozenset[str] = frozenset({'enabled'}) show_hyperlink_targets: bool = False single_window_margin_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0) single_window_padding_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0) - startup_session: typing.Optional[str] = None + startup_session: str | None = None strip_trailing_spaces: choices_for_strip_trailing_spaces = 'never' sync_to_monitor: bool = True tab_activity_symbol: str = '' tab_bar_align: choices_for_tab_bar_align = 'left' - tab_bar_background: typing.Optional[kitty.fast_data_types.Color] = None + tab_bar_background: kitty.fast_data_types.Color | None = None tab_bar_edge: int = 3 - tab_bar_margin_color: typing.Optional[kitty.fast_data_types.Color] = None + tab_bar_margin_color: kitty.fast_data_types.Color | None = None tab_bar_margin_height: TabBarMarginHeight = TabBarMarginHeight(outer=0, inner=0) tab_bar_margin_width: float = 0 tab_bar_min_tabs: int = 2 tab_bar_style: choices_for_tab_bar_style = 'fade' - tab_fade: typing.Tuple[float, ...] = (0.25, 0.5, 0.75, 1.0) + tab_fade: tuple[float, ...] = (0.25, 0.5, 0.75, 1.0) tab_powerline_style: choices_for_tab_powerline_style = 'angled' tab_separator: str = ' ┇' tab_switch_strategy: choices_for_tab_switch_strategy = 'previous' @@ -614,48 +614,48 @@ class Options: text_composition_strategy: str = 'platform' text_fg_override_threshold: float = 0.0 touch_scroll_multiplier: float = 1.0 - transparent_background_colors: typing.Tuple[typing.Tuple[kitty.fast_data_types.Color, float], ...] = () + transparent_background_colors: tuple[tuple[kitty.fast_data_types.Color, float], ...] = () undercurl_style: choices_for_undercurl_style = 'thin-sparse' underline_exclusion: tuple[float, typing.Literal['', 'px', 'pt']] = (1.0, '') underline_hyperlinks: choices_for_underline_hyperlinks = 'hover' update_check_interval: float = 24.0 url_color: Color = Color(0, 135, 189) url_excluded_characters: str = '' - url_prefixes: typing.Tuple[str, ...] = ('file', 'ftp', 'ftps', 'gemini', 'git', 'gopher', 'http', 'https', 'irc', 'ircs', 'kitty', 'mailto', 'news', 'sftp', 'ssh') + url_prefixes: tuple[str, ...] = ('file', 'ftp', 'ftps', 'gemini', 'git', 'gopher', 'http', 'https', 'irc', 'ircs', 'kitty', 'mailto', 'news', 'sftp', 'ssh') url_style: int = 3 - visual_bell_color: typing.Optional[kitty.fast_data_types.Color] = None - visual_bell_duration: typing.Tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (0.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction()) + visual_bell_color: kitty.fast_data_types.Color | None = None + visual_bell_duration: tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (0.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction()) visual_window_select_characters: str = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ' wayland_enable_ime: bool = True wayland_titlebar_color: int = 0 wheel_scroll_min_lines: int = 1 wheel_scroll_multiplier: float = 5.0 window_alert_on_bell: bool = True - window_border_width: typing.Tuple[float, str] = (0.5, 'pt') + window_border_width: tuple[float, str] = (0.5, 'pt') window_logo_alpha: float = 0.5 - window_logo_path: typing.Optional[str] = None + window_logo_path: str | None = None window_logo_position: choices_for_window_logo_position = 'bottom-right' - window_logo_scale: typing.Tuple[float, float] = (0, -1.0) + window_logo_scale: tuple[float, float] = (0, -1.0) window_margin_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0) window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0) window_resize_step_cells: int = 2 window_resize_step_lines: int = 2 - action_alias: typing.Dict[str, str] = {} - env: typing.Dict[str, str] = {} - exe_search_path: typing.Dict[str, str] = {} - filter_notification: typing.Dict[str, str] = {} - font_features: typing.Dict[str, typing.Tuple[kitty.fast_data_types.ParsedFontFeature, ...]] = {} - kitten_alias: typing.Dict[str, str] = {} - menu_map: typing.Dict[typing.Tuple[str, ...], str] = {} - modify_font: typing.Dict[str, kitty.fonts.FontModification] = {} - narrow_symbols: typing.Dict[typing.Tuple[int, int], int] = {} - remote_control_password: typing.Dict[str, typing.Sequence[str]] = {} - symbol_map: typing.Dict[typing.Tuple[int, int], str] = {} - watcher: typing.Dict[str, str] = {} - map: typing.List[kitty.options.utils.KeyDefinition] = [] + action_alias: dict[str, str] = {} + env: dict[str, str] = {} + exe_search_path: dict[str, str] = {} + filter_notification: dict[str, str] = {} + font_features: dict[str, tuple[kitty.fast_data_types.ParsedFontFeature, ...]] = {} + kitten_alias: dict[str, str] = {} + menu_map: dict[tuple[str, ...], str] = {} + modify_font: dict[str, kitty.fonts.FontModification] = {} + narrow_symbols: dict[tuple[int, int], int] = {} + remote_control_password: dict[str, typing.Sequence[str]] = {} + symbol_map: dict[tuple[int, int], str] = {} + watcher: dict[str, str] = {} + map: list[kitty.options.utils.KeyDefinition] = [] keyboard_modes: KeyboardModeMap = {} alias_map: AliasMap = AliasMap() - mouse_map: typing.List[kitty.options.utils.MouseMapping] = [] + mouse_map: list[kitty.options.utils.MouseMapping] = [] mousemap: MouseMap = {} color_table: "array[int]" = array("L", ( 0x000000, 0xcc0403, 0x19cb00, 0xcecb00, 0x0d73cc, 0xcb1ed1, 0x0dcdcd, 0xdddddd, @@ -691,11 +691,11 @@ class Options: 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee, )) - config_paths: typing.Tuple[str, ...] = () - all_config_paths: typing.Tuple[str, ...] = () - config_overrides: typing.Tuple[str, ...] = () + config_paths: tuple[str, ...] = () + all_config_paths: tuple[str, ...] = () + config_overrides: tuple[str, ...] = () - def __init__(self, options_dict: typing.Optional[typing.Dict[str, typing.Any]] = None) -> None: + def __init__(self, options_dict: dict[str, typing.Any] | None = None) -> None: self.color_table = array(self.color_table.typecode, self.color_table) if options_dict is not None: null = object() @@ -705,7 +705,7 @@ class Options: setattr(self, key, val) @property - def _fields(self) -> typing.Tuple[str, ...]: + def _fields(self) -> tuple[str, ...]: return option_names def __iter__(self) -> typing.Iterator[str]: @@ -722,7 +722,7 @@ class Options: ans = ans[:] return ans - def _asdict(self) -> typing.Dict[str, typing.Any]: + def _asdict(self) -> dict[str, typing.Any]: return {k: self._copy_of_val(k) for k in self} def _replace(self, **kw: typing.Any) -> "Options": @@ -733,7 +733,7 @@ class Options: setattr(ans, name, val) return ans - def __getitem__(self, key: typing.Union[int, str]) -> typing.Any: + def __getitem__(self, key: int | str) -> typing.Any: k = option_names[key] if isinstance(key, int) else key try: return getattr(self, k) diff --git a/kitty/options/utils.py b/kitty/options/utils.py index 95b6ab91e..ac309b19d 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -6,26 +6,16 @@ import enum import re import sys from collections import defaultdict +from collections.abc import Callable, Container, Iterable, Iterator, Sequence from contextlib import suppress from dataclasses import dataclass, fields from functools import lru_cache from typing import ( Any, - Callable, - Container, - Dict, - FrozenSet, Generic, - Iterable, - Iterator, - List, Literal, NamedTuple, - Optional, - Sequence, - Tuple, TypeVar, - Union, cast, get_args, ) @@ -54,20 +44,20 @@ from kitty.rgb import color_as_int from kitty.types import FloatEdges, MouseEvent from kitty.utils import expandvars, log_error, resolve_abs_or_config_path, shlex_split -KeyMap = Dict[SingleKey, List['KeyDefinition']] -MouseMap = Dict[MouseEvent, str] -KeySequence = Tuple[SingleKey, ...] +KeyMap = dict[SingleKey, list['KeyDefinition']] +MouseMap = dict[MouseEvent, str] +KeySequence = tuple[SingleKey, ...] MINIMUM_FONT_SIZE = 4 default_tab_separator = ' ┇' mod_map = {'⌃': 'CONTROL', 'CTRL': 'CONTROL', '⇧': 'SHIFT', '⌥': 'ALT', 'OPTION': 'ALT', 'OPT': 'ALT', '⌘': 'SUPER', 'COMMAND': 'SUPER', 'CMD': 'SUPER', 'KITTY_MOD': 'KITTY'} -character_key_name_aliases_with_ascii_lowercase: Dict[str, str] = character_key_name_aliases.copy() +character_key_name_aliases_with_ascii_lowercase: dict[str, str] = character_key_name_aliases.copy() for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': character_key_name_aliases_with_ascii_lowercase[x] = x.lower() sequence_sep = '>' mouse_button_map = {'left': 'b1', 'middle': 'b3', 'right': 'b2'} mouse_trigger_count_map = {'doubleclick': -3, 'click': -2, 'release': -1, 'press': 1, 'doublepress': 2, 'triplepress': 3} -FuncArgsType = Tuple[str, Sequence[Any]] +FuncArgsType = tuple[str, Sequence[Any]] func_with_args = KeyFuncWrapper[FuncArgsType]() DELETE_ENV_VAR = '_delete_this_env_var_' @@ -200,13 +190,13 @@ def signal_child_parse(func: str, rest: str) -> FuncArgsType: @func_with_args('change_font_size') -def parse_change_font_size(func: str, rest: str) -> Tuple[str, Tuple[bool, Optional[str], float]]: +def parse_change_font_size(func: str, rest: str) -> tuple[str, tuple[bool, str | None, float]]: vals = rest.strip().split(maxsplit=1) if len(vals) != 2: log_error(f'Invalid change_font_size specification: {rest}, treating it as default') return func, (True, None, 0) c_all = vals[0].lower() == 'all' - sign: Optional[str] = None + sign: str | None = None amt = vals[1] if amt[0] in '+-': sign = amt[0] @@ -284,7 +274,7 @@ def resize_window(func: str, rest: str) -> FuncArgsType: def move_window(func: str, rest: str) -> FuncArgsType: rest = rest.lower() rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest) - prest: Union[int, str] = rest + prest: int | str = rest try: prest = int(prest) except Exception: @@ -370,7 +360,7 @@ def layout_action(func: str, rest: str) -> FuncArgsType: return func, [parts[0], tuple(parts[1:])] -def parse_marker_spec(ftype: str, parts: Sequence[str]) -> Tuple[str, Union[str, Tuple[Tuple[int, str], ...]], int]: +def parse_marker_spec(ftype: str, parts: Sequence[str]) -> tuple[str, str | tuple[tuple[int, str], ...], int]: flags = re.UNICODE if ftype in ('text', 'itext', 'regex', 'iregex'): if ftype.startswith('i'): @@ -388,7 +378,7 @@ def parse_marker_spec(ftype: str, parts: Sequence[str]) -> Tuple[str, Union[str, sspec = re.escape(sspec) ans.append((color, sspec)) ftype = 'regex' - spec: Union[str, Tuple[Tuple[int, str], ...]] = tuple(ans) + spec: str | tuple[tuple[int, str], ...] = tuple(ans) elif ftype == 'function': spec = ' '.join(parts) else: @@ -446,7 +436,7 @@ def load_config_file(func: str, rest: str) -> FuncArgsType: # }}} -def parse_mods(parts: Iterable[str], sc: str) -> Optional[int]: +def parse_mods(parts: Iterable[str], sc: str) -> int | None: def map_mod(m: str) -> str: return mod_map.get(m, m) @@ -492,7 +482,7 @@ def parse_shortcut(sc: str) -> SingleKey: except Exception: uq = q.upper() uq = functional_key_name_aliases.get(uq, uq) - x: Optional[int] = getattr(defines, f'GLFW_FKEY_{uq}', None) + x: int | None = getattr(defines, f'GLFW_FKEY_{uq}', None) if x is None: lf = get_key_name_lookup() key = lf(q, False) or 0 @@ -512,14 +502,14 @@ def disable_ligatures(x: str) -> int: return cmap.get(x.lower(), 0) -def box_drawing_scale(x: str) -> Tuple[float, float, float, float]: +def box_drawing_scale(x: str) -> tuple[float, float, float, float]: ans = tuple(float(q.strip()) for q in x.split(',')) if len(ans) != 4: raise ValueError('Invalid box_drawing scale, must have four entries') return ans[0], ans[1], ans[2], ans[3] -def cursor_text_color(x: str) -> Optional[Color]: +def cursor_text_color(x: str) -> Color | None: if x.lower() == 'background': return None return to_color(x) @@ -560,7 +550,7 @@ def to_cursor_unfocused_shape(x: str) -> int: ) ) -def cursor_trail_decay(x: str) -> Tuple[float, float]: +def cursor_trail_decay(x: str) -> tuple[float, float]: fast, slow = map(positive_float, x.split()) slow = max(slow, fast) return fast, slow @@ -586,7 +576,7 @@ def url_style(x: str) -> int: return url_style_map.get(x, url_style_map['curly']) -def url_prefixes(x: str) -> Tuple[str, ...]: +def url_prefixes(x: str) -> tuple[str, ...]: return tuple(a.lower() for a in x.replace(',', ' ').split()) @@ -600,13 +590,13 @@ def copy_on_select(raw: str) -> str: return raw -def window_size(val: str) -> Tuple[int, str]: +def window_size(val: str) -> tuple[int, str]: val = val.lower() unit = 'cells' if val.endswith('c') else 'px' return positive_int(val.rstrip('c')), unit -def parse_layout_names(parts: Iterable[str]) -> List[str]: +def parse_layout_names(parts: Iterable[str]) -> list[str]: from kitty.layout.interface import all_layouts ans = [] for p in parts: @@ -621,11 +611,11 @@ def parse_layout_names(parts: Iterable[str]) -> List[str]: return uniq(ans) -def to_layout_names(raw: str) -> List[str]: +def to_layout_names(raw: str) -> list[str]: return parse_layout_names(x.strip() for x in raw.split(',')) -def window_border_width(x: Union[str, int, float]) -> Tuple[float, str]: +def window_border_width(x: str | int | float) -> tuple[float, str]: unit = 'pt' if isinstance(x, str): trailer = x[-2:] @@ -675,14 +665,14 @@ def resize_draw_strategy(x: str) -> int: return cmap.get(x.lower(), 0) -def window_logo_scale(x: str) -> Tuple[float, float]: +def window_logo_scale(x: str) -> tuple[float, float]: parts = x.split(maxsplit=1) if len(parts) == 1: return positive_float(parts[0]), -1.0 return positive_float(parts[0]), positive_float(parts[1]) -def resize_debounce_time(x: str) -> Tuple[float, float]: +def resize_debounce_time(x: str) -> tuple[float, float]: parts = x.split(maxsplit=1) if len(parts) == 1: return positive_float(parts[0]), 0.5 @@ -717,7 +707,7 @@ def tab_bar_edge(x: str) -> int: return {'top': 1, 'bottom': 3}.get(x.lower(), 3) -def tab_font_style(x: str) -> Tuple[bool, bool]: +def tab_font_style(x: str) -> tuple[bool, bool]: return { 'bold-italic': (True, True), 'bold': (True, False), @@ -729,7 +719,7 @@ def tab_bar_min_tabs(x: str) -> int: return max(1, positive_int(x)) -def tab_fade(x: str) -> Tuple[float, ...]: +def tab_fade(x: str) -> tuple[float, ...]: return tuple(map(unit_float, x.split())) @@ -757,7 +747,7 @@ def tab_title_template(x: str) -> str: return x -def active_tab_title_template(x: str) -> Optional[str]: +def active_tab_title_template(x: str) -> str | None: x = tab_title_template(x) return None if x == 'none' else x @@ -771,7 +761,7 @@ class NotifyOnCmdFinish(NamedTuple): when: str = 'never' duration: float = 5.0 action: str = 'notify' - cmdline: Tuple[str, ...] = () + cmdline: tuple[str, ...] = () clear_on: tuple[ClearOn, ...] = default_clear_on @@ -784,7 +774,7 @@ def notify_on_cmd_finish(x: str) -> NotifyOnCmdFinish: if len(parts) > 1: duration = float(parts[1]) action = 'notify' - cmdline: Tuple[str, ...] = () + cmdline: tuple[str, ...] = () clear_on = default_clear_on if len(parts) > 2: if parts[2] not in ('notify', 'bell', 'command'): @@ -807,17 +797,17 @@ def notify_on_cmd_finish(x: str) -> NotifyOnCmdFinish: return NotifyOnCmdFinish(when, duration, action, cmdline, clear_on) -def config_or_absolute_path(x: str, env: Optional[Dict[str, str]] = None) -> Optional[str]: +def config_or_absolute_path(x: str, env: dict[str, str] | None = None) -> str | None: if not x or x.lower() == 'none': return None return resolve_abs_or_config_path(x, env) -def filter_notification(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, str]]: +def filter_notification(val: str, current_val: dict[str, str]) -> Iterable[tuple[str, str]]: yield val, '' -def remote_control_password(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, Sequence[str]]]: +def remote_control_password(val: str, current_val: dict[str, str]) -> Iterable[tuple[str, Sequence[str]]]: val = val.strip() if val: parts = to_cmdline(val, expand=False) @@ -831,7 +821,7 @@ def remote_control_password(val: str, current_val: Dict[str, str]) -> Iterable[T yield parts[0], tuple(parts[1:]) -def clipboard_control(x: str) -> Tuple[str, ...]: +def clipboard_control(x: str) -> tuple[str, ...]: return tuple(x.lower().split()) @@ -893,25 +883,25 @@ def tab_bar_margin_height(x: str) -> TabBarMarginHeight: return TabBarMarginHeight(next(ans), next(ans)) -def clone_source_strategies(x: str) -> FrozenSet[str]: +def clone_source_strategies(x: str) -> frozenset[str]: return frozenset({'venv', 'conda', 'path', 'env_var'} & set(x.lower().split(','))) -def clear_all_mouse_actions(val: str, dict_with_parse_results: Optional[Dict[str, Any]] = None) -> bool: +def clear_all_mouse_actions(val: str, dict_with_parse_results: dict[str, Any] | None = None) -> bool: ans = to_bool(val) if ans and dict_with_parse_results is not None: dict_with_parse_results['mouse_map'] = [None] return ans -def clear_all_shortcuts(val: str, dict_with_parse_results: Optional[Dict[str, Any]] = None) -> bool: +def clear_all_shortcuts(val: str, dict_with_parse_results: dict[str, Any] | None = None) -> bool: ans = to_bool(val) if ans and dict_with_parse_results is not None: dict_with_parse_results['map'] = [None] return ans -def font_features(val: str) -> Iterable[Tuple[str, Tuple[defines.ParsedFontFeature, ...]]]: +def font_features(val: str) -> Iterable[tuple[str, tuple[defines.ParsedFontFeature, ...]]]: if val == 'none': return parts = val.split() @@ -928,13 +918,13 @@ def font_features(val: str) -> Iterable[Tuple[str, Tuple[defines.ParsedFontFeatu yield parts[0], tuple(features) -def modify_font(val: str) -> Iterable[Tuple[str, FontModification]]: +def modify_font(val: str) -> Iterable[tuple[str, FontModification]]: parts = val.split() pos, plen = 0, len(parts) if plen < 2: log_error(f"Ignoring invalid modify_font: {val}") return - mtype: Optional[ModificationType] = getattr(ModificationType, parts[pos], None) + mtype: ModificationType | None = getattr(ModificationType, parts[pos], None) if mtype is None: log_error(f"Ignoring invalid modify_font with unknown modification type: {parts[pos]}") return @@ -966,7 +956,7 @@ def modify_font(val: str) -> Iterable[Tuple[str, FontModification]]: yield key, FontModification(mtype, ModificationValue(mvalue, munit), font_name) -def env(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, str]]: +def env(val: str, current_val: dict[str, str]) -> Iterable[tuple[str, str]]: val = val.strip() if val: if '=' in val: @@ -980,13 +970,13 @@ def env(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, str]]: yield val, DELETE_ENV_VAR -def store_multiple(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]: +def store_multiple(val: str, current_val: Container[str]) -> Iterable[tuple[str, str]]: val = val.strip() if val not in current_val: yield val, val -def menu_map(val: str, current_val: Container[str]) -> Iterable[Tuple[Tuple[str, ...], str]]: +def menu_map(val: str, current_val: Container[str]) -> Iterable[tuple[tuple[str, ...], str]]: parts = val.split(maxsplit=1) if len(parts) != 2: raise ValueError(f'Ignoring invalid menu action: {val}') @@ -1009,7 +999,7 @@ def menu_map(val: str, current_val: Container[str]) -> Iterable[Tuple[Tuple[str, allowed_shell_integration_values = frozenset({'enabled', 'disabled', 'no-rc', 'no-cursor', 'no-title', 'no-prompt-mark', 'no-complete', 'no-cwd', 'no-sudo'}) -def shell_integration(x: str) -> FrozenSet[str]: +def shell_integration(x: str) -> frozenset[str]: q = frozenset(x.lower().split()) if not q.issubset(allowed_shell_integration_values): log_error(f'Invalid shell integration options: {q - allowed_shell_integration_values}, ignoring') @@ -1031,7 +1021,7 @@ def underline_exclusion(x: str) -> tuple[float, Literal['', 'px', 'pt']]: return val, unit -def paste_actions(x: str) -> FrozenSet[str]: +def paste_actions(x: str) -> frozenset[str]: s = frozenset({'quote-urls-at-prompt', 'confirm', 'filter', 'confirm-if-large', 'replace-dangerous-control-codes', 'replace-newline', 'no-op'}) q = frozenset(x.lower().split(',')) if not q.issubset(s): @@ -1039,7 +1029,7 @@ def paste_actions(x: str) -> FrozenSet[str]: return q -def action_alias(val: str) -> Iterable[Tuple[str, str]]: +def action_alias(val: str) -> Iterable[tuple[str, str]]: parts = val.split(maxsplit=1) if len(parts) > 1: alias_name, rest = parts @@ -1049,7 +1039,7 @@ def action_alias(val: str) -> Iterable[Tuple[str, str]]: kitten_alias = action_alias -def symbol_map_parser(val: str, min_size: int = 2) -> Iterable[Tuple[Tuple[int, int], str]]: +def symbol_map_parser(val: str, min_size: int = 2) -> Iterable[tuple[tuple[int, int], str]]: parts = val.split() if len(parts) < min_size: @@ -1070,11 +1060,11 @@ def symbol_map_parser(val: str, min_size: int = 2) -> Iterable[Tuple[Tuple[int, yield (a, b), family -def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]: +def symbol_map(val: str) -> Iterable[tuple[tuple[int, int], str]]: yield from symbol_map_parser(val) -def narrow_symbols(val: str) -> Iterable[Tuple[Tuple[int, int], int]]: +def narrow_symbols(val: str) -> Iterable[tuple[tuple[int, int], int]]: for x, y in symbol_map_parser(val, min_size=1): yield x, int(y or 1) @@ -1101,7 +1091,7 @@ class ActionAlias(NamedTuple): class AliasMap: def __init__(self) -> None: - self.aliases: Dict[str, List[ActionAlias]] = {} + self.aliases: dict[str, list[ActionAlias]] = {} def append(self, name: str, aa: ActionAlias) -> None: self.aliases.setdefault(name, []).append(aa) @@ -1110,11 +1100,11 @@ class AliasMap: self.aliases.update(aa.aliases) @lru_cache(maxsize=256) - def resolve_aliases(self, definition: str, map_type: MapType = MapType.MAP) -> Tuple[KeyAction, ...]: + def resolve_aliases(self, definition: str, map_type: MapType = MapType.MAP) -> tuple[KeyAction, ...]: return tuple(resolve_aliases_and_parse_actions(definition, self.aliases, map_type)) -def build_action_aliases(raw: Dict[str, str], first_arg_replacement: str = '') -> AliasMap: +def build_action_aliases(raw: dict[str, str], first_arg_replacement: str = '') -> AliasMap: ans = AliasMap() if first_arg_replacement: for alias_name, rest in raw.items(): @@ -1126,7 +1116,7 @@ def build_action_aliases(raw: Dict[str, str], first_arg_replacement: str = '') - def resolve_aliases_and_parse_actions( - defn: str, aliases: Dict[str, List[ActionAlias]], map_type: MapType + defn: str, aliases: dict[str, list[ActionAlias]], map_type: MapType ) -> Iterator[KeyAction]: parts = defn.split(maxsplit=1) if len(parts) == 1: @@ -1223,13 +1213,13 @@ T = TypeVar('T') class LiteralField(Generic[T]): - def __init__(self, vals: Tuple[T, ...]): + def __init__(self, vals: tuple[T, ...]): self._vals = vals def __set_name__(self, owner: object, name: str) -> None: self._name = "_" + name - def __get__(self, obj: object, type: Optional[type] = None) -> T: + def __get__(self, obj: object, type: type | None = None) -> T: if obj is None: return self._vals[0] return getattr(obj, self._name, self._vals[0]) @@ -1261,7 +1251,7 @@ class KeyDefinition(BaseDefinition): def __init__( self, is_sequence: bool = False, trigger: SingleKey = SingleKey(), - rest: Tuple[SingleKey, ...] = (), definition: str = '', + rest: tuple[SingleKey, ...] = (), definition: str = '', options: KeyMapOptions = default_key_map_options ): super().__init__(definition) @@ -1275,11 +1265,11 @@ class KeyDefinition(BaseDefinition): return not self.options.when_focus_on and not self.options.mode and not self.options.new_mode and not self.is_sequence @property - def full_key_sequence_to_trigger(self) -> Tuple[SingleKey, ...]: + def full_key_sequence_to_trigger(self) -> tuple[SingleKey, ...]: return (self.trigger,) + self.rest @property - def unique_identity_within_keymap(self) -> Tuple[Tuple[SingleKey, ...], str]: + def unique_identity_within_keymap(self) -> tuple[tuple[SingleKey, ...], str]: return self.full_key_sequence_to_trigger, self.options.when_focus_on def __repr__(self) -> str: @@ -1309,17 +1299,17 @@ class KeyboardMode: on_unknown: OnUnknown = get_args(OnUnknown)[0] on_action : OnAction = get_args(OnAction)[0] - sequence_keys: Optional[List[defines.KeyEvent]] = None + sequence_keys: list[defines.KeyEvent] | None = None def __init__(self, name: str = '') -> None: self.name = name self.keymap: KeyMap = defaultdict(list) -KeyboardModeMap = Dict[str, KeyboardMode] +KeyboardModeMap = dict[str, KeyboardMode] -def parse_options_for_map(val: str) -> Tuple[KeyMapOptions, str]: +def parse_options_for_map(val: str) -> tuple[KeyMapOptions, str]: expecting_arg = '' ans = KeyMapOptions() s = Shlex(val) @@ -1363,8 +1353,8 @@ def parse_map(val: str) -> Iterable[KeyDefinition]: return is_sequence = sequence_sep in sc if is_sequence: - trigger: Optional[SingleKey] = None - restl: List[SingleKey] = [] + trigger: SingleKey | None = None + restl: list[SingleKey] = [] for part in sc.split(sequence_sep): try: mods, is_native, key = parse_shortcut(part) @@ -1447,10 +1437,10 @@ class EasingFunction(NamedTuple): num_steps: int = 0 jump_type: JumpTypes = 'end' - linear_x: Tuple[float, ...] = () - linear_y: Tuple[float, ...] = () + linear_x: tuple[float, ...] = () + linear_y: tuple[float, ...] = () - cubic_bezier_points: Tuple[float, ...] = () + cubic_bezier_points: tuple[float, ...] = () def __repr__(self) -> str: fields = ', '.join(f'{f}={getattr(self, f)!r}' for f in self._fields if getattr(self, f) != self._field_defaults[f]) @@ -1472,8 +1462,8 @@ class EasingFunction(NamedTuple): parts = params.split(',') if len(parts) < 2: raise ValueError('Must specify at least two points for the linear easing function') - xaxis: List[float] = [] - yaxis: List[float] = [] + xaxis: list[float] = [] + yaxis: list[float] = [] def balance(end: float) -> None: extra = len(yaxis) - len(xaxis) @@ -1490,7 +1480,7 @@ class EasingFunction(NamedTuple): for i in range(extra): xaxis.append(i * delta) - def add_point(y: float, x: Optional[float] = None) -> None: + def add_point(y: float, x: float | None = None) -> None: if x is None: yaxis.append(y) else: @@ -1521,7 +1511,7 @@ class EasingFunction(NamedTuple): if len(parts) == 2: n = int(parts[0]) jt = parts[1] - mapping: Dict[str, JumpTypes] = { + mapping: dict[str, JumpTypes] = { 'jump-start': 'start', 'start': 'start', 'end': 'end', 'jump-end': 'end', 'jump-none': 'none', 'jump-both': 'both' } try: @@ -1537,7 +1527,7 @@ class EasingFunction(NamedTuple): return cls(type='steps', jump_type=jump_type, num_steps=n) -def parse_animation(spec: str, interval: float = -1.) -> Tuple[float, EasingFunction, EasingFunction]: +def parse_animation(spec: str, interval: float = -1.) -> tuple[float, EasingFunction, EasingFunction]: with suppress(Exception): interval = float(spec) return interval, EasingFunction(), EasingFunction() @@ -1583,15 +1573,15 @@ def parse_animation(spec: str, interval: float = -1.) -> Tuple[float, EasingFunc return interval, m[0], m[1] -def cursor_blink_interval(spec: str) -> Tuple[float, EasingFunction, EasingFunction]: +def cursor_blink_interval(spec: str) -> tuple[float, EasingFunction, EasingFunction]: return parse_animation(spec) -def visual_bell_duration(spec: str) -> Tuple[float, EasingFunction, EasingFunction]: +def visual_bell_duration(spec: str) -> tuple[float, EasingFunction, EasingFunction]: return parse_animation(spec, interval=0.) -def transparent_background_colors(spec: str) -> Tuple[Tuple[Color, float], ...]: +def transparent_background_colors(spec: str) -> tuple[tuple[Color, float], ...]: if not spec: return () ans: list[tuple[Color, float]] = [] @@ -1608,7 +1598,7 @@ def transparent_background_colors(spec: str) -> Tuple[Tuple[Color, float], ...]: return tuple(ans[:7]) -def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str, Any]) -> None: +def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: dict[str, Any]) -> None: if not hasattr(deprecated_hide_window_decorations_aliases, key): setattr(deprecated_hide_window_decorations_aliases, key, True) log_error(f'The option {key} is deprecated. Use hide_window_decorations instead.') @@ -1617,7 +1607,7 @@ def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str ans['hide_window_decorations'] = True -def deprecated_macos_show_window_title_in_menubar_alias(key: str, val: str, ans: Dict[str, Any]) -> None: +def deprecated_macos_show_window_title_in_menubar_alias(key: str, val: str, ans: dict[str, Any]) -> None: if not hasattr(deprecated_macos_show_window_title_in_menubar_alias, key): setattr(deprecated_macos_show_window_title_in_menubar_alias, 'key', True) log_error(f'The option {key} is deprecated. Use macos_show_window_title_in menubar instead.') @@ -1635,7 +1625,7 @@ def deprecated_macos_show_window_title_in_menubar_alias(key: str, val: str, ans: ans['macos_show_window_title_in'] = macos_show_window_title_in -def deprecated_send_text(key: str, val: str, ans: Dict[str, Any]) -> None: +def deprecated_send_text(key: str, val: str, ans: dict[str, Any]) -> None: parts = val.split(' ') def abort(msg: str) -> None: @@ -1650,7 +1640,7 @@ def deprecated_send_text(key: str, val: str, ans: Dict[str, Any]) -> None: ans['map'].append(k) -def deprecated_adjust_line_height(key: str, x: str, opts_dict: Dict[str, Any]) -> None: +def deprecated_adjust_line_height(key: str, x: str, opts_dict: dict[str, Any]) -> None: fm = {'adjust_line_height': 'cell_height', 'adjust_baseline': 'baseline', 'adjust_column_width': 'cell_width'}[key] mtype = getattr(ModificationType, fm) if x.endswith('%'): diff --git a/kitty/os_window_size.py b/kitty/os_window_size.py index b870a4182..5a9c7ef7f 100644 --- a/kitty/os_window_size.py +++ b/kitty/os_window_size.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import Any, Callable, NamedTuple, Optional, Union +from collections.abc import Callable +from typing import Any, NamedTuple from .constants import is_macos, is_wayland from .fast_data_types import get_options @@ -37,7 +38,7 @@ def sanitize_window_size(x: Any) -> int: return max(20, min(ans, 50000)) -def edge_spacing(which: EdgeLiteral, opts:Optional[Union[WindowSizeData, Options]] = None) -> float: +def edge_spacing(which: EdgeLiteral, opts:WindowSizeData | Options | None = None) -> float: if opts is None: opts = get_options() margin: float = getattr(opts.single_window_margin_width, which) diff --git a/kitty/rc/action.py b/kitty/rc/action.py index dd15ec0e6..8f55db1b5 100644 --- a/kitty/rc/action.py +++ b/kitty/rc/action.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import ( MATCH_WINDOW_OPTION, @@ -57,7 +57,7 @@ using this option means that you will not be notified of failures. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'action': ' '.join(args), 'self': opts.self, 'match_window': opts.match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: w = self.windows_for_match_payload(boss, window, payload_get) if w: window = w[0] diff --git a/kitty/rc/base.py b/kitty/rc/base.py index 817919288..1c0b886a2 100644 --- a/kitty/rc/base.py +++ b/kitty/rc/base.py @@ -1,10 +1,11 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal +from collections.abc import Callable, Iterable, Iterator from contextlib import suppress from dataclasses import dataclass, field from io import BytesIO -from typing import TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Iterable, Iterator, List, NoReturn, Optional, Set, Tuple, Type, Union, cast +from typing import TYPE_CHECKING, Any, NoReturn, Optional, Union, cast from kitty.cli import CompletionSpec, get_defaults_from_seq, parse_args, parse_option_spec from kitty.cli_stub import RCOptions as R @@ -19,7 +20,7 @@ if TYPE_CHECKING: Boss = B Tab = T else: - Boss = Window = Tab = None + Boss = Window = Tab = object RCOptions = R @@ -61,11 +62,11 @@ class StreamError(ValueError): class PayloadGetter: - def __init__(self, cmd: 'RemoteCommand', payload: Dict[str, Any]): + def __init__(self, cmd: 'RemoteCommand', payload: dict[str, Any]): self.payload = payload self.cmd = cmd - def __call__(self, key: str, opt_name: Optional[str] = None, missing: Any = None) -> Any: + def __call__(self, key: str, opt_name: str | None = None, missing: Any = None) -> Any: ans = self.payload.get(key, payload_get) if ans is not payload_get: return ans @@ -75,11 +76,11 @@ class PayloadGetter: no_response = NoResponse() payload_get = object() ResponseType = Union[bool, str, None, NoResponse, AsyncResponse] -CmdReturnType = Union[Dict[str, Any], List[Any], Tuple[Any, ...], str, int, float, bool] +CmdReturnType = Union[dict[str, Any], list[Any], tuple[Any, ...], str, int, float, bool] CmdGenerator = Iterator[CmdReturnType] PayloadType = Optional[Union[CmdReturnType, CmdGenerator]] PayloadGetType = PayloadGetter -ArgsType = List[str] +ArgsType = list[str] ImageCompletion = CompletionSpec.from_string('type:file group:"Images"') ImageCompletion.extensions = 'png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff' SUPPORTED_IMAGE_FORMATS = tuple(x.upper() for x in ImageCompletion.extensions if x != 'jpg') @@ -172,7 +173,7 @@ class ParsingOfArgsFailed(ValueError): class AsyncResponder: - def __init__(self, payload_get: PayloadGetType, window: Optional[Window]) -> None: + def __init__(self, payload_get: PayloadGetType, window: Window | None) -> None: self.async_id: str = payload_get('async_id', missing='') self.peer_id: int = payload_get('peer_id', missing=0) self.window_id: int = getattr(window, 'id', 0) @@ -190,17 +191,17 @@ class AsyncResponder: class ArgsHandling: json_field: str = '' - count: Optional[int] = None + count: int | None = None spec: str = '' completion: CompletionSpec = field(default_factory=CompletionSpec) - value_if_unspecified: Tuple[str, ...] = () + value_if_unspecified: tuple[str, ...] = () minimum_count: int = -1 - first_rest: Optional[Tuple[str, str]] = None + first_rest: tuple[str, str] | None = None special_parse: str = '' - args_choices: Optional[Callable[[], Iterable[str]]] = None + args_choices: Callable[[], Iterable[str]] | None = None @property - def args_count(self) -> Optional[int]: + def args_count(self) -> int | None: if not self.spec: return 0 return self.count @@ -212,7 +213,7 @@ class ArgsHandling: if self.completion: yield from self.completion.as_go_code(go_name + '.ArgCompleter', ' = ') - def as_go_code(self, cmd_name: str, field_types: Dict[str, str], handled_fields: Set[str]) -> Iterator[str]: + def as_go_code(self, cmd_name: str, field_types: dict[str, str], handled_fields: set[str]) -> Iterator[str]: c = self.args_count if c == 0: yield f'if len(args) != 0 {{ return fmt.Errorf("%s", "Unknown extra argument(s) supplied to {cmd_name}") }}' @@ -271,7 +272,7 @@ class ArgsHandling: return if jt.startswith('choices.'): yield f'if len(args) != 1 {{ return fmt.Errorf("%s", "Must specify exactly 1 argument for {cmd_name}") }}' - choices = ", ".join((f'"{x}"' for x in jt.split('.')[1:])) + choices = ", ".join(f'"{x}"' for x in jt.split('.')[1:]) yield 'switch(args[0]) {' yield f'case {choices}:\n\t{dest} = args[0]' yield f'default: return fmt.Errorf("%s is not a valid choice. Allowed values: %s", args[0], `{choices}`)' @@ -287,9 +288,9 @@ class StreamInFlight: def __init__(self) -> None: self.stream_id = '' - self.tempfile: Optional[BytesIO] = None + self.tempfile: BytesIO | None = None - def handle_data(self, stream_id: str, data: bytes) -> Union[AsyncResponse, BytesIO]: + def handle_data(self, stream_id: str, data: bytes) -> AsyncResponse | BytesIO: from ..remote_control import close_active_stream def abort_stream() -> None: close_active_stream(self.stream_id) @@ -325,15 +326,15 @@ class RemoteCommand: short_desc: str = '' desc: str = '' args: ArgsHandling = ArgsHandling() - options_spec: Optional[str] = None + options_spec: str | None = None response_timeout: float = 10. # seconds string_return_is_error: bool = False - defaults: Optional[Dict[str, Any]] = None + defaults: dict[str, Any] | None = None is_asynchronous: bool = False - options_class: Type[RCOptions] = RCOptions + options_class: type[RCOptions] = RCOptions protocol_spec: str = '' argspec = args_count = args_completion = ArgsHandling() - field_to_option_map: Optional[Dict[str, str]] = None + field_to_option_map: dict[str, str] | None = None reads_streaming_data: bool = False disallow_responses: bool = False @@ -354,7 +355,7 @@ class RemoteCommand: return self.defaults.get(name, missing) return missing - def windows_for_match_payload(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> List['Window']: + def windows_for_match_payload(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> list['Window']: if payload_get('all'): windows = list(boss.all_windows) else: @@ -370,7 +371,7 @@ class RemoteCommand: raise MatchError(payload_get('match')) return windows - def tabs_for_match_payload(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> List['Tab']: + def tabs_for_match_payload(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> list['Tab']: if payload_get('all'): return list(boss.all_tabs) match = payload_get('match') @@ -391,7 +392,7 @@ class RemoteCommand: def windows_for_payload( self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType, window_match_name: str = 'match_window', tab_match_name: str = 'match_tab', - ) -> List['Window']: + ) -> list['Window']: if payload_get('all'): windows = list(boss.all_windows) else: @@ -410,7 +411,7 @@ class RemoteCommand: windows += list(tab) return windows - def create_async_responder(self, payload_get: PayloadGetType, window: Optional[Window]) -> AsyncResponder: + def create_async_responder(self, payload_get: PayloadGetType, window: Window | None) -> AsyncResponder: return AsyncResponder(payload_get, window) def message_to_kitty(self, global_opts: RCOptions, opts: Any, args: ArgsType) -> PayloadType: @@ -422,18 +423,18 @@ class RemoteCommand: def cancel_async_request(self, boss: 'Boss', window: Optional['Window'], payload_get: PayloadGetType) -> None: pass - def handle_streamed_data(self, data: bytes, payload_get: PayloadGetType) -> Union[BytesIO, AsyncResponse]: + def handle_streamed_data(self, data: bytes, payload_get: PayloadGetType) -> BytesIO | AsyncResponse: stream_id = payload_get('stream_id') if not stream_id or not isinstance(stream_id, str): raise StreamError('No stream_id in rc payload') return self.stream_in_flight.handle_data(stream_id, data) -def cli_params_for(command: RemoteCommand) -> Tuple[Callable[[], str], str, str, str]: +def cli_params_for(command: RemoteCommand) -> tuple[Callable[[], str], str, str, str]: return (command.options_spec or '\n').format, command.args.spec, command.desc, f'kitten @ {command.name}' -def parse_subcommand_cli(command: RemoteCommand, args: ArgsType) -> Tuple[Any, ArgsType]: +def parse_subcommand_cli(command: RemoteCommand, args: ArgsType) -> tuple[Any, ArgsType]: opts, items = parse_args(args[1:], *cli_params_for(command), result_class=command.options_class) if command.args.args_count is not None and command.args.args_count != len(items): if command.args.args_count == 0: @@ -457,7 +458,7 @@ def command_for_name(cmd_name: str) -> RemoteCommand: return cast(RemoteCommand, getattr(m, cmd_name)) -def all_command_names() -> FrozenSet[str]: +def all_command_names() -> frozenset[str]: def ok(name: str) -> bool: root, _, ext = name.rpartition('.') diff --git a/kitty/rc/close_tab.py b/kitty/rc/close_tab.py index 6aae21bc4..b7d9deb94 100644 --- a/kitty/rc/close_tab.py +++ b/kitty/rc/close_tab.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -47,7 +47,7 @@ Do not return an error if no tabs are matched to be closed. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'self': opts.self, 'ignore_no_match': opts.ignore_no_match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: try: tabs = self.tabs_for_match_payload(boss, window, payload_get) except MatchError: diff --git a/kitty/rc/close_window.py b/kitty/rc/close_window.py index b854335c5..66a0d73f9 100644 --- a/kitty/rc/close_window.py +++ b/kitty/rc/close_window.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -39,7 +39,7 @@ Do not return an error if no windows are matched to be closed. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'self': opts.self, 'ignore_no_match': opts.ignore_no_match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: try: windows = self.windows_for_match_payload(boss, window, payload_get) except MatchError: diff --git a/kitty/rc/create_marker.py b/kitty/rc/create_marker.py index 4391c0ddb..35fb7ba96 100644 --- a/kitty/rc/create_marker.py +++ b/kitty/rc/create_marker.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from kitty.options.utils import parse_marker_spec @@ -40,7 +40,7 @@ Apply marker to the window this command is run in, rather than the active window self.fatal(f"Failed to parse marker specification {' '.join(args)} with error: {err}") return {'match': opts.match, 'self': opts.self, 'marker_spec': args} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: args = payload_get('marker_spec') for window in self.windows_for_match_payload(boss, window, payload_get): if window: diff --git a/kitty/rc/detach_tab.py b/kitty/rc/detach_tab.py index ca2d84781..660d6b780 100644 --- a/kitty/rc/detach_tab.py +++ b/kitty/rc/detach_tab.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -31,7 +31,7 @@ Detach the tab this command is run in, rather than the active tab. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'target_tab': opts.target_tab, 'self': opts.self} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: match = payload_get('target_tab') kwargs = {} if match: diff --git a/kitty/rc/detach_window.py b/kitty/rc/detach_window.py index 3dee86a80..64d115985 100644 --- a/kitty/rc/detach_window.py +++ b/kitty/rc/detach_window.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -42,11 +42,11 @@ Keep the focus on a window in the currently focused tab after moving the specifi def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'target_tab': opts.target_tab, 'self': opts.self, 'stay_in_tab': opts.stay_in_tab} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: windows = self.windows_for_match_payload(boss, window, payload_get) match = payload_get('target_tab') - target_tab_id: Optional[Union[str, int]] = None - newval: Union[str, int] = 'new' + target_tab_id: str | int | None = None + newval: str | int = 'new' if match: if match == 'new': target_tab_id = newval diff --git a/kitty/rc/disable_ligatures.py b/kitty/rc/disable_ligatures.py index acda8741b..24cbf35e7 100644 --- a/kitty/rc/disable_ligatures.py +++ b/kitty/rc/disable_ligatures.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -46,7 +46,7 @@ cause ligatures to be changed in all windows. 'all': opts.all, } - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: windows = self.windows_for_payload(boss, window, payload_get) boss.disable_ligatures_in(windows, payload_get('strategy')) return None diff --git a/kitty/rc/env.py b/kitty/rc/env.py index 05754ea63..47ba1fff9 100644 --- a/kitty/rc/env.py +++ b/kitty/rc/env.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import Any, Optional +from typing import Any from .base import ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -32,7 +32,7 @@ class Env(RemoteCommand): env[x + '='] = '' return {'env': env} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from kitty.child import default_env, set_default_env from kitty.utils import expandvars new_env = payload_get('env') or {} diff --git a/kitty/rc/focus_tab.py b/kitty/rc/focus_tab.py index d235689a4..62c9385c3 100644 --- a/kitty/rc/focus_tab.py +++ b/kitty/rc/focus_tab.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -30,7 +30,7 @@ using this option means that you will not be notified of failures. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'no_response': opts.no_response} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: for tab in self.tabs_for_match_payload(boss, window, payload_get): if tab: boss.set_active_tab(tab) diff --git a/kitty/rc/focus_window.py b/kitty/rc/focus_window.py index 8bd07944c..3d3fc4916 100644 --- a/kitty/rc/focus_window.py +++ b/kitty/rc/focus_window.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from kitty.fast_data_types import focus_os_window @@ -30,7 +30,7 @@ the command will exit with a success code. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: for window in self.windows_for_match_payload(boss, window, payload_get): if window: os_window_id = boss.set_active_window(window) diff --git a/kitty/rc/get_colors.py b/kitty/rc/get_colors.py index f38b4edde..b5e0f1d3a 100644 --- a/kitty/rc/get_colors.py +++ b/kitty/rc/get_colors.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from kitty.fast_data_types import Color from kitty.rgb import color_as_sharp, color_from_int @@ -39,7 +39,7 @@ configured colors. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'configured': opts.configured, 'match': opts.match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from kitty.fast_data_types import get_options opts = get_options() ans = {k: getattr(opts, k) for k in opts if isinstance(getattr(opts, k), Color)} diff --git a/kitty/rc/get_text.py b/kitty/rc/get_text.py index f10f8e8a3..2b065c284 100644 --- a/kitty/rc/get_text.py +++ b/kitty/rc/get_text.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -79,7 +79,7 @@ Get text from the window this command is run in, rather than the active window. 'self': opts.self, } - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from kitty.window import CommandOutput windows = self.windows_for_match_payload(boss, window, payload_get) if windows and windows[0]: diff --git a/kitty/rc/goto_layout.py b/kitty/rc/goto_layout.py index 63a2ea355..78db37537 100644 --- a/kitty/rc/goto_layout.py +++ b/kitty/rc/goto_layout.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Iterable, Optional +from collections.abc import Iterable +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, UnknownLayout, Window @@ -39,7 +40,7 @@ class GotoLayout(RemoteCommand): self.fatal('Exactly one layout must be specified') return {'layout': args[0], 'match': opts.match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: tabs = self.tabs_for_match_payload(boss, window, payload_get) for tab in tabs: if tab: diff --git a/kitty/rc/kitten.py b/kitty/rc/kitten.py index c22a0888d..7ba2aa668 100644 --- a/kitty/rc/kitten.py +++ b/kitty/rc/kitten.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -34,7 +34,7 @@ class Kitten(RemoteCommand): self.fatal('Must specify kitten name') return {'match': opts.match, 'args': list(args)[1:], 'kitten': args[0]} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: retval = None for window in self.windows_for_match_payload(boss, window, payload_get): if window: diff --git a/kitty/rc/last_used_layout.py b/kitty/rc/last_used_layout.py index 3c942ce8a..85dbfce58 100644 --- a/kitty/rc/last_used_layout.py +++ b/kitty/rc/last_used_layout.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -36,7 +36,7 @@ the command will exit with a success code. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'all': opts.all, 'no_response': opts.no_response} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: for tab in self.tabs_for_match_payload(boss, window, payload_get): if tab: tab.last_used_layout() diff --git a/kitty/rc/launch.py b/kitty/rc/launch.py index 134713c35..af2691698 100644 --- a/kitty/rc/launch.py +++ b/kitty/rc/launch.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING from kitty.cli_stub import LaunchCLIOptions from kitty.launch import launch as do_launch @@ -80,7 +80,7 @@ instead of the active tab ans[attr] = val return ans - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: default_opts = parse_launch_args()[0] opts = LaunchCLIOptions() for key, default_value in default_opts.__dict__.items(): @@ -92,7 +92,7 @@ instead of the active tab setattr(opts, key, val) ceval = payload_get('copy_env') opts.copy_env = False - base_env: Optional[Dict[str, str]] = None + base_env: dict[str, str] | None = None if ceval: if isinstance(ceval, list): base_env = {} diff --git a/kitty/rc/load_config.py b/kitty/rc/load_config.py index ed994fb76..70523a850 100644 --- a/kitty/rc/load_config.py +++ b/kitty/rc/load_config.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from kitty.constants import appname @@ -62,7 +62,7 @@ using this option means that you will not be notified of failures. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'paths': args, 'override': opts.override, 'ignore_overrides': opts.ignore_overrides} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from kitty.cli import parse_override from kitty.utils import resolve_abs_or_config_path paths = tuple(map(resolve_abs_or_config_path, payload_get('paths', missing=()))) diff --git a/kitty/rc/ls.py b/kitty/rc/ls.py index e2bb6e36c..5880a21fd 100644 --- a/kitty/rc/ls.py +++ b/kitty/rc/ls.py @@ -2,7 +2,8 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal import json -from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Tuple +from collections.abc import Callable +from typing import TYPE_CHECKING from kitty.constants import appname @@ -45,9 +46,9 @@ Only list the window this command is run in. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'all_env_vars': opts.all_env_vars, 'match': opts.match, 'match_tab': opts.match_tab} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: - tab_filter: Optional[Callable[[Tab], bool]] = None - window_filter: Optional[Callable[[Window], bool]] = None + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: + tab_filter: Callable[[Tab], bool] | None = None + window_filter: Callable[[Window], bool] | None = None if payload_get('self'): def wf(w: Window) -> bool: @@ -60,12 +61,12 @@ Only list the window this command is run in. window_filter = wf data = list(boss.list_os_windows(window, tab_filter, window_filter)) if not payload_get('all_env_vars'): - all_env_blocks: List[Dict[str, str]] = [] - common_env_vars: Set[Tuple[str, str]] = set() + all_env_blocks: list[dict[str, str]] = [] + common_env_vars: set[tuple[str, str]] = set() for osw in data: for tab in osw.get('tabs', ()): for w in tab.get('windows', ()): - env: Dict[str, str] = w.get('env', {}) + env: dict[str, str] = w.get('env', {}) frozen_env = set(env.items()) if all_env_blocks: common_env_vars &= frozen_env diff --git a/kitty/rc/new_window.py b/kitty/rc/new_window.py index 3ddfa57ac..7806dcc12 100644 --- a/kitty/rc/new_window.py +++ b/kitty/rc/new_window.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -86,7 +86,7 @@ the id of the new window will not be printed out. ans[attr] = val return ans - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from .launch import launch return launch.response_from_kitty(boss, window, payload_get) diff --git a/kitty/rc/remove_marker.py b/kitty/rc/remove_marker.py index 51a787849..63902db81 100644 --- a/kitty/rc/remove_marker.py +++ b/kitty/rc/remove_marker.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -27,7 +27,7 @@ Apply marker to the window this command is run in, rather than the active window def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'self': opts.self} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: for window in self.windows_for_match_payload(boss, window, payload_get): if window: window.remove_marker() diff --git a/kitty/rc/resize_os_window.py b/kitty/rc/resize_os_window.py index 599055394..aab80c4f6 100644 --- a/kitty/rc/resize_os_window.py +++ b/kitty/rc/resize_os_window.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -76,7 +76,7 @@ using this option means that you will not be notified of failures. 'incremental': opts.incremental } - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: windows = self.windows_for_match_payload(boss, window, payload_get) if windows: ac = payload_get('action') diff --git a/kitty/rc/resize_window.py b/kitty/rc/resize_window.py index 58c378852..338c18f94 100644 --- a/kitty/rc/resize_window.py +++ b/kitty/rc/resize_window.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -48,9 +48,9 @@ Resize the window this command is run in, rather than the active window. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'increment': opts.increment, 'axis': opts.axis, 'self': opts.self} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: windows = self.windows_for_match_payload(boss, window, payload_get) - resized: Union[bool, None, str] = False + resized: bool | None | str = False if windows and windows[0]: resized = boss.resize_layout_window( windows[0], increment=payload_get('increment'), is_horizontal=payload_get('axis') == 'horizontal', diff --git a/kitty/rc/run.py b/kitty/rc/run.py index 41eb26e00..41304d297 100644 --- a/kitty/rc/run.py +++ b/kitty/rc/run.py @@ -3,7 +3,7 @@ import sys from base64 import standard_b64decode, standard_b64encode -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING from kitty.launch import env_docs, remote_control_password_docs from kitty.options.utils import env as parse_env @@ -88,7 +88,7 @@ The executed program will have privileges to run remote control commands in kitt yield ret return pipe() - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: import os import tempfile data = payload_get('data') @@ -106,7 +106,7 @@ The executed program will have privileges to run remote control commands in kitt responder = self.create_async_responder(payload_get, window) stdout, stderr = tempfile.TemporaryFile(), tempfile.TemporaryFile() - def on_death(exit_status: int, err: Optional[Exception]) -> None: + def on_death(exit_status: int, err: Exception | None) -> None: with stdout, stderr: if err: responder.send_error(f'Failed to run: {cmdline} with err: {err}') @@ -120,7 +120,7 @@ The executed program will have privileges to run remote control commands in kitt 'exit_code': exit_code, 'exit_status': exit_status, }) - env: Dict[str, str] = {} + env: dict[str, str] = {} for x in payload_get('env') or (): for k, v in parse_env(x, env): env[k] = v diff --git a/kitty/rc/scroll_window.py b/kitty/rc/scroll_window.py index 98dbf76cf..124145fb0 100644 --- a/kitty/rc/scroll_window.py +++ b/kitty/rc/scroll_window.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional, Tuple, Union +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -41,7 +41,7 @@ using this option means that you will not be notified of failures. if len(args) < 1: self.fatal('Scroll amount must be specified') amt = args[0] - amount: Tuple[Union[str, float], Optional[str]] = (amt, None) + amount: tuple[str | float, str | None] = (amt, None) if amt not in ('start', 'end'): pages = 'p' in amt unscroll = 'u' in amt @@ -54,7 +54,7 @@ using this option means that you will not be notified of failures. # defaults to scroll the window this command is run in return {'match': opts.match, 'amount': amount, 'self': True} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: amt = payload_get('amount') for window in self.windows_for_match_payload(boss, window, payload_get): if window: diff --git a/kitty/rc/select_window.py b/kitty/rc/select_window.py index 2d594cda9..c3b07cddd 100644 --- a/kitty/rc/select_window.py +++ b/kitty/rc/select_window.py @@ -62,10 +62,10 @@ be refocused. 'reactivate_prev_tab': opts.reactivate_prev_tab} return ans - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: responder = self.create_async_responder(payload_get, window) - def callback(tab: Optional['Tab'], window: Optional[Window]) -> None: + def callback(tab: Optional['Tab'], window: Window | None) -> None: if window: responder.send_data(window.id) else: diff --git a/kitty/rc/send_key.py b/kitty/rc/send_key.py index d71196f5a..8463e7de5 100644 --- a/kitty/rc/send_key.py +++ b/kitty/rc/send_key.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import ( MATCH_TAB_OPTION, @@ -56,7 +56,7 @@ Do not send text to the active window, even if it is one of the matched windows. ret = {'match': opts.match, 'keys': args, 'match_tab': opts.match_tab, 'all': opts.all, 'exclude_active': opts.exclude_active} return ret - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: windows = self.windows_for_payload(boss, None, payload_get, window_match_name='match') keys = payload_get('keys') for w in windows: diff --git a/kitty/rc/send_text.py b/kitty/rc/send_text.py index 77daeef93..1e35bb71a 100644 --- a/kitty/rc/send_text.py +++ b/kitty/rc/send_text.py @@ -3,7 +3,7 @@ import base64 import sys -from typing import TYPE_CHECKING, Any, Dict, Optional, Set, Union +from typing import TYPE_CHECKING, Any from kitty.fast_data_types import KeyEvent as WindowSystemKeyEvent from kitty.fast_data_types import get_boss @@ -31,14 +31,14 @@ if TYPE_CHECKING: class Session: id: str - window_ids: Set[int] + window_ids: set[int] def __init__(self, id: str): self.id = id self.window_ids = set() -sessions_map: Dict[str, Session] = {} +sessions_map: dict[str, Session] = {} class SessionAction: @@ -192,14 +192,14 @@ on bracketed paste mode. yield from src return chain() - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: sid = payload_get('session_id', '') windows = self.windows_for_payload(boss, window, payload_get, window_match_name='match') pdata: str = payload_get('data') encoding, _, q = pdata.partition(':') session = '' if encoding == 'text': - data: Union[bytes, WindowSystemKeyEvent] = q.encode('utf-8') + data: bytes | WindowSystemKeyEvent = q.encode('utf-8') elif encoding == 'base64': data = base64.standard_b64decode(q) elif encoding == 'kitty-key': diff --git a/kitty/rc/set_background_image.py b/kitty/rc/set_background_image.py index 2d89b0482..42822dfc0 100644 --- a/kitty/rc/set_background_image.py +++ b/kitty/rc/set_background_image.py @@ -4,7 +4,7 @@ import os from base64 import standard_b64decode, standard_b64encode from io import BytesIO -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from kitty.types import AsyncResponse from kitty.utils import is_png @@ -107,7 +107,7 @@ failed, the command will exit with a success code. yield ret return file_pipe(path) - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: data = payload_get('data') windows = self.windows_for_payload(boss, window, payload_get, window_match_name='match') os_windows = tuple({w.os_window_id for w in windows if w}) diff --git a/kitty/rc/set_background_opacity.py b/kitty/rc/set_background_opacity.py index 4f1776a04..d7c0c7897 100644 --- a/kitty/rc/set_background_opacity.py +++ b/kitty/rc/set_background_opacity.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import ( MATCH_TAB_OPTION, @@ -60,7 +60,7 @@ equal to the specified value, otherwise it will be set to the specified value. 'all': opts.all, 'match_tab': opts.match_tab, 'toggle': opts.toggle, } - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from kitty.fast_data_types import background_opacity_of, get_options opts = get_options() if not opts.dynamic_background_opacity: diff --git a/kitty/rc/set_colors.py b/kitty/rc/set_colors.py index a616ceacb..657c1272c 100644 --- a/kitty/rc/set_colors.py +++ b/kitty/rc/set_colors.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING from kitty.cli import emph from kitty.fast_data_types import Color @@ -66,7 +66,7 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors -- completion=RemoteCommand.CompletionSpec.from_string('type:file group:"CONF files", ext:conf')) def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: - final_colors: Dict[str, int | None | str] = {} + final_colors: dict[str, int | None | str] = {} transparent_background_colors: tuple[tuple[Color, float], ...] = () from kitty.colors import parse_colors if not opts.reset: @@ -86,10 +86,10 @@ this option, any color arguments are ignored and :option:`kitten @ set-colors -- } return ans - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: from kitty.colors import patch_colors windows = self.windows_for_payload(boss, window, payload_get) - colors: Dict[str, int | None] = payload_get('colors') or {} + colors: dict[str, int | None] = payload_get('colors') or {} if payload_get('reset'): colors = {k: None if v is None else int(v) for k, v in boss.color_settings_at_startup.items()} tbc = colors.get('transparent_background_colors') diff --git a/kitty/rc/set_enabled_layouts.py b/kitty/rc/set_enabled_layouts.py index 439727ad1..db9b5691b 100644 --- a/kitty/rc/set_enabled_layouts.py +++ b/kitty/rc/set_enabled_layouts.py @@ -1,7 +1,8 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Iterable, List, Optional +from collections.abc import Iterable +from typing import TYPE_CHECKING from kitty.fast_data_types import get_options from kitty.options.utils import parse_layout_names @@ -46,7 +47,7 @@ as well. def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: if len(args) < 1: self.fatal('At least one layout must be specified') - a: List[str] = [] + a: list[str] = [] for x in args: a.extend(y.strip() for y in x.split(',')) try: @@ -55,7 +56,7 @@ as well. self.fatal(str(err)) return {'layouts': layouts, 'match': opts.match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: tabs = self.tabs_for_match_payload(boss, window, payload_get) layouts = parse_layout_names(payload_get('layouts')) if payload_get('configured'): diff --git a/kitty/rc/set_font_size.py b/kitty/rc/set_font_size.py index 464527824..f92a52ad1 100644 --- a/kitty/rc/set_font_size.py +++ b/kitty/rc/set_font_size.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -42,7 +42,7 @@ the font size for any newly created OS Windows in the future. inc = fs[0] if fs and fs[0] in '+-' else None return {'size': abs(float(fs)), 'all': opts.all, 'increment_op': inc} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: boss.change_font_size( payload_get('all'), payload_get('increment_op'), payload_get('size') or 0) diff --git a/kitty/rc/set_spacing.py b/kitty/rc/set_spacing.py index caa2e624f..381d4081d 100644 --- a/kitty/rc/set_spacing.py +++ b/kitty/rc/set_spacing.py @@ -2,7 +2,8 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Dict, Iterable, List, Optional +from collections.abc import Iterable +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -11,7 +12,7 @@ if TYPE_CHECKING: from kitty.options.types import Options -def patch_window_edges(w: Window, s: Dict[str, Optional[float]]) -> None: +def patch_window_edges(w: Window, s: dict[str, float | None]) -> None: for k, v in s.items(): which, edge = k.lower().split('-', 1) if edge == 'left': @@ -24,7 +25,7 @@ def patch_window_edges(w: Window, s: Dict[str, Optional[float]]) -> None: w.patch_edge_width(which, 'bottom', v) -def patch_configured_edges(opts: 'Options', s: Dict[str, Optional[float]]) -> None: +def patch_configured_edges(opts: 'Options', s: dict[str, float | None]) -> None: for k, val in s.items(): if val is None: continue @@ -34,15 +35,15 @@ def patch_configured_edges(opts: 'Options', s: Dict[str, Optional[float]]) -> No setattr(opts, q, new_edges) -def parse_spacing_settings(args: Iterable[str]) -> Dict[str, Optional[float]]: - mapper: Dict[str, List[str]] = {} +def parse_spacing_settings(args: Iterable[str]) -> dict[str, float | None]: + mapper: dict[str, list[str]] = {} for q in ('margin', 'padding'): mapper[q] = f'{q}-left {q}-top {q}-right {q}-bottom'.split() mapper[f'{q}-h'] = mapper[f'{q}-horizontal'] = f'{q}-left {q}-right'.split() mapper[f'{q}-v'] = mapper[f'{q}-vertical'] = f'{q}-top {q}-bottom'.split() for edge in ('left', 'top', 'right', 'bottom'): mapper[f'{q}-{edge}'] = [f'{q}-{edge}'] - settings: Dict[str, Optional[float]] = {} + settings: dict[str, float | None] = {} for spec in args: parts = spec.split('=', 1) if len(parts) != 2: @@ -108,9 +109,9 @@ windows). } return ans - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: windows = self.windows_for_payload(boss, window, payload_get) - settings: Dict[str, Optional[float]] = payload_get('settings') + settings: dict[str, float | None] = payload_get('settings') dirtied_tabs = {} from kitty.fast_data_types import get_options if payload_get('configured'): diff --git a/kitty/rc/set_tab_color.py b/kitty/rc/set_tab_color.py index 4e2ae3019..49db763be 100644 --- a/kitty/rc/set_tab_color.py +++ b/kitty/rc/set_tab_color.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING from kitty.rgb import to_color @@ -15,15 +15,15 @@ if TYPE_CHECKING: valid_color_names = frozenset('active_fg active_bg inactive_fg inactive_bg'.split()) -def parse_colors(args: ArgsType) -> Dict[str, Optional[int]]: - ans: Dict[str, Optional[int]] = {} +def parse_colors(args: ArgsType) -> dict[str, int | None]: + ans: dict[str, int | None] = {} for spec in args: key, val = spec.split('=', 1) key = key.lower() if key.lower() not in valid_color_names: raise KeyError(f'{key} is not a valid color name') if val.lower() == 'none': - col: Optional[int] = None + col: int | None = None else: q = to_color(val, validate=True) if q is not None: @@ -65,7 +65,7 @@ Close the tab this command is run in, rather than the active tab. raise ParsingOfArgsFailed('No colors specified') return {'match': opts.match, 'self': opts.self, 'colors': colors} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: colors = payload_get('colors') s = {k: None if colors[k] is None else int(colors[k]) for k in valid_color_names if k in colors} for tab in self.tabs_for_match_payload(boss, window, payload_get): diff --git a/kitty/rc/set_tab_title.py b/kitty/rc/set_tab_title.py index 1bd57c1c2..a87fba786 100644 --- a/kitty/rc/set_tab_title.py +++ b/kitty/rc/set_tab_title.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_TAB_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -30,7 +30,7 @@ class SetTabTitle(RemoteCommand): def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'title': ' '.join(args), 'match': opts.match} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: for tab in self.tabs_for_match_payload(boss, window, payload_get): if tab: tab.set_title(payload_get('title')) diff --git a/kitty/rc/set_user_vars.py b/kitty/rc/set_user_vars.py index e45538233..522af8368 100644 --- a/kitty/rc/set_user_vars.py +++ b/kitty/rc/set_user_vars.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -29,7 +29,7 @@ class SetUserVars(RemoteCommand): def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: return {'match': opts.match, 'var': args, 'self': True} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: val = {} for x in payload_get('var') or (): a, sep, b = x.partition('=') diff --git a/kitty/rc/set_window_logo.py b/kitty/rc/set_window_logo.py index 20775cefa..aa99e5755 100644 --- a/kitty/rc/set_window_logo.py +++ b/kitty/rc/set_window_logo.py @@ -4,7 +4,7 @@ import os from base64 import standard_b64decode, standard_b64encode from io import BytesIO -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from kitty.types import AsyncResponse from kitty.utils import is_png @@ -102,7 +102,7 @@ failed, the command will exit with a success code. yield ret return file_pipe(path) - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: data = payload_get('data') alpha = float(payload_get('alpha', '-1')) position = payload_get('position') or '' diff --git a/kitty/rc/set_window_title.py b/kitty/rc/set_window_title.py index 96c2fb324..33d55f386 100644 --- a/kitty/rc/set_window_title.py +++ b/kitty/rc/set_window_title.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -41,7 +41,7 @@ again. If you want to allow other programs to change it afterwards, use this opt ans['self'] = True return ans - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: title = payload_get('title') if payload_get('temporary') and title is not None: title = memoryview(title.encode('utf-8')) diff --git a/kitty/rc/signal_child.py b/kitty/rc/signal_child.py index 280a086aa..60497ec5f 100644 --- a/kitty/rc/signal_child.py +++ b/kitty/rc/signal_child.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from .base import MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window @@ -38,7 +38,7 @@ using this option means that you will not be notified of failures. # defaults to signal the window this command is run in return {'match': opts.match, 'self': True, 'signals': [x.upper() for x in args] or ['SIGINT']} - def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: + def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType: import signal signals = tuple(getattr(signal, x) for x in payload_get('signals')) for window in self.windows_for_match_payload(boss, window, payload_get): diff --git a/kitty/remote_control.py b/kitty/remote_control.py index 3e1156d68..902b3749a 100644 --- a/kitty/remote_control.py +++ b/kitty/remote_control.py @@ -15,7 +15,6 @@ from typing import ( TYPE_CHECKING, Any, Optional, - Union, cast, ) @@ -82,7 +81,7 @@ def parse_cmd(serialized_cmd: memoryview, encryption_key: EllipticCurveKey) -> d class CMDChecker: - def __call__(self, pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> Optional[bool]: + def __call__(self, pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> bool | None: return False @@ -105,7 +104,7 @@ def fnmatch_pattern(pat: str) -> 're.Pattern[str]': def remote_control_allowed( - pcmd: dict[str, Any], remote_control_passwords: Optional[dict[str, Sequence[str]]], + pcmd: dict[str, Any], remote_control_passwords: dict[str, Sequence[str]] | None, window: Optional['Window'], extra_data: dict[str, Any] ) -> bool: if not remote_control_passwords: @@ -168,7 +167,7 @@ def password_authorizer(auth_items: frozenset[str]) -> PasswordAuthorizer: user_password_allowed: dict[str, bool] = {} -def is_cmd_allowed(pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> Optional[bool]: +def is_cmd_allowed(pcmd: dict[str, Any], window: Optional['Window'], from_socket: bool, extra_data: dict[str, Any]) -> bool | None: sid = pcmd.get('stream_id', '') if sid and active_streams.get(sid, '') == pcmd['cmd']: return True @@ -205,8 +204,8 @@ def close_active_stream(stream_id: str) -> None: def handle_cmd( - boss: BossType, window: Optional[WindowType], cmd: dict[str, Any], peer_id: int, self_window: Optional[WindowType] -) -> Union[dict[str, Any], None, AsyncResponse]: + boss: BossType, window: WindowType | None, cmd: dict[str, Any], peer_id: int, self_window: WindowType | None +) -> dict[str, Any] | None | AsyncResponse: v = cmd['version'] no_response = cmd.get('no_response', False) if tuple(v)[:2] > version[:2]: @@ -326,7 +325,7 @@ class SocketIO: self.socket.shutdown(socket.SHUT_RDWR) self.socket.close() - def send(self, data: Union[bytes, Iterable[Union[str, bytes]]]) -> None: + def send(self, data: bytes | Iterable[str | bytes]) -> None: import socket with self.socket.makefile('wb') as out: if isinstance(data, bytes): @@ -362,11 +361,11 @@ class RCIO(TTYIO): def do_io( - to: Optional[str], original_cmd: dict[str, Any], no_response: bool, response_timeout: float, encrypter: 'CommandEncrypter' + to: str | None, original_cmd: dict[str, Any], no_response: bool, response_timeout: float, encrypter: 'CommandEncrypter' ) -> dict[str, Any]: payload = original_cmd.get('payload') if not isinstance(payload, GeneratorType): - send_data: Union[bytes, Iterator[bytes]] = encode_send(encrypter(original_cmd)) + send_data: bytes | Iterator[bytes] = encode_send(encrypter(original_cmd)) else: def send_generator() -> Iterator[bytes]: assert payload is not None @@ -375,7 +374,7 @@ def do_io( yield encode_send(encrypter(original_cmd)) send_data = send_generator() - io: Union[SocketIO, RCIO] = SocketIO(to) if to else RCIO() + io: SocketIO | RCIO = SocketIO(to) if to else RCIO() with io: io.send(send_data) if no_response: @@ -462,7 +461,7 @@ def send_response_to_client(data: Any = None, error: str = '', peer_id: int = 0, if active_async_requests.pop(async_id, None) is None: return if error: - response: dict[str, Union[bool, int, str]] = {'ok': False, 'error': error} + response: dict[str, bool | int | str] = {'ok': False, 'error': error} else: response = {'ok': True, 'data': data} if peer_id > 0: diff --git a/kitty/rgb.py b/kitty/rgb.py index 8c2259f0a..606d6291d 100644 --- a/kitty/rgb.py +++ b/kitty/rgb.py @@ -3,7 +3,6 @@ import re from contextlib import suppress -from typing import Optional from .fast_data_types import Color @@ -26,7 +25,7 @@ def parse_single_color(c: str) -> int: return int(c[:2], 16) -def parse_sharp(spec: str) -> Optional[Color]: +def parse_sharp(spec: str) -> Color | None: if len(spec) in (3, 6, 9, 12): part_len = len(spec) // 3 colors = re.findall(fr'[a-fA-F0-9]{{{part_len}}}', spec) @@ -34,7 +33,7 @@ def parse_sharp(spec: str) -> Optional[Color]: return None -def parse_rgb(spec: str) -> Optional[Color]: +def parse_rgb(spec: str) -> Color | None: colors = spec.split('/') if len(colors) == 3: return Color(*map(parse_single_color, colors)) @@ -45,7 +44,7 @@ def parse_single_intensity(x: str) -> int: return int(max(0, min(abs(float(x)), 1)) * 255) -def parse_rgbi(spec: str) -> Optional[Color]: +def parse_rgbi(spec: str) -> Color | None: colors = spec.split('/') if len(colors) == 3: return Color(*map(parse_single_intensity, colors)) @@ -68,13 +67,13 @@ def color_as_sgr(x: Color) -> str: return x.as_sgr -def to_color(raw: str, validate: bool = False) -> Optional[Color]: +def to_color(raw: str, validate: bool = False) -> Color | None: # See man XParseColor x = raw.strip().lower() ans = color_names.get(x) if ans is not None: return ans - val: Optional[Color] = None + val: Color | None = None with suppress(Exception): if raw.startswith('#'): val = parse_sharp(raw[1:]) diff --git a/kitty/search_query_parser.py b/kitty/search_query_parser.py index d83c2755c..4c551d11b 100644 --- a/kitty/search_query_parser.py +++ b/kitty/search_query_parser.py @@ -1,11 +1,11 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2022, Kovid Goyal import re -from collections.abc import Iterator, Sequence +from collections.abc import Callable, Iterator, Sequence from enum import Enum from functools import lru_cache from gettext import gettext as _ -from typing import Callable, NamedTuple, Optional, TypeVar, Union +from typing import NamedTuple, TypeVar from .types import run_once @@ -151,7 +151,7 @@ class Parser: self.tokens: list[Token] = [] self.allow_no_location = allow_no_location - def token(self, advance: bool = False) -> Optional[str]: + def token(self, advance: bool = False) -> str | None: if self.is_eof(): return None res = self.tokens[self.current_token].val @@ -159,7 +159,7 @@ class Parser: self.current_token += 1 return res - def lcase_token(self, advance: bool = False) -> Optional[str]: + def lcase_token(self, advance: bool = False) -> str | None: if self.is_eof(): return None res = self.tokens[self.current_token].val @@ -280,7 +280,7 @@ class Parser: @lru_cache(maxsize=64) -def build_tree(query: str, locations: Union[str, tuple[str, ...]], allow_no_location: bool = False) -> SearchTreeNode: +def build_tree(query: str, locations: str | tuple[str, ...], allow_no_location: bool = False) -> SearchTreeNode: if isinstance(locations, str): locations = tuple(locations.split()) p = Parser(allow_no_location) @@ -291,7 +291,7 @@ def build_tree(query: str, locations: Union[str, tuple[str, ...]], allow_no_loca def search( - query: str, locations: Union[str, tuple[str, ...]], universal_set: set[T], get_matches: GetMatches[T], + query: str, locations: str | tuple[str, ...], universal_set: set[T], get_matches: GetMatches[T], allow_no_location: bool = False, ) -> set[T]: return build_tree(query, locations, allow_no_location).search(universal_set, get_matches) diff --git a/kitty/session.py b/kitty/session.py index 1e98478b6..9639b898c 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -4,10 +4,10 @@ import os import shlex import sys -from collections.abc import Generator, Iterator, Mapping +from collections.abc import Callable, Generator, Iterator, Mapping from contextlib import suppress from functools import partial -from typing import TYPE_CHECKING, Callable, Optional, Union +from typing import TYPE_CHECKING, Optional, Union from .cli_stub import CLIOptions from .layout.interface import all_layouts @@ -39,7 +39,7 @@ class WindowSpec: def __init__(self, launch_spec: Union['LaunchSpec', 'SpecialWindowInstance']): self.launch_spec = launch_spec - self.resize_spec: Optional[ResizeSpec] = None + self.resize_spec: ResizeSpec | None = None self.focus_matching_window_spec: str = '' self.is_background_process = False if hasattr(launch_spec, 'opts'): # LaunchSpec @@ -52,14 +52,14 @@ class Tab: def __init__(self, opts: Options, name: str): self.windows: list[WindowSpec] = [] - self.pending_resize_spec: Optional[ResizeSpec] = None + self.pending_resize_spec: ResizeSpec | None = None self.pending_focus_matching_window: str = '' self.name = name.strip() self.active_window_idx = 0 self.enabled_layouts = opts.enabled_layouts self.layout = (self.enabled_layouts or ['tall'])[0] - self.cwd: Optional[str] = None - self.next_title: Optional[str] = None + self.cwd: str | None = None + self.next_title: str | None = None @property def has_non_background_processes(self) -> bool: @@ -71,13 +71,13 @@ class Tab: class Session: - def __init__(self, default_title: Optional[str] = None): + def __init__(self, default_title: str | None = None): self.tabs: list[Tab] = [] self.active_tab_idx = 0 self.default_title = default_title - self.os_window_size: Optional[WindowSizes] = None - self.os_window_class: Optional[str] = None - self.os_window_state: Optional[str] = None + self.os_window_size: WindowSizes | None = None + self.os_window_class: str | None = None + self.os_window_state: str | None = None self.focus_os_window: bool = False @property @@ -100,7 +100,7 @@ class Session: raise ValueError(f'{val} is not a valid layout') self.tabs[-1].layout = val - def add_window(self, cmd: Union[None, str, list[str]], expand: Callable[[str], str] = lambda x: x) -> None: + def add_window(self, cmd: None | str | list[str], expand: Callable[[str], str] = lambda x: x) -> None: from .launch import parse_launch_args needs_expandvars = False if isinstance(cmd, str) and cmd: @@ -161,7 +161,7 @@ class Session: self.tabs[-1].cwd = val -def parse_session(raw: str, opts: Options, environ: Optional[Mapping[str, str]] = None) -> Generator[Session, None, None]: +def parse_session(raw: str, opts: Options, environ: Mapping[str, str] | None = None) -> Generator[Session, None, None]: def finalize_session(ans: Session) -> Session: from .tabs import SpecialWindow @@ -233,17 +233,17 @@ class PreReadSession(str): def create_sessions( opts: Options, - args: Optional[CLIOptions] = None, + args: CLIOptions | None = None, special_window: Optional['SpecialWindowInstance'] = None, cwd_from: Optional['CwdRequest'] = None, respect_cwd: bool = False, - default_session: Optional[str] = None, + default_session: str | None = None, ) -> Iterator[Session]: if args and args.session: if args.session == "none": default_session = "none" else: - environ: Optional[Mapping[str, str]] = None + environ: Mapping[str, str] | None = None if isinstance(args.session, PreReadSession): session_data = '' + str(args.session) environ = args.session.associated_environ # type: ignore @@ -272,7 +272,7 @@ def create_sessions( if special_window is None: cmd = args.args if args and args.args else resolved_shell(opts) from kitty.tabs import SpecialWindow - cwd: Optional[str] = args.directory if respect_cwd and args else None + cwd: str | None = args.directory if respect_cwd and args else None special_window = SpecialWindow(cmd, cwd_from=cwd_from, cwd=cwd, hold=bool(args and args.hold)) ans.add_special_window(special_window) yield ans diff --git a/kitty/shaders.py b/kitty/shaders.py index 61c68b1a4..d3f80f73e 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -2,10 +2,10 @@ # License: GPLv3 Copyright: 2023, Kovid Goyal import re -from collections.abc import Iterator +from collections.abc import Callable, Iterator from functools import lru_cache, partial from itertools import count -from typing import Any, Callable, Optional +from typing import Any, Optional from .constants import read_kitty_resource from .fast_data_types import ( diff --git a/kitty/shell_integration.py b/kitty/shell_integration.py index c3cf1493b..21779d5a0 100644 --- a/kitty/shell_integration.py +++ b/kitty/shell_integration.py @@ -4,8 +4,8 @@ import os import subprocess +from collections.abc import Callable from contextlib import suppress -from typing import Callable, Optional from .constants import shell_integration_dir from .fast_data_types import get_options @@ -24,7 +24,7 @@ def setup_fish_env(env: dict[str, str], argv: list[str]) -> None: env['XDG_DATA_DIRS'] = os.pathsep.join(dirs) -def is_new_zsh_install(env: dict[str, str], zdotdir: Optional[str]) -> bool: +def is_new_zsh_install(env: dict[str, str], zdotdir: str | None) -> bool: # if ZDOTDIR is empty, zsh will read user rc files from / # if there aren't any, it'll run zsh-newuser-install # the latter will bail if there are rc files in $HOME @@ -39,7 +39,7 @@ def is_new_zsh_install(env: dict[str, str], zdotdir: Optional[str]) -> bool: return True -def get_zsh_zdotdir_from_global_zshenv(env: dict[str, str], argv: list[str]) -> Optional[str]: +def get_zsh_zdotdir_from_global_zshenv(env: dict[str, str], argv: list[str]) -> str | None: exe = which(argv[0], only_system=True) or 'zsh' with suppress(Exception): return subprocess.check_output([exe, '--norcs', '--interactive', '-c', 'echo -n $ZDOTDIR'], env=env).decode('utf-8') @@ -183,7 +183,7 @@ ENV_SERIALIZERS: dict[str, Callable[[dict[str, str]], str]] = { } -def get_supported_shell_name(path: str) -> Optional[str]: +def get_supported_shell_name(path: str) -> str | None: name = os.path.basename(path) if name.lower().endswith('.exe'): name = name.rpartition('.')[0] @@ -205,7 +205,7 @@ def serialize_env(path: str, env: dict[str, str]) -> str: return ENV_SERIALIZERS[name](env) -def get_effective_ksi_env_var(opts: Optional[Options] = None) -> str: +def get_effective_ksi_env_var(opts: Options | None = None) -> str: opts = opts or get_options() if 'disabled' in opts.shell_integration: return '' diff --git a/kitty/shm.py b/kitty/shm.py index 3ed23fed7..ffb384a23 100644 --- a/kitty/shm.py +++ b/kitty/shm.py @@ -11,7 +11,6 @@ import os import secrets import stat import struct -from typing import Optional, Union from kitty.fast_data_types import SHM_NAME_MAX, shm_open, shm_unlink @@ -41,7 +40,7 @@ class SharedMemory: ''' _fd: int = -1 _name: str = '' - _mmap: Optional[mmap.mmap] = None + _mmap: mmap.mmap | None = None _size: int = 0 size_fmt = '!I' num_bytes_for_size = struct.calcsize(size_fmt) @@ -112,7 +111,7 @@ class SharedMemory: def flush(self) -> None: self.mmap.flush() - def write_data_with_size(self, data: Union[str, bytes]) -> None: + def write_data_with_size(self, data: str | bytes) -> None: if isinstance(data, str): data = data.encode('utf-8') sz = struct.pack(self.size_fmt, len(data)) diff --git a/kitty/short_uuid.py b/kitty/short_uuid.py index 355b8f18a..e22a0eea1 100644 --- a/kitty/short_uuid.py +++ b/kitty/short_uuid.py @@ -5,10 +5,9 @@ import math import string import uuid as _uuid from collections.abc import Sequence -from typing import Optional -def num_to_string(number: int, alphabet: Sequence[str], alphabet_len: int, pad_to_length: Optional[int] = None) -> str: +def num_to_string(number: int, alphabet: Sequence[str], alphabet_len: int, pad_to_length: int | None = None) -> str: ans = [] number = max(0, number) while number: @@ -38,12 +37,12 @@ class ShortUUID: self.alphabet_map = {c: i for i, c in enumerate(self.alphabet)} self.uuid_pad_len = int(math.ceil(math.log(1 << 128, self.alphabet_len))) - def uuid4(self, pad_to_length: Optional[int] = None) -> str: + def uuid4(self, pad_to_length: int | None = None) -> str: if pad_to_length is None: pad_to_length = self.uuid_pad_len return num_to_string(_uuid.uuid4().int, self.alphabet, self.alphabet_len, pad_to_length) - def uuid5(self, namespace: _uuid.UUID, name: str, pad_to_length: Optional[int] = None) -> str: + def uuid5(self, namespace: _uuid.UUID, name: str, pad_to_length: int | None = None) -> str: if pad_to_length is None: pad_to_length = self.uuid_pad_len return num_to_string(_uuid.uuid5(namespace, name).int, self.alphabet, self.alphabet_len, pad_to_length) @@ -56,7 +55,7 @@ _global_instance = ShortUUID() uuid4 = _global_instance.uuid4 uuid5 = _global_instance.uuid5 decode = _global_instance.decode -_escape_code_instance: Optional[ShortUUID] = None +_escape_code_instance: ShortUUID | None = None def uuid4_for_escape_code() -> str: diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index 96450633b..d95b4cbc3 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -3,14 +3,12 @@ import os import re -from collections.abc import Sequence +from collections.abc import Callable, Sequence from functools import lru_cache, partial, wraps from string import Formatter as StringFormatter from typing import ( Any, - Callable, NamedTuple, - Optional, ) from .borders import Border, BorderColor @@ -45,10 +43,10 @@ class TabBarData(NamedTuple): num_window_groups: int layout_name: str has_activity_since_last_focus: bool - active_fg: Optional[int] - active_bg: Optional[int] - inactive_fg: Optional[int] - inactive_bg: Optional[int] + active_fg: int | None + active_bg: int | None + inactive_fg: int | None + inactive_bg: int | None num_of_windows_with_progress: int total_progress: int last_focused_window_with_progress_id: int @@ -66,7 +64,7 @@ class DrawData(NamedTuple): inactive_bg: Color default_bg: Color title_template: str - active_title_template: Optional[str] + active_title_template: str | None tab_activity_symbol: str powerline_style: PowerlineStyle tab_bar_edge: EdgeLiteral @@ -170,8 +168,8 @@ class SupSub: class ExtraData: - prev_tab: Optional[TabBarData] = None - next_tab: Optional[TabBarData] = None + prev_tab: TabBarData | None = None + next_tab: TabBarData | None = None # true if the draw_tab function is called just for layout. In such cases, # if drawing is expensive the draw_tab function should avoid drawing and # just move the cursor to its final position, as if drawing was performed. @@ -574,7 +572,7 @@ class TabBar: else: self.align = lambda: None - def patch_colors(self, spec: dict[str, Optional[int]]) -> None: + def patch_colors(self, spec: dict[str, int | None]) -> None: opts = get_options() atf = spec.get('active_tab_foreground') if isinstance(atf, int): @@ -745,7 +743,7 @@ class TabBar: self.screen.reset_callbacks() del self.screen - def tab_at(self, x: int) -> Optional[int]: + def tab_at(self, x: int) -> int | None: if self.laid_out_once: x = (x - self.window_geometry.left) // self.cell_width for i, (a, b) in enumerate(self.cell_ranges): diff --git a/kitty/tabs.py b/kitty/tabs.py index 64b415fbe..daf2c3642 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -6,17 +6,15 @@ import re import stat import weakref from collections import deque -from collections.abc import Generator, Iterable, Iterator, Sequence +from collections.abc import Callable, Generator, Iterable, Iterator, Sequence from contextlib import suppress from gettext import gettext as _ from operator import attrgetter from typing import ( Any, - Callable, Deque, NamedTuple, Optional, - Union, ) from .borders import Border, Borders @@ -66,7 +64,7 @@ class TabMouseEvent(NamedTuple): modifiers: int action: int at: float - tab_idx: Optional[int] + tab_idx: int | None class TabDict(TypedDict): @@ -84,27 +82,27 @@ class TabDict(TypedDict): class SpecialWindowInstance(NamedTuple): - cmd: Optional[list[str]] - stdin: Optional[bytes] - override_title: Optional[str] - cwd_from: Optional[CwdRequest] - cwd: Optional[str] - overlay_for: Optional[int] - env: Optional[dict[str, str]] - watchers: Optional[Watchers] + cmd: list[str] | None + stdin: bytes | None + override_title: str | None + cwd_from: CwdRequest | None + cwd: str | None + overlay_for: int | None + env: dict[str, str] | None + watchers: Watchers | None overlay_behind: bool hold: bool def SpecialWindow( - cmd: Optional[list[str]], - stdin: Optional[bytes] = None, - override_title: Optional[str] = None, - cwd_from: Optional[CwdRequest] = None, - cwd: Optional[str] = None, - overlay_for: Optional[int] = None, - env: Optional[dict[str, str]] = None, - watchers: Optional[Watchers] = None, + cmd: list[str] | None, + stdin: bytes | None = None, + override_title: str | None = None, + cwd_from: CwdRequest | None = None, + cwd: str | None = None, + overlay_for: int | None = None, + env: dict[str, str] | None = None, + watchers: Watchers | None = None, overlay_behind: bool = False, hold: bool = False, ) -> SpecialWindowInstance: @@ -121,10 +119,10 @@ def add_active_id_to_history(items: Deque[int], item_id: int, maxlen: int = 64) class Tab: # {{{ - active_fg: Optional[int] = None - active_bg: Optional[int] = None - inactive_fg: Optional[int] = None - inactive_bg: Optional[int] = None + active_fg: int | None = None + active_bg: int | None = None + inactive_fg: int | None = None + inactive_bg: int | None = None confirm_close_window_id: int = 0 num_of_windows_with_progress: int = 0 total_progress: int = 0 @@ -134,8 +132,8 @@ class Tab: # {{{ self, tab_manager: 'TabManager', session_tab: Optional['SessionTab'] = None, - special_window: Optional[SpecialWindowInstance] = None, - cwd_from: Optional[CwdRequest] = None, + special_window: SpecialWindowInstance | None = None, + cwd_from: CwdRequest | None = None, no_initial_window: bool = False ): self.tab_manager_ref = weakref.ref(tab_manager) @@ -148,8 +146,8 @@ class Tab: # {{{ self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or get_options().enabled_layouts] self.borders = Borders(self.os_window_id, self.id) self.windows: WindowList = WindowList(self) - self._last_used_layout: Optional[str] = None - self._current_layout_name: Optional[str] = None + self._last_used_layout: str | None = None + self._current_layout_name: str | None = None self.cwd = self.args.directory if no_initial_window: self._set_current_layout(self.enabled_layouts[0]) @@ -286,11 +284,11 @@ class Tab: # {{{ tm.mark_tab_bar_dirty() @property - def active_window(self) -> Optional[Window]: + def active_window(self) -> Window | None: return self.windows.active_window @property - def active_window_for_cwd(self) -> Optional[Window]: + def active_window_for_cwd(self) -> Window | None: return self.windows.active_group_main @property @@ -310,11 +308,11 @@ class Tab: # {{{ ans += 1 return ans - def get_cwd_of_active_window(self, oldest: bool = False) -> Optional[str]: + def get_cwd_of_active_window(self, oldest: bool = False) -> str | None: w = self.active_window return w.get_cwd_of_child(oldest) if w else None - def get_exe_of_active_window(self, oldest: bool = False) -> Optional[str]: + def get_exe_of_active_window(self, oldest: bool = False) -> str | None: w = self.active_window return w.get_exe_of_child(oldest) if w else None @@ -430,7 +428,7 @@ class Tab: # {{{ else: self.goto_layout(layout_name) - def resize_window_by(self, window_id: int, increment: float, is_horizontal: bool) -> Optional[str]: + def resize_window_by(self, window_id: int, increment: float, is_horizontal: bool) -> str | None: increment_as_percent = self.current_layout.bias_increment_for_cell(self.windows, is_horizontal) * increment if self.current_layout.modify_size_of_window(self.windows, window_id, increment_as_percent, is_horizontal): self.relayout() @@ -473,11 +471,11 @@ class Tab: # {{{ def launch_child( self, use_shell: bool = False, - cmd: Optional[list[str]] = None, - stdin: Optional[bytes] = None, - cwd_from: Optional[CwdRequest] = None, - cwd: Optional[str] = None, - env: Optional[dict[str, str]] = None, + cmd: list[str] | None = None, + stdin: bytes | None = None, + cwd_from: CwdRequest | None = None, + cwd: str | None = None, + env: dict[str, str] | None = None, is_clone_launch: str = '', add_listen_on_env_var: bool = True, hold: bool = False, @@ -537,8 +535,8 @@ class Tab: # {{{ return ans def _add_window( - self, window: Window, location: Optional[str] = None, overlay_for: Optional[int] = None, - overlay_behind: bool = False, bias: Optional[float] = None + self, window: Window, location: str | None = None, overlay_for: int | None = None, + overlay_behind: bool = False, bias: float | None = None ) -> None: self.current_layout.add_window(self.windows, window, location, overlay_for, put_overlay_behind=overlay_behind, bias=bias) if overlay_behind and (w := self.active_window): @@ -551,23 +549,23 @@ class Tab: # {{{ def new_window( self, use_shell: bool = True, - cmd: Optional[list[str]] = None, - stdin: Optional[bytes] = None, - override_title: Optional[str] = None, - cwd_from: Optional[CwdRequest] = None, - cwd: Optional[str] = None, - overlay_for: Optional[int] = None, - env: Optional[dict[str, str]] = None, - location: Optional[str] = None, - copy_colors_from: Optional[Window] = None, + cmd: list[str] | None = None, + stdin: bytes | None = None, + override_title: str | None = None, + cwd_from: CwdRequest | None = None, + cwd: str | None = None, + overlay_for: int | None = None, + env: dict[str, str] | None = None, + location: str | None = None, + copy_colors_from: Window | None = None, allow_remote_control: bool = False, - marker: Optional[str] = None, - watchers: Optional[Watchers] = None, + marker: str | None = None, + watchers: Watchers | None = None, overlay_behind: bool = False, is_clone_launch: str = '', - remote_control_passwords: Optional[dict[str, Sequence[str]]] = None, + remote_control_passwords: dict[str, Sequence[str]] | None = None, hold: bool = False, - bias: Optional[float] = None, + bias: float | None = None, pass_fds: tuple[int, ...] = (), remote_control_fd: int = -1, ) -> Window: @@ -595,10 +593,10 @@ class Tab: # {{{ def new_special_window( self, special_window: SpecialWindowInstance, - location: Optional[str] = None, - copy_colors_from: Optional[Window] = None, + location: str | None = None, + copy_colors_from: Window | None = None, allow_remote_control: bool = False, - remote_control_passwords: Optional[dict[str, Sequence[str]]] = None, + remote_control_passwords: dict[str, Sequence[str]] | None = None, pass_fds: tuple[int, ...] = (), remote_control_fd: int = -1, ) -> Window: @@ -622,8 +620,8 @@ class Tab: # {{{ def move_window_to_top_of_group(self, window: Window) -> bool: return self.windows.move_window_to_top_of_group(window) - def overlay_parent(self, window: Window) -> Optional[Window]: - prev: Optional[Window] = None + def overlay_parent(self, window: Window) -> Window | None: + prev: Window | None = None for x in self.windows.windows_in_group_of(window): if x is window: break @@ -654,10 +652,10 @@ class Tab: # {{{ attach_window(self.os_window_id, self.id, window.id) self._add_window(window) - def set_active_window(self, x: Union[Window, int], for_keep_focus: Optional[Window] = None) -> None: + def set_active_window(self, x: Window | int, for_keep_focus: Window | None = None) -> None: self.windows.set_active_window_group_for(x, for_keep_focus=for_keep_focus) - def get_nth_window(self, n: int) -> Optional[Window]: + def get_nth_window(self, n: int) -> Window | None: if self.windows: return self.current_layout.nth_window(self.windows, n) return None @@ -734,7 +732,7 @@ class Tab: # {{{ prev_window = previous_window - def most_recent_group(self, groups: Sequence[int]) -> Optional[int]: + def most_recent_group(self, groups: Sequence[int]) -> int | None: groups_set = frozenset(groups) for window_id in reversed(self.windows.active_window_history): @@ -752,7 +750,7 @@ class Tab: # {{{ ids = tuple(reversed(self.windows.active_window_history)) return ids[min(n - 1, len(ids) - 1)] if ids else 0 - def neighboring_group_id(self, which: EdgeLiteral) -> Optional[int]: + def neighboring_group_id(self, which: EdgeLiteral) -> int | None: neighbors = self.current_layout.neighbors(self.windows) candidates = neighbors.get(which) if candidates: @@ -780,7 +778,7 @@ class Tab: # {{{ map ctrl+left move_window left map ctrl+down move_window bottom ''') - def move_window(self, delta: Union[EdgeLiteral, int] = 1) -> None: + def move_window(self, delta: EdgeLiteral | int = 1) -> None: if isinstance(delta, int): if self.current_layout.move_window(self.windows, delta): self.relayout() @@ -811,7 +809,7 @@ class Tab: # {{{ over the windows for easy selection in this mode. See :opt:`visual_window_select_characters`. ''') def focus_visible_window(self) -> None: - def callback(tab: Optional[Tab], window: Optional[Window]) -> None: + def callback(tab: Tab | None, window: Window | None) -> None: if tab and window: tab.set_active_window(window) @@ -819,7 +817,7 @@ class Tab: # {{{ @ac('win', 'Swap the current window with another window in the current tab, selected visually. See :opt:`visual_window_select_characters`') def swap_with_window(self) -> None: - def callback(tab: Optional[Tab], window: Optional[Window]) -> None: + def callback(tab: Tab | None, window: Window | None) -> None: if tab and window: tab.swap_active_window_with(window.id) get_boss().visual_window_select_action(self, callback, 'Choose window to swap with', only_window_ids=self.all_window_ids_except_active_window) @@ -838,7 +836,7 @@ class Tab: # {{{ def move_window_backward(self) -> None: self.move_window(-1) - def list_windows(self, self_window: Optional[Window] = None, window_filter: Optional[Callable[[Window], bool]] = None) -> Generator[WindowDict, None, None]: + def list_windows(self, self_window: Window | None = None, window_filter: Callable[[Window], bool] | None = None) -> Generator[WindowDict, None, None]: active_window = self.active_window for w in self: if window_filter is None or window_filter(w): @@ -920,7 +918,7 @@ class TabManager: # {{{ confirm_close_window_id: int = 0 - def __init__(self, os_window_id: int, args: CLIOptions, wm_class: str, wm_name: str, startup_session: Optional[SessionType] = None): + def __init__(self, os_window_id: int, args: CLIOptions, wm_class: str, wm_name: str, startup_session: SessionType | None = None): self.os_window_id = os_window_id self.wm_class = wm_class self.recent_mouse_events: Deque[TabMouseEvent] = deque() @@ -947,7 +945,7 @@ class TabManager: # {{{ if new_active_tab_idx == self._active_tab_idx: return try: - old_active_tab: Optional[Tab] = self.tabs[self._active_tab_idx] + old_active_tab: Tab | None = self.tabs[self._active_tab_idx] except Exception: old_active_tab = None else: @@ -955,7 +953,7 @@ class TabManager: # {{{ add_active_id_to_history(self.active_tab_history, old_active_tab.id) self._active_tab_idx = new_active_tab_idx try: - new_active_tab: Optional[Tab] = self.tabs[self._active_tab_idx] + new_active_tab: Tab | None = self.tabs[self._active_tab_idx] except Exception: new_active_tab = None if old_active_tab is not new_active_tab: @@ -1028,7 +1026,7 @@ class TabManager: # {{{ tab.relayout_borders() self.mark_tab_bar_dirty() - def set_active_tab(self, tab: Tab, for_keep_focus: Optional[Tab] = None) -> bool: + def set_active_tab(self, tab: Tab, for_keep_focus: Tab | None = None) -> bool: try: idx = self.tabs.index(tab) except Exception: @@ -1057,7 +1055,7 @@ class TabManager: # {{{ self.set_active_tab(x) break - def tab_at_location(self, loc: str) -> Optional[Tab]: + def tab_at_location(self, loc: str) -> Tab | None: if loc == 'prev': if self.active_tab_history: old_active_tab_id = self.active_tab_history[-1] @@ -1085,7 +1083,7 @@ class TabManager: # {{{ self.set_active_tab_idx(idx) break - def nth_active_tab(self, n: int = 0) -> Optional[Tab]: + def nth_active_tab(self, n: int = 0) -> Tab | None: if n <= 0: return self.active_tab tab_ids = tuple(reversed(self.active_tab_history)) @@ -1098,9 +1096,9 @@ class TabManager: # {{{ return len(self.tabs) def list_tabs( - self, self_window: Optional[Window] = None, - tab_filter: Optional[Callable[[Tab], bool]] = None, - window_filter: Optional[Callable[[Window], bool]] = None + self, self_window: Window | None = None, + tab_filter: Callable[[Tab], bool] | None = None, + window_filter: Callable[[Window], bool] | None = None ) -> Generator[TabDict, None, None]: active_tab = self.active_tab for tab in self: @@ -1130,14 +1128,14 @@ class TabManager: # {{{ } @property - def active_tab(self) -> Optional[Tab]: + def active_tab(self) -> Tab | None: try: return self.tabs[self.active_tab_idx] if self.tabs else None except Exception: return None @property - def active_window(self) -> Optional[Window]: + def active_window(self) -> Window | None: t = self.active_tab if t is not None: return t.active_window @@ -1157,7 +1155,7 @@ class TabManager: # {{{ count += len(tab) return count - def tab_for_id(self, tab_id: int) -> Optional[Tab]: + def tab_for_id(self, tab_id: int) -> Tab | None: for t in self.tabs: if t.id == tab_id: return t @@ -1176,8 +1174,8 @@ class TabManager: # {{{ def new_tab( self, - special_window: Optional[SpecialWindowInstance] = None, - cwd_from: Optional[CwdRequest] = None, + special_window: SpecialWindowInstance | None = None, + cwd_from: CwdRequest | None = None, as_neighbor: bool = False, empty_tab: bool = False, location: str = 'last' diff --git a/kitty/terminfo.py b/kitty/terminfo.py index 6751e23c3..acf63b5ff 100644 --- a/kitty/terminfo.py +++ b/kitty/terminfo.py @@ -4,7 +4,7 @@ import re from binascii import hexlify, unhexlify from collections.abc import Generator -from typing import Optional, cast +from typing import cast from kitty.options.types import Options @@ -521,7 +521,7 @@ def key_as_bytes(name: str) -> bytes: def get_capabilities(query_string: str, opts: 'Options', window_id: int = 0, os_window_id: int = 0) -> Generator[str, None, None]: from .fast_data_types import ERROR_PREFIX - def result(encoded_query_name: str, x: Optional[str] = None) -> str: + def result(encoded_query_name: str, x: str | None = None) -> str: if not x: valid = 0 if x is None else 1 return f'{valid}+r{encoded_query_name}' diff --git a/kitty/types.py b/kitty/types.py index eb7891f94..8830586cd 100644 --- a/kitty/types.py +++ b/kitty/types.py @@ -1,10 +1,10 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2021, Kovid Goyal -from collections.abc import Iterator, Mapping, Sequence +from collections.abc import Callable, Iterator, Mapping, Sequence from enum import Enum from functools import update_wrapper -from typing import TYPE_CHECKING, Any, Callable, Generic, NamedTuple, Optional, TypedDict, TypeVar, Union +from typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypedDict, TypeVar, Union if TYPE_CHECKING: from kitty.fast_data_types import SingleKey @@ -19,7 +19,7 @@ class SingleInstanceData(TypedDict): cwd: str session_data: str environ: Mapping[str, str] - notify_on_os_window_death: Optional[str] + notify_on_os_window_death: str | None class OverlayType(Enum): diff --git a/kitty/typing.py b/kitty/typing.py index 4370a8df2..80463480a 100644 --- a/kitty/typing.py +++ b/kitty/typing.py @@ -2,15 +2,15 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -BossType = ChildType = TabType = WindowType = ScreenType = None -BadLineType = SequenceMap = KeyActionType = AliasMap = None -AddressFamily = PopenType = Socket = StartupCtx = None -SessionTab = SessionType = LayoutType = SpecialWindowInstance = None -MarkType = RemoteCommandType = CoreTextFont = FontConfigPattern = None -KeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = None -GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = GRT_C = None -ScreenSize = KittensKeyActionType = MouseEvent = MouseButton = AbstractEventLoop = None -TermManagerType = LoopType = Debug = GraphicsCommandType = None +BossType = ChildType = TabType = WindowType = ScreenType = object +BadLineType = SequenceMap = KeyActionType = AliasMap = object +AddressFamily = PopenType = Socket = StartupCtx = object +SessionTab = SessionType = LayoutType = SpecialWindowInstance = object +MarkType = RemoteCommandType = object +KeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = object +GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = GRT_C = object +ScreenSize = KittensKeyActionType = MouseEvent = MouseButton = AbstractEventLoop = object +TermManagerType = LoopType = Debug = GraphicsCommandType = object ReadableBuffer = WriteableBuffer = bytearray CompletedProcess = tuple @@ -22,3 +22,4 @@ MatchType = str Protocol = object OptionsProtocol = object NotRequired = tuple +CoreTextFont = FontConfigPattern = dict diff --git a/kitty/update_check.py b/kitty/update_check.py index f5ed5fba6..da8a24fcf 100644 --- a/kitty/update_check.py +++ b/kitty/update_check.py @@ -5,7 +5,7 @@ import os import subprocess import time from contextlib import suppress -from typing import NamedTuple, Optional +from typing import NamedTuple from urllib.request import urlopen from .config import atomic_save @@ -119,7 +119,7 @@ def update_check() -> bool: return True -def update_check_callback(timer_id: Optional[int]) -> None: +def update_check_callback(timer_id: int | None) -> None: update_check() diff --git a/kitty/utils.py b/kitty/utils.py index dbea58464..771f0697a 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -7,7 +7,7 @@ import os import re import string import sys -from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence +from collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Sequence from contextlib import contextmanager, suppress from functools import lru_cache from re import Match, Pattern @@ -15,10 +15,8 @@ from typing import ( TYPE_CHECKING, Any, BinaryIO, - Callable, NamedTuple, Optional, - Union, cast, ) @@ -99,7 +97,7 @@ def kitty_ansi_sanitizer_pat() -> 're.Pattern[str]': return re.compile(r'\x1b(?:\[[0-9;:]*?m|\].*?\x1b\\)') -def platform_window_id(os_window_id: int) -> Optional[int]: +def platform_window_id(os_window_id: int) -> int | None: if is_macos: from .fast_data_types import cocoa_window_id with suppress(Exception): @@ -178,9 +176,9 @@ def read_screen_size(fd: int = -1) -> ScreenSize: class ScreenSizeGetter: changed = True Size = ScreenSize - ans: Optional[ScreenSize] = None + ans: ScreenSize | None = None - def __init__(self, fd: Optional[int]): + def __init__(self, fd: int | None): if fd is None: fd = sys.stdout.fileno() self.fd = fd @@ -193,7 +191,7 @@ class ScreenSizeGetter: @lru_cache(maxsize=64, typed=True) -def screen_size_function(fd: Optional[int] = None) -> ScreenSizeGetter: +def screen_size_function(fd: int | None = None) -> ScreenSizeGetter: return ScreenSizeGetter(fd) @@ -226,7 +224,7 @@ def base64_encode( return ans -def command_for_open(program: Union[str, list[str]] = 'default') -> list[str]: +def command_for_open(program: str | list[str] = 'default') -> list[str]: if isinstance(program, str): from .conf.utils import to_cmdline program = to_cmdline(program) @@ -237,8 +235,8 @@ def command_for_open(program: Union[str, list[str]] = 'default') -> list[str]: return cmd -def open_cmd(cmd: Union[Iterable[str], list[str]], arg: Union[None, Iterable[str], str] = None, - cwd: Optional[str] = None, extra_env: Optional[dict[str, str]] = None) -> 'PopenType[bytes]': +def open_cmd(cmd: Iterable[str] | list[str], arg: None | Iterable[str] | str = None, + cwd: str | None = None, extra_env: dict[str, str] | None = None) -> 'PopenType[bytes]': import subprocess if arg is not None: cmd = list(cmd) @@ -246,7 +244,7 @@ def open_cmd(cmd: Union[Iterable[str], list[str]], arg: Union[None, Iterable[str cmd.append(arg) else: cmd.extend(arg) - env: Optional[dict[str, str]] = None + env: dict[str, str] | None = None if extra_env: env = os.environ.copy() env.update(extra_env) @@ -255,7 +253,7 @@ def open_cmd(cmd: Union[Iterable[str], list[str]], arg: Union[None, Iterable[str preexec_fn=clear_handled_signals, env=env) -def open_url(url: str, program: Union[str, list[str]] = 'default', cwd: Optional[str] = None, extra_env: Optional[dict[str, str]] = None) -> 'PopenType[bytes]': +def open_url(url: str, program: str | list[str] = 'default', cwd: str | None = None, extra_env: dict[str, str] | None = None) -> 'PopenType[bytes]': return open_cmd(command_for_open(program), url, cwd=cwd, extra_env=extra_env) @@ -271,7 +269,7 @@ def detach(fork: bool = True, setsid: bool = True, redirect: bool = True) -> Non redirect_std_streams(os.devnull) -def init_startup_notification_x11(window_handle: int, startup_id: Optional[str] = None) -> Optional['StartupCtx']: +def init_startup_notification_x11(window_handle: int, startup_id: str | None = None) -> Optional['StartupCtx']: # https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt from kitty.fast_data_types import init_x11_startup_notification sid = startup_id or os.environ.pop('DESKTOP_STARTUP_ID', None) # ensure child processes don't get this env var @@ -289,7 +287,7 @@ def end_startup_notification_x11(ctx: 'StartupCtx') -> None: end_x11_startup_notification(ctx) -def init_startup_notification(window_handle: Optional[int], startup_id: Optional[str] = None) -> Optional['StartupCtx']: +def init_startup_notification(window_handle: int | None, startup_id: str | None = None) -> Optional['StartupCtx']: if is_macos or is_wayland(): return None if window_handle is None: @@ -328,7 +326,7 @@ class startup_notification_handler: # after the window is shown, not before, as they do not do two stage window # creation. - def __init__(self, do_notify: bool = True, startup_id: Optional[str] = None, extra_callback: Optional[Callable[[int], None]] = None): + def __init__(self, do_notify: bool = True, startup_id: str | None = None, extra_callback: Callable[[int], None] | None = None): self.do_notify = do_notify self.startup_id = startup_id self.extra_callback = extra_callback @@ -371,14 +369,14 @@ def unix_socket_paths(name: str, ext: str = '.lock') -> Generator[str, None, Non yield os.path.join(loc, filename) -def parse_address_spec(spec: str) -> tuple[AddressFamily, Union[tuple[str, int], str], Optional[str]]: +def parse_address_spec(spec: str) -> tuple[AddressFamily, tuple[str, int] | str, str | None]: import socket try: protocol, rest = spec.split(':', 1) except ValueError: raise ValueError(f'Invalid listen-on value: {spec} must be of the form protocol:address') socket_path = None - address: Union[str, tuple[str, int]] = '' + address: str | tuple[str, int] = '' if protocol == 'unix': family = socket.AF_UNIX address = rest @@ -409,7 +407,7 @@ def parse_os_window_state(state: str) -> int: }[state] -def write_all(fd: int, data: Union[str, bytes], block_until_written: bool = True) -> None: +def write_all(fd: int, data: str | bytes, block_until_written: bool = True) -> None: if isinstance(data, str): data = data.encode('utf-8') mvd = memoryview(data) @@ -448,7 +446,7 @@ class TTYIO: def read(self, limit: int) -> bytes: return os.read(self.tty_fd, limit) - def send(self, data: Union[str, bytes, Iterable[Union[str, bytes]]]) -> None: + def send(self, data: str | bytes | Iterable[str | bytes]) -> None: if isinstance(data, (str, bytes)): write_all(self.tty_fd, data) else: @@ -466,7 +464,7 @@ class TTYIO: break -def set_echo(fd: int = -1, on: bool = False) -> tuple[int, list[Union[int, list[Union[bytes, int]]]]]: +def set_echo(fd: int = -1, on: bool = False) -> tuple[int, list[int | list[bytes | int]]]: import termios if fd < 0: fd = sys.stdin.fileno() @@ -492,10 +490,10 @@ def no_echo(fd: int = -1) -> Iterator[None]: def natsort_ints(iterable: Iterable[str]) -> list[str]: - def convert(text: str) -> Union[int, str]: + def convert(text: str) -> int | str: return int(text) if text.isdigit() else text - def alphanum_key(key: str) -> tuple[Union[int, str], ...]: + def alphanum_key(key: str) -> tuple[int | str, ...]: return tuple(map(convert, re.split(r'(\d+)', key))) return sorted(iterable, key=alphanum_key) @@ -509,7 +507,7 @@ def get_hostname(fallback: str = '') -> str: return fallback -def resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> Optional[str]: +def resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> str | None: import shlex editor_cmd = list(shlex_split(editor)) editor_exe = (editor_cmd or ('',))[0] @@ -534,7 +532,7 @@ def resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> Optional[st return None -def get_editor_from_env(env: Mapping[str, str]) -> Optional[str]: +def get_editor_from_env(env: Mapping[str, str]) -> str | None: for var in ('VISUAL', 'EDITOR'): editor = env.get(var) if editor: @@ -544,7 +542,7 @@ def get_editor_from_env(env: Mapping[str, str]) -> Optional[str]: return None -def get_editor_from_env_vars(opts: Optional[Options] = None) -> list[str]: +def get_editor_from_env_vars(opts: Options | None = None) -> list[str]: editor = get_editor_from_env(os.environ) if not editor: shell_env = read_shell_environment(opts) @@ -558,7 +556,7 @@ def get_editor_from_env_vars(opts: Optional[Options] = None) -> list[str]: return list(shlex_split(ans)) -def get_editor(opts: Optional[Options] = None, path_to_edit: str = '', line_number: int = 0) -> list[str]: +def get_editor(opts: Options | None = None, path_to_edit: str = '', line_number: int = 0) -> list[str]: if opts is None: try: opts = get_options() @@ -587,7 +585,7 @@ def is_path_in_temp_dir(path: str) -> bool: if not path: return False - def abspath(x: Optional[str]) -> str: + def abspath(x: str | None) -> str: if x: x = os.path.abspath(os.path.realpath(x)) return x or '' @@ -621,7 +619,7 @@ def is_ok_to_read_image_file(path: str, fd: int) -> bool: return stat.S_ISREG(fd_stat.st_mode) -def resolve_abs_or_config_path(path: str, env: Optional[Mapping[str, str]] = None, conf_dir: Optional[str] = None) -> str: +def resolve_abs_or_config_path(path: str, env: Mapping[str, str] | None = None, conf_dir: str | None = None) -> str: path = os.path.expanduser(path) path = expandvars(path, env or {}) if not os.path.isabs(path): @@ -630,7 +628,7 @@ def resolve_abs_or_config_path(path: str, env: Optional[Mapping[str, str]] = Non def resolve_custom_file(path: str) -> str: - opts: Optional[Options] = None + opts: Options | None = None with suppress(RuntimeError): opts = get_options() return resolve_abs_or_config_path(path, opts.env if opts else {}) @@ -644,7 +642,7 @@ def func_name(f: Any) -> str: return str(f) -def resolved_shell(opts: Optional[Options] = None) -> list[str]: +def resolved_shell(opts: Options | None = None) -> list[str]: q: str = getattr(opts, 'shell', '.') if q == '.': ans = [shell_path] @@ -691,12 +689,12 @@ def system_paths_on_macos() -> tuple[str, ...]: return tuple(entries) -def which(name: str, only_system: bool = False) -> Optional[str]: +def which(name: str, only_system: bool = False) -> str | None: if os.sep in name: return name import shutil - opts: Optional[Options] = None + opts: Options | None = None with suppress(RuntimeError): opts = get_options() @@ -748,8 +746,8 @@ def which(name: str, only_system: bool = False) -> Optional[str]: return None -def read_shell_environment(opts: Optional[Options] = None) -> dict[str, str]: - ans: Optional[dict[str, str]] = getattr(read_shell_environment, 'ans', None) +def read_shell_environment(opts: Options | None = None) -> dict[str, str]: + ans: dict[str, str] | None = getattr(read_shell_environment, 'ans', None) if ans is None: from .child import openpty ans = {} @@ -775,7 +773,7 @@ def read_shell_environment(opts: Optional[Options] = None) -> dict[str, str]: start_time = monotonic() while monotonic() - start_time < 1.5: try: - ret: Optional[int] = p.wait(0.01) + ret: int | None = p.wait(0.01) except subprocess.TimeoutExpired: ret = None with suppress(Exception): @@ -832,7 +830,7 @@ def edit_config_file() -> None: class SSHConnectionData(NamedTuple): binary: str hostname: str - port: Optional[int] = None + port: int | None = None identity_file: str = '' extra_args: tuple[tuple[str, str], ...] = () @@ -932,7 +930,7 @@ def cleanup_ssh_control_masters() -> None: os.remove(x) -def path_from_osc7_url(url: Union[str, bytes]) -> str: +def path_from_osc7_url(url: str | bytes) -> str: if isinstance(url, bytes): url = url.decode('utf-8') if url.startswith('kitty-shell-cwd://'): @@ -987,7 +985,7 @@ def safer_fork() -> int: return pid -def docs_url(which: str = '', local_docs_root: Optional[str] = '') -> str: +def docs_url(which: str = '', local_docs_root: str | None = '') -> str: from urllib.parse import quote from .conf.types import resolve_ref @@ -1081,14 +1079,14 @@ def cmdline_for_hold(cmd: Sequence[str] = (), opts: Optional['Options'] = None) return [kitten_exe(), 'run-shell', f'--shell={shell}', f'--shell-integration={ksi}', '--env=KITTY_HOLD=1'] + list(cmd) -def safe_mtime(path: str) -> Optional[float]: +def safe_mtime(path: str) -> float | None: with suppress(OSError): return os.path.getmtime(path) return None @run_once -def get_custom_window_icon() -> Union[tuple[float, str], tuple[None, None]]: +def get_custom_window_icon() -> tuple[float, str] | tuple[None, None]: filenames = ['kitty.app.png'] if is_macos: # On macOS, prefer icns to png. diff --git a/kitty/window.py b/kitty/window.py index 4194434f8..a6ca18228 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -7,7 +7,7 @@ import re import sys import weakref from collections import deque -from collections.abc import Generator, Iterable, Sequence +from collections.abc import Callable, Generator, Iterable, Sequence from contextlib import contextmanager, suppress from enum import Enum, IntEnum, auto from functools import lru_cache, partial @@ -18,7 +18,6 @@ from time import time_ns from typing import ( TYPE_CHECKING, Any, - Callable, Deque, Literal, NamedTuple, @@ -156,7 +155,7 @@ class CwdRequest: return window.get_cwd_of_root_child() or '' return window.get_cwd_of_child(oldest=self.request_type is CwdRequestType.oldest) or '' - def modify_argv_for_launch_with_cwd(self, argv: list[str], env: Optional[dict[str, str]]=None) -> str: + def modify_argv_for_launch_with_cwd(self, argv: list[str], env: dict[str, str] | None=None) -> str: window = self.window if not window: return '' @@ -231,7 +230,7 @@ class WindowDict(TypedDict): is_focused: bool is_active: bool title: str - pid: Optional[int] + pid: int | None cwd: str cmdline: list[str] last_reported_cmdline: str @@ -342,7 +341,7 @@ class Watchers: def call_watchers(windowref: Callable[[], Optional['Window']], which: str, data: dict[str, Any]) -> None: - def callback(timer_id: Optional[int]) -> None: + def callback(timer_id: int | None) -> None: w = windowref() if w is not None: watchers: list[Watcher] = getattr(w.watchers, which) @@ -483,7 +482,7 @@ def transparent_background_color_control(cp: ColorProfile, responses: dict[str, cp.set_transparent_background_color(index - 1) -def color_control(cp: ColorProfile, code: int, value: Union[str, bytes, memoryview] = '') -> str: +def color_control(cp: ColorProfile, code: int, value: str | bytes | memoryview = '') -> str: if isinstance(value, (bytes, memoryview)): value = str(value, 'utf-8', 'replace') responses: dict[str, str] = {} @@ -503,7 +502,7 @@ def color_control(cp: ColorProfile, code: int, value: Union[str, bytes, memoryvi with suppress(Exception): colnum = int(key) - def serialize_color(c: Optional[Color]) -> str: + def serialize_color(c: Color | None) -> str: return '' if c is None else f'rgb:{c.red:02x}/{c.green:02x}/{c.blue:02x}' if sep == '=': @@ -546,12 +545,12 @@ def color_control(cp: ColorProfile, code: int, value: Union[str, bytes, memoryvi class EdgeWidths: - left: Optional[float] - top: Optional[float] - right: Optional[float] - bottom: Optional[float] + left: float | None + top: float | None + right: float | None + bottom: float | None - def __init__(self, serialized: Optional[dict[str, Optional[float]]] = None): + def __init__(self, serialized: dict[str, float | None] | None = None): if serialized is not None: self.left = serialized['left'] self.right = serialized['right'] @@ -560,7 +559,7 @@ class EdgeWidths: else: self.left = self.top = self.right = self.bottom = None - def serialize(self) -> dict[str, Optional[float]]: + def serialize(self) -> dict[str, float | None]: return {'left': self.left, 'right': self.right, 'top': self.top, 'bottom': self.bottom} def copy(self) -> 'EdgeWidths': @@ -570,7 +569,7 @@ class EdgeWidths: class GlobalWatchers: def __init__(self) -> None: - self.options_spec: Optional[dict[str, str]] = None + self.options_spec: dict[str, str] | None = None self.ans = Watchers() self.extra = '' @@ -619,11 +618,11 @@ class Window: tab: TabType, child: ChildType, args: CLIOptions, - override_title: Optional[str] = None, + override_title: str | None = None, copy_colors_from: Optional['Window'] = None, - watchers: Optional[Watchers] = None, + watchers: Watchers | None = None, allow_remote_control: bool = False, - remote_control_passwords: Optional[dict[str, Sequence[str]]] = None, + remote_control_passwords: dict[str, Sequence[str]] | None = None, ): if watchers: self.watchers = watchers @@ -639,16 +638,16 @@ class Window: self.created_at = time_ns() self.current_remote_data: list[str] = [] self.current_mouse_event_button = 0 - self.current_clipboard_read_ask: Optional[bool] = None + self.current_clipboard_read_ask: bool | None = None self.last_cmd_output_start_time = 0. - self.last_cmd_end_notification: Optional[tuple[int, 'OnlyWhen']] = None + self.last_cmd_end_notification: tuple[int, 'OnlyWhen'] | None = None self.open_url_handler: 'OpenUrlHandler' = None self.last_cmd_cmdline = '' self.last_cmd_exit_status = 0 self.actions_on_close: list[Callable[['Window'], None]] = [] self.actions_on_focus_change: list[Callable[['Window', bool], None]] = [] self.actions_on_removal: list[Callable[['Window'], None]] = [] - self.current_marker_spec: Optional[tuple[str, Union[str, tuple[tuple[int, str], ...]]]] = None + self.current_marker_spec: tuple[str, str | tuple[tuple[int, str], ...]] | None = None self.kitten_result_processors: list[Callable[['Window', Any], None]] = [] self.child_is_launched = False self.last_reported_pty_size = (-1, -1, -1, -1) @@ -663,12 +662,12 @@ class Window: self.clipboard_request_manager = ClipboardRequestManager(self.id) self.margin = EdgeWidths() self.padding = EdgeWidths() - self.kitten_result: Optional[dict[str, Any]] = None + self.kitten_result: dict[str, Any] | None = None if not self.id: raise Exception(f'No tab with id: {tab.id} in OS Window: {tab.os_window_id} was found, or the window counter wrapped') self.tab_id = tab.id self.os_window_id = tab.os_window_id - self.tabref: Callable[[], Optional[TabType]] = weakref.ref(tab) + self.tabref: Callable[[], TabType | None] = weakref.ref(tab) self.destroyed = False self.geometry: WindowGeometry = WindowGeometry(0, 0, 0, 0, 0, 0) self.needs_layout = True @@ -738,7 +737,7 @@ class Window: self.effective_padding('left'), self.effective_padding('top'), self.effective_padding('right'), self.effective_padding('bottom')) - def patch_edge_width(self, which: str, edge: EdgeLiteral, val: Optional[float]) -> None: + def patch_edge_width(self, which: str, edge: EdgeLiteral, val: float | None) -> None: q = self.padding if which == 'padding' else self.margin setattr(q, edge, val) if q is self.padding: @@ -819,7 +818,7 @@ class Window: return tab.overlay_parent(self) @property - def current_colors(self) -> dict[str, Union[int, None, tuple[tuple[Color, float], ...]]]: + def current_colors(self) -> dict[str, int | None | tuple[tuple[Color, float], ...]]: return self.screen.color_profile.as_dict() @property @@ -853,7 +852,7 @@ class Window: return False return False - def matches_query(self, field: str, query: str, active_tab: Optional[TabType] = None, self_window: Optional['Window'] = None) -> bool: + def matches_query(self, field: str, query: str, active_tab: TabType | None = None, self_window: Optional['Window'] = None) -> bool: if field in ('num', 'recent'): if active_tab is not None: try: @@ -890,7 +889,7 @@ class Window: t = get_boss().active_tab if t is None: return False - gid: Optional[int] = None + gid: int | None = None if query == 'left': gid = t.neighboring_group_id("left") elif query == 'right': @@ -1031,7 +1030,7 @@ class Window: text = ''.join(strings) get_boss().display_scrollback(self, text, title='Dump of lines', report_cursor=False) - def write_to_child(self, data: Union[str, bytes]) -> None: + def write_to_child(self, data: str | bytes) -> None: if data: if isinstance(data, str): data = data.encode('utf-8') @@ -1044,7 +1043,7 @@ class Window: if t is not None: t.title_changed(self) - def set_title(self, title: Optional[str]) -> None: + def set_title(self, title: str | None) -> None: if title: title = sanitize_title(title) self.override_title = title or None @@ -1068,7 +1067,7 @@ class Window: map f3 set_window_title " " ''' ) - def set_window_title(self, title: Optional[str] = None) -> None: + def set_window_title(self, title: str | None = None) -> None: if title is not None and title not in ('" "', "' '"): if title in ('""', "''"): title = '' @@ -1081,7 +1080,7 @@ class Window: _('Enter the new title for this window below. An empty title will cause the default title to be used.'), self.set_title, window=self, initial_value=prefilled) - def set_user_var(self, key: str, val: Optional[Union[str, bytes]]) -> None: + def set_user_var(self, key: str, val: str | bytes | None) -> None: key = sanitize_control_codes(key).replace('\n', ' ') self.user_vars.pop(key, None) # ensure key will be newest in user_vars even if already present if len(self.user_vars) > 64: # dont store too many user vars @@ -1130,7 +1129,7 @@ class Window: return get_boss().notification_manager.handle_notification_cmd(self.id, osc_code, raw_data) - def clear_progress_if_needed(self, timer_id: Optional[int] = None) -> None: + def clear_progress_if_needed(self, timer_id: int | None = None) -> None: # Clear stuck or completed progress if self.progress.clear_progress(): if (tab := self.tabref()) is not None: @@ -1147,7 +1146,7 @@ class Window: return False return get_boss().combine(action, window_for_dispatch=self, dispatch_type='MouseEvent') - def open_url(self, url: str, hyperlink_id: int, cwd: Optional[str] = None) -> None: + def open_url(self, url: str, hyperlink_id: int, cwd: str | None = None) -> None: boss = get_boss() try: if self.open_url_handler and self.open_url_handler(boss, self, url, hyperlink_id, cwd or ''): @@ -1184,7 +1183,7 @@ class Window: return boss.open_url(url, cwd=cwd) - def hyperlink_open_confirmed(self, url: str, cwd: Optional[str], q: str) -> None: + def hyperlink_open_confirmed(self, url: str, cwd: str | None, q: str) -> None: if q == 'o': get_boss().open_url(url, cwd=cwd) elif q == 'c': @@ -1196,7 +1195,7 @@ class Window: from .utils import SSHConnectionData args = self.ssh_kitten_cmdline() - conn_data: Union[None, list[str], SSHConnectionData] = None + conn_data: None | list[str] | SSHConnectionData = None if args: ssh_cmdline = sorted(self.child.foreground_processes, key=lambda p: p['pid'])[-1]['cmdline'] or [''] if 'ControlPath=' in ' '.join(ssh_cmdline): @@ -1251,7 +1250,7 @@ class Window: # Cancel IME composition after loses focus update_ime_position_for_window(self.id, False, -1) - def title_changed(self, new_title: Optional[memoryview], is_base64: bool = False) -> None: + def title_changed(self, new_title: memoryview | None, is_base64: bool = False) -> None: self.child_title = process_title_from_child(new_title or memoryview(b''), is_base64, self.default_title) self.call_watchers(self.watchers.on_title_change, {'title': self.child_title, 'from_child': True}) if self.override_title is None: @@ -1308,12 +1307,12 @@ class Window: if pty_size[0] > -1 and self.screen.in_band_resize_notification: self.screen.send_escape_code_to_child(ESC_CSI, f'48;{pty_size[0]};{pty_size[1]};{pty_size[3]};{pty_size[2]}t') - def color_control(self, code: int, value: Union[str, bytes, memoryview] = '') -> None: + def color_control(self, code: int, value: str | bytes | memoryview = '') -> None: response = color_control(self.screen.color_profile, code, value) if response: self.screen.send_escape_code_to_child(ESC_OSC, response) - def set_dynamic_color(self, code: int, value: Union[str, bytes, memoryview] = '') -> None: + def set_dynamic_color(self, code: int, value: str | bytes | memoryview = '') -> None: if isinstance(value, (bytes, memoryview)): value = str(value, 'utf-8', 'replace') if code == 22: @@ -1363,11 +1362,11 @@ class Window: n = 1 if self.is_dark else 2 self.screen.send_escape_code_to_child(ESC_CSI, f'?997;{n}n') - def set_color_table_color(self, code: int, bvalue: Optional[memoryview] = None) -> None: + def set_color_table_color(self, code: int, bvalue: memoryview | None = None) -> None: value = str(bvalue or b'', 'utf-8', 'replace') cp = self.screen.color_profile - def parse_color_set(raw: str) -> Generator[tuple[int, Optional[int]], None, None]: + def parse_color_set(raw: str) -> Generator[tuple[int, int | None], None, None]: parts = raw.split(';') lp = len(parts) if lp % 2 != 0: @@ -1531,7 +1530,7 @@ class Window: def file_transmission(self, data: memoryview) -> None: self.file_transmission_control.handle_serialized_command(data) - def clipboard_control(self, data: memoryview, is_partial: Optional[bool] = False) -> None: + def clipboard_control(self, data: memoryview, is_partial: bool | None = False) -> None: if is_partial is None: self.clipboard_request_manager.parse_osc_5522(data) else: @@ -1590,7 +1589,7 @@ class Window: else: raise ValueError(f'Unknown action in option `notify_on_cmd_finish`: {action}') - def cmd_output_marking(self, is_start: Optional[bool], cmdline: str = '') -> None: + def cmd_output_marking(self, is_start: bool | None, cmdline: str = '') -> None: if is_start: start_time = monotonic() self.last_cmd_output_start_time = start_time @@ -1723,17 +1722,17 @@ class Window: def cmd_output(self, which: CommandOutput = CommandOutput.last_run, as_ansi: bool = False, add_wrap_markers: bool = False) -> str: return cmd_output(self.screen, which, as_ansi, add_wrap_markers) - def get_cwd_of_child(self, oldest: bool = False) -> Optional[str]: + def get_cwd_of_child(self, oldest: bool = False) -> str | None: return self.child.get_foreground_cwd(oldest) or self.child.current_cwd - def get_cwd_of_root_child(self) -> Optional[str]: + def get_cwd_of_root_child(self) -> str | None: return self.child.current_cwd def get_exe_of_child(self, oldest: bool = False) -> str: return self.child.get_foreground_exe(oldest) or self.child.argv[0] @property - def cwd_of_child(self) -> Optional[str]: + def cwd_of_child(self) -> str | None: return self.get_cwd_of_child() @property @@ -1844,13 +1843,13 @@ class Window: if confirmed: self.paste_text(btext) - def paste_bytes(self, text: Union[str, bytes]) -> None: + def paste_bytes(self, text: str | bytes) -> None: # paste raw bytes without any processing if isinstance(text, str): text = text.encode('utf-8') self.screen.paste_bytes(text) - def paste_text(self, text: Union[str, bytes]) -> None: + def paste_text(self, text: str | bytes) -> None: if text and not self.destroyed: if isinstance(text, str): text = text.encode('utf-8') @@ -1973,42 +1972,42 @@ class Window: self.screen.clear_selection() @ac('sc', 'Scroll up by one line when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.') - def scroll_line_up(self) -> Optional[bool]: + def scroll_line_up(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll(SCROLL_LINE, True) return None return True @ac('sc', 'Scroll down by one line when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.') - def scroll_line_down(self) -> Optional[bool]: + def scroll_line_down(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll(SCROLL_LINE, False) return None return True @ac('sc', 'Scroll up by one page when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.') - def scroll_page_up(self) -> Optional[bool]: + def scroll_page_up(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll(SCROLL_PAGE, True) return None return True @ac('sc', 'Scroll down by one page when in main screen. To scroll by different amounts, you can map the remote_control scroll-window action.') - def scroll_page_down(self) -> Optional[bool]: + def scroll_page_down(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll(SCROLL_PAGE, False) return None return True @ac('sc', 'Scroll to the top of the scrollback buffer when in main screen') - def scroll_home(self) -> Optional[bool]: + def scroll_home(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll(SCROLL_FULL, True) return None return True @ac('sc', 'Scroll to the bottom of the scrollback buffer when in main screen') - def scroll_end(self) -> Optional[bool]: + def scroll_end(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll(SCROLL_FULL, False) return None @@ -2026,7 +2025,7 @@ class Window: map ctrl+n scroll_to_prompt 1 # jump to next map ctrl+o scroll_to_prompt 0 # jump to last visited ''') - def scroll_to_prompt(self, num_of_prompts: int = -1) -> Optional[bool]: + def scroll_to_prompt(self, num_of_prompts: int = -1) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll_to_prompt(num_of_prompts) return None @@ -2034,7 +2033,7 @@ class Window: @ac('sc', 'Scroll prompt to the top of the screen, filling screen with empty lines, when in main screen.' ' To avoid putting the lines above the prompt into the scrollback use scroll_prompt_to_top y') - def scroll_prompt_to_top(self, clear_scrollback: bool = False) -> Optional[bool]: + def scroll_prompt_to_top(self, clear_scrollback: bool = False) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll_until_cursor_prompt(not clear_scrollback) if self.screen.scrolled_by > 0: @@ -2043,14 +2042,14 @@ class Window: return True @ac('sc', 'Scroll prompt to the bottom of the screen, filling in extra lines from the scrollback buffer, when in main screen') - def scroll_prompt_to_bottom(self) -> Optional[bool]: + def scroll_prompt_to_bottom(self) -> bool | None: if self.screen.is_main_linebuf(): self.screen.scroll_prompt_to_bottom() return None return True @ac('mk', 'Toggle the current marker on/off') - def toggle_marker(self, ftype: str, spec: Union[str, tuple[tuple[int, str], ...]], flags: int) -> None: + def toggle_marker(self, ftype: str, spec: str | tuple[tuple[int, str], ...], flags: int) -> None: from .marks import marker_from_spec key = ftype, spec if key == self.current_marker_spec: @@ -2059,7 +2058,7 @@ class Window: self.screen.set_marker(marker_from_spec(ftype, spec, flags)) self.current_marker_spec = key - def set_marker(self, spec: Union[str, Sequence[str]]) -> None: + def set_marker(self, spec: str | Sequence[str]) -> None: from .marks import marker_from_spec from .options.utils import parse_marker_spec, toggle_marker if isinstance(spec, str): diff --git a/kitty/window_list.py b/kitty/window_list.py index 690ebb138..beaf1d216 100644 --- a/kitty/window_list.py +++ b/kitty/window_list.py @@ -6,7 +6,7 @@ from collections import deque from collections.abc import Iterator from contextlib import suppress from itertools import count -from typing import Any, Deque, Optional, Union +from typing import Any, Deque, Union from .fast_data_types import Color, get_options from .types import OverlayType, WindowGeometry @@ -129,7 +129,7 @@ class WindowGroup: return get_options().background @property - def geometry(self) -> Optional[WindowGeometry]: + def geometry(self) -> WindowGeometry | None: if self.windows: w = self.windows[-1] return w.geometry @@ -191,7 +191,7 @@ class WindowList: ans.append(w) return ans - def notify_on_active_window_change(self, old_active_window: Optional[WindowType], new_active_window: Optional[WindowType]) -> None: + def notify_on_active_window_change(self, old_active_window: WindowType | None, new_active_window: WindowType | None) -> None: if old_active_window is not None: old_active_window.focus_changed(False) if new_active_window is not None: @@ -263,14 +263,14 @@ class WindowList: def num_groups(self) -> int: return len(self.groups) - def group_for_window(self, x: WindowOrId) -> Optional[WindowGroup]: + def group_for_window(self, x: WindowOrId) -> WindowGroup | None: q = self.id_map[x] if isinstance(x, int) else x for g in self.groups: if q in g: return g return None - def group_idx_for_window(self, x: WindowOrId) -> Optional[int]: + def group_idx_for_window(self, x: WindowOrId) -> int | None: q = self.id_map[x] if isinstance(x, int) else x for i, g in enumerate(self.groups): if q in g: @@ -297,24 +297,24 @@ class WindowList: return iter(()) @property - def active_group(self) -> Optional[WindowGroup]: + def active_group(self) -> WindowGroup | None: with suppress(Exception): return self.groups[self.active_group_idx] return None @property - def active_window(self) -> Optional[WindowType]: + def active_window(self) -> WindowType | None: with suppress(Exception): return self.id_map[self.groups[self.active_group_idx].active_window_id] return None @property - def active_group_main(self) -> Optional[WindowType]: + def active_group_main(self) -> WindowType | None: with suppress(Exception): return self.id_map[self.groups[self.active_group_idx].main_window_id] return None - def set_active_window_group_for(self, x: WindowOrId, for_keep_focus: Optional[WindowType] = None) -> None: + def set_active_window_group_for(self, x: WindowOrId, for_keep_focus: WindowType | None = None) -> None: try: q = self.id_map[x] if isinstance(x, int) else x except KeyError: @@ -331,15 +331,15 @@ class WindowList: def add_window( self, window: WindowType, - group_of: Optional[WindowOrId] = None, - next_to: Optional[WindowOrId] = None, + group_of: WindowOrId | None = None, + next_to: WindowOrId | None = None, before: bool = False, make_active: bool = True, head_of_group: bool = False, ) -> WindowGroup: self.all_windows.append(window) self.id_map[window.id] = window - target_group: Optional[WindowGroup] = None + target_group: WindowGroup | None = None if group_of is not None: target_group = self.group_for_window(group_of) @@ -396,14 +396,14 @@ class WindowList: if old_active_window is not new_active_window: self.notify_on_active_window_change(old_active_window, new_active_window) - def active_window_in_nth_group(self, n: int, clamp: bool = False) -> Optional[WindowType]: + def active_window_in_nth_group(self, n: int, clamp: bool = False) -> WindowType | None: if clamp: n = max(0, min(n, self.num_groups - 1)) if 0 <= n < self.num_groups: return self.id_map.get(self.groups[n].active_window_id) return None - def active_window_in_group_id(self, group_id: int) -> Optional[WindowType]: + def active_window_in_group_id(self, group_id: int) -> WindowType | None: for g in self.groups: if g.id == group_id: return self.id_map.get(g.active_window_id) @@ -412,7 +412,7 @@ class WindowList: def activate_next_window_group(self, delta: int) -> None: self.set_active_group_idx(wrap_increment(self.active_group_idx, self.num_groups, delta)) - def move_window_group(self, by: Optional[int] = None, to_group: Optional[int] = None) -> bool: + def move_window_group(self, by: int | None = None, to_group: int | None = None) -> bool: if self.active_group_idx < 0 or not self.groups: return False target = -1 diff --git a/kitty_tests/__init__.py b/kitty_tests/__init__.py index dad64557e..146e92c35 100644 --- a/kitty_tests/__init__.py +++ b/kitty_tests/__init__.py @@ -15,7 +15,6 @@ import time from contextlib import contextmanager, suppress from functools import wraps from pty import CHILD, STDIN_FILENO, STDOUT_FILENO, fork -from typing import Optional from unittest import TestCase from kitty.config import finalize_keys, finalize_mouse_mappings @@ -91,7 +90,7 @@ class Callbacks: def color_profile_popped(self, x) -> None: pass - def cmd_output_marking(self, is_start: Optional[bool], data: str = '') -> None: + def cmd_output_marking(self, is_start: bool | None, data: str = '') -> None: if is_start: self.last_cmd_at = monotonic() self.last_cmd_cmdline = decode_cmdline(data) if data else data diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index 4396420f9..e2c9d41dd 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -5,10 +5,10 @@ import array import os import tempfile import unittest +from collections.abc import Iterable from functools import lru_cache, partial from itertools import repeat from math import ceil -from typing import Iterable from kitty.constants import is_macos, read_kitty_resource from kitty.fast_data_types import ( diff --git a/kitty_tests/main.py b/kitty_tests/main.py index ef63a657c..986cd479f 100644 --- a/kitty_tests/main.py +++ b/kitty_tests/main.py @@ -9,14 +9,13 @@ import subprocess import sys import time import unittest -from collections.abc import Generator, Iterator, Sequence +from collections.abc import Callable, Generator, Iterator, Sequence from contextlib import contextmanager from functools import lru_cache from tempfile import TemporaryDirectory from threading import Thread from typing import ( Any, - Callable, NoReturn, Optional, )