Update codebase to Python 3.10 using pyupgrade

This commit is contained in:
Kovid Goyal
2025-01-10 22:31:41 +05:30
parent 922d08516b
commit da1626090a
126 changed files with 1739 additions and 1764 deletions

View File

@@ -2,10 +2,6 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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)

View File

@@ -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:

View File

@@ -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():

View File

@@ -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(':')

View File

@@ -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(

View File

@@ -2,8 +2,9 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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:

View File

@@ -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')

View File

@@ -2,7 +2,8 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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')

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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')

View File

@@ -2,7 +2,6 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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__':

View File

@@ -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:

View File

@@ -2,7 +2,6 @@
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
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__':

View File

@@ -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')

View File

@@ -2,8 +2,8 @@
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import os
from collections.abc import Generator
from contextlib import contextmanager
from typing import Generator
_cwd = _home = ''

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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:]

View File

@@ -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

View File

@@ -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))

View File

@@ -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))

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
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": ["", "", "", "", "", "", "", "", "", ""]

View File

@@ -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:

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env python
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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__':