Use a nicer decorator for functions that only need to be run once

This commit is contained in:
Kovid Goyal 2021-02-05 10:40:52 +05:30
parent 726d736aac
commit fe07306ff1
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 67 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,10 @@
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
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)

View File

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

View File

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