diff --git a/kittens/diff/render.py b/kittens/diff/render.py index 1db4d6e29..4bb2a490e 100644 --- a/kittens/diff/render.py +++ b/kittens/diff/render.py @@ -3,16 +3,17 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal import warnings -from functools import lru_cache from gettext import gettext as _ from itertools import repeat, zip_longest from math import ceil from typing import Callable, Dict, Generator, Iterable, List, Optional, Tuple -from kitty.fast_data_types import truncate_point_for_length, wcswidth from kitty.cli_stub import DiffCLIOptions +from kitty.fast_data_types import truncate_point_for_length, wcswidth +from kitty.types import run_once from kitty.utils import ScreenSize +from ..tui.images import ImageManager, can_display_images from .collect import ( Collection, Segment, data_for_path, highlights_for_path, is_image, lines_for_path, path_name_map, sanitize @@ -20,14 +21,13 @@ from .collect import ( from .config import formats from .diff_speedup import split_with_highlights as _split_with_highlights from .patch import Chunk, Hunk, Patch -from ..tui.images import ImageManager, can_display_images class ImageSupportWarning(Warning): pass -@lru_cache(maxsize=2) +@run_once def images_supported() -> bool: ans = can_display_images() if not ans: diff --git a/kittens/icat/main.py b/kittens/icat/main.py index 475af526c..54d4c4826 100755 --- a/kittens/icat/main.py +++ b/kittens/icat/main.py @@ -10,7 +10,6 @@ import socket import sys import zlib from base64 import standard_b64encode -from functools import lru_cache from math import ceil from tempfile import NamedTemporaryFile from typing import ( @@ -21,6 +20,7 @@ from kitty.cli import parse_args from kitty.cli_stub import IcatCLIOptions from kitty.constants import appname from kitty.guess_mime_type import guess_type +from kitty.types import run_once from kitty.typing import GRT_f, GRT_t from kitty.utils import ( TTYIO, ScreenSize, ScreenSizeGetter, fit_image, screen_size_function @@ -142,7 +142,7 @@ def get_screen_size() -> ScreenSize: return screen_size() -@lru_cache(maxsize=2) +@run_once def options_spec() -> str: return OPTIONS.format(appname='{}-icat'.format(appname)) diff --git a/kittens/runner.py b/kittens/runner.py index 267cfc219..c420b14f4 100644 --- a/kittens/runner.py +++ b/kittens/runner.py @@ -6,9 +6,11 @@ import importlib import os import sys -from functools import lru_cache, partial +from functools import partial from typing import Any, Dict, FrozenSet, List +from kitty.types import run_once + aliases = {'url_hints': 'hints'} @@ -57,8 +59,9 @@ def create_kitten_handler(kitten: str, orig_args: List[str]) -> Any: def set_debug(kitten: str) -> None: - from kittens.tui.loop import debug import builtins + + from kittens.tui.loop import debug setattr(builtins, 'debug', debug) @@ -118,7 +121,7 @@ def run_kitten(kitten: str, run_name: str = '__main__') -> None: m['main'](sys.argv) -@lru_cache(maxsize=2) +@run_once def all_kitten_names() -> FrozenSet[str]: n = [] import glob diff --git a/kitty/complete.py b/kitty/complete.py index b4b32bdda..c1259aa58 100644 --- a/kitty/complete.py +++ b/kitty/complete.py @@ -5,7 +5,6 @@ import os import shlex import sys -from functools import lru_cache from typing import ( Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple ) @@ -17,6 +16,7 @@ from .cli import ( ) from .rc.base import all_command_names, command_for_name from .shell import options_for_cmd +from .types import run_once ''' To add completion for a new shell, you need to: @@ -75,7 +75,7 @@ class Completions: self.delegate: Delegate = Delegate() -@lru_cache(maxsize=2) +@run_once def remote_control_command_names() -> Tuple[str, ...]: return tuple(sorted(x.replace('_', '-') for x in all_command_names())) diff --git a/kitty/constants.py b/kitty/constants.py index 84ebbb9e8..a4170bfa7 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -7,10 +7,10 @@ import os import pwd import sys from contextlib import suppress -from functools import lru_cache from typing import NamedTuple, Optional, Set from .options_stub import Options +from .types import run_once class Version(NamedTuple): @@ -27,7 +27,7 @@ is_macos: bool = 'darwin' in _plat base = os.path.dirname(os.path.abspath(__file__)) -@lru_cache(maxsize=2) +@run_once def kitty_exe() -> str: rpath = sys._xoptions.get('bundle_exe_dir') if not rpath: @@ -93,11 +93,8 @@ del _get_config_dir defconf = os.path.join(config_dir, 'kitty.conf') -@lru_cache(maxsize=2) +@run_once def cache_dir() -> str: - override: Optional[str] = getattr(cache_dir, 'override_dir', None) - if override: - return override if 'KITTY_CACHE_DIRECTORY' in os.environ: candidate = os.path.abspath(os.environ['KITTY_CACHE_DIRECTORY']) elif is_macos: diff --git a/kitty/launch.py b/kitty/launch.py index b9b43b87f..2f4343e31 100644 --- a/kitty/launch.py +++ b/kitty/launch.py @@ -3,7 +3,6 @@ # License: GPLv3 Copyright: 2019, Kovid Goyal -from functools import lru_cache from typing import Any, Dict, List, NamedTuple, Optional, Sequence from .boss import Boss @@ -13,6 +12,7 @@ from .cli_stub import LaunchCLIOptions from .constants import resolve_custom_file from .fast_data_types import patch_color_profiles, set_clipboard_string from .tabs import Tab +from .types import run_once from .utils import find_exe, read_shell_environment, set_primary_selection from .window import Watchers, Window @@ -27,7 +27,7 @@ class LaunchSpec(NamedTuple): args: List[str] -@lru_cache(maxsize=2) +@run_once def options_spec() -> str: return ''' --window-title --title diff --git a/kitty/open_actions.py b/kitty/open_actions.py index 408c99502..a2635dbae 100644 --- a/kitty/open_actions.py +++ b/kitty/open_actions.py @@ -6,7 +6,6 @@ import os import posixpath from contextlib import suppress -from functools import lru_cache from typing import ( Any, Generator, Iterable, List, NamedTuple, Optional, Tuple, cast ) @@ -15,9 +14,10 @@ from urllib.parse import ParseResult, unquote, urlparse from .conf.utils import to_cmdline from .config import KeyAction, parse_key_action from .constants import config_dir +from .guess_mime_type import guess_type +from .types import run_once from .typing import MatchType from .utils import expandvars, log_error -from .guess_mime_type import guess_type class MatchCriteria(NamedTuple): @@ -175,7 +175,7 @@ def actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Genera return -@lru_cache(maxsize=2) +@run_once def load_open_actions() -> Tuple[OpenAction, ...]: try: f = open(os.path.join(config_dir, 'open-actions.conf')) diff --git a/kitty/shell.py b/kitty/shell.py index 15aadf512..b3feb71cd 100644 --- a/kitty/shell.py +++ b/kitty/shell.py @@ -21,9 +21,10 @@ from .rc.base import ( RemoteCommand, all_command_names, command_for_name, display_subcommand_help, parse_subcommand_cli ) +from .types import run_once -@lru_cache(maxsize=2) +@run_once def match_commands() -> Tuple[str, ...]: all_commands = tuple(sorted(x.replace('_', '-') for x in all_command_names())) return tuple(sorted(all_commands + ('exit', 'help', 'quit'))) diff --git a/kitty/types.py b/kitty/types.py index 358dad0e4..fc554125b 100644 --- a/kitty/types.py +++ b/kitty/types.py @@ -2,7 +2,10 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2021, Kovid Goyal -from typing import NamedTuple, Union +from functools import update_wrapper +from typing import TYPE_CHECKING, Callable, Generic, NamedTuple, TypeVar, Union + +_T = TypeVar('_T') class ParsedShortcut(NamedTuple): @@ -50,3 +53,36 @@ class SingleKey(NamedTuple): ConvertibleToNumbers = Union[str, bytes, int, float] + + +if TYPE_CHECKING: + class RunOnce(Generic[_T]): + + def __init__(self, func: Callable[[], _T]): ... + def __call__(self) -> _T: ... + def set_override(self, val: _T) -> None: ... + def clear_override(self) -> None: ... +else: + class RunOnce: + + def __init__(self, f): + self._override = RunOnce + self._cached_result = RunOnce + update_wrapper(self, f) + + def __call__(self): + if self._override is not RunOnce: + return self._override + if self._cached_result is RunOnce: + self._cached_result = self.__wrapped__() + return self._cached_result + + def set_override(self, val): + self._override = val + + def clear_override(self): + self._override = RunOnce + + +def run_once(f: Callable[[], _T]) -> RunOnce: + return RunOnce(f) diff --git a/kitty/utils.py b/kitty/utils.py index cbaeb845b..3a0f8d848 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -23,7 +23,7 @@ from .constants import ( ) from .options_stub import Options from .rgb import Color, to_color -from .types import ConvertibleToNumbers +from .types import ConvertibleToNumbers, run_once from .typing import AddressFamily, PopenType, Socket, StartupCtx BASE = os.path.dirname(os.path.abspath(__file__)) @@ -463,7 +463,7 @@ def natsort_ints(iterable: Iterable[str]) -> List[str]: return sorted(iterable, key=alphanum_key) -@lru_cache(maxsize=2) +@run_once def get_editor() -> List[str]: import shlex import shutil @@ -512,7 +512,7 @@ def resolved_shell(opts: Optional[Options] = None) -> List[str]: return ans -@lru_cache(maxsize=2) +@run_once def system_paths_on_macos() -> List[str]: entries, seen = [], set() diff --git a/kitty_tests/graphics.py b/kitty_tests/graphics.py index f4d0b8bfe..47ee3abe3 100644 --- a/kitty_tests/graphics.py +++ b/kitty_tests/graphics.py @@ -180,12 +180,11 @@ def make_send_command(screen): class TestGraphics(BaseTest): def setUp(self): - cache_dir.override_dir = tempfile.mkdtemp() - cache_dir.cache_clear() + cache_dir.set_override(tempfile.mkdtemp()) def tearDown(self): - os.rmdir(cache_dir.override_dir) - cache_dir.override_dir = None + os.rmdir(cache_dir()) + cache_dir.clear_override() def test_xor_data(self):