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> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import warnings import warnings
from functools import lru_cache
from gettext import gettext as _ from gettext import gettext as _
from itertools import repeat, zip_longest from itertools import repeat, zip_longest
from math import ceil from math import ceil
from typing import Callable, Dict, Generator, Iterable, List, Optional, Tuple 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.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 kitty.utils import ScreenSize
from ..tui.images import ImageManager, can_display_images
from .collect import ( from .collect import (
Collection, Segment, data_for_path, highlights_for_path, is_image, Collection, Segment, data_for_path, highlights_for_path, is_image,
lines_for_path, path_name_map, sanitize lines_for_path, path_name_map, sanitize
@ -20,14 +21,13 @@ from .collect import (
from .config import formats from .config import formats
from .diff_speedup import split_with_highlights as _split_with_highlights from .diff_speedup import split_with_highlights as _split_with_highlights
from .patch import Chunk, Hunk, Patch from .patch import Chunk, Hunk, Patch
from ..tui.images import ImageManager, can_display_images
class ImageSupportWarning(Warning): class ImageSupportWarning(Warning):
pass pass
@lru_cache(maxsize=2) @run_once
def images_supported() -> bool: def images_supported() -> bool:
ans = can_display_images() ans = can_display_images()
if not ans: if not ans:

View File

@ -10,7 +10,6 @@ import socket
import sys import sys
import zlib import zlib
from base64 import standard_b64encode from base64 import standard_b64encode
from functools import lru_cache
from math import ceil from math import ceil
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import ( from typing import (
@ -21,6 +20,7 @@ from kitty.cli import parse_args
from kitty.cli_stub import IcatCLIOptions from kitty.cli_stub import IcatCLIOptions
from kitty.constants import appname from kitty.constants import appname
from kitty.guess_mime_type import guess_type from kitty.guess_mime_type import guess_type
from kitty.types import run_once
from kitty.typing import GRT_f, GRT_t from kitty.typing import GRT_f, GRT_t
from kitty.utils import ( from kitty.utils import (
TTYIO, ScreenSize, ScreenSizeGetter, fit_image, screen_size_function TTYIO, ScreenSize, ScreenSizeGetter, fit_image, screen_size_function
@ -142,7 +142,7 @@ def get_screen_size() -> ScreenSize:
return screen_size() return screen_size()
@lru_cache(maxsize=2) @run_once
def options_spec() -> str: def options_spec() -> str:
return OPTIONS.format(appname='{}-icat'.format(appname)) return OPTIONS.format(appname='{}-icat'.format(appname))

View File

@ -6,9 +6,11 @@
import importlib import importlib
import os import os
import sys import sys
from functools import lru_cache, partial from functools import partial
from typing import Any, Dict, FrozenSet, List from typing import Any, Dict, FrozenSet, List
from kitty.types import run_once
aliases = {'url_hints': 'hints'} 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: def set_debug(kitten: str) -> None:
from kittens.tui.loop import debug
import builtins import builtins
from kittens.tui.loop import debug
setattr(builtins, 'debug', debug) setattr(builtins, 'debug', debug)
@ -118,7 +121,7 @@ def run_kitten(kitten: str, run_name: str = '__main__') -> None:
m['main'](sys.argv) m['main'](sys.argv)
@lru_cache(maxsize=2) @run_once
def all_kitten_names() -> FrozenSet[str]: def all_kitten_names() -> FrozenSet[str]:
n = [] n = []
import glob import glob

View File

@ -5,7 +5,6 @@
import os import os
import shlex import shlex
import sys import sys
from functools import lru_cache
from typing import ( from typing import (
Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple 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 .rc.base import all_command_names, command_for_name
from .shell import options_for_cmd from .shell import options_for_cmd
from .types import run_once
''' '''
To add completion for a new shell, you need to: To add completion for a new shell, you need to:
@ -75,7 +75,7 @@ class Completions:
self.delegate: Delegate = Delegate() self.delegate: Delegate = Delegate()
@lru_cache(maxsize=2) @run_once
def remote_control_command_names() -> Tuple[str, ...]: def remote_control_command_names() -> Tuple[str, ...]:
return tuple(sorted(x.replace('_', '-') for x in all_command_names())) return tuple(sorted(x.replace('_', '-') for x in all_command_names()))

View File

@ -7,10 +7,10 @@ import os
import pwd import pwd
import sys import sys
from contextlib import suppress from contextlib import suppress
from functools import lru_cache
from typing import NamedTuple, Optional, Set from typing import NamedTuple, Optional, Set
from .options_stub import Options from .options_stub import Options
from .types import run_once
class Version(NamedTuple): class Version(NamedTuple):
@ -27,7 +27,7 @@ is_macos: bool = 'darwin' in _plat
base = os.path.dirname(os.path.abspath(__file__)) base = os.path.dirname(os.path.abspath(__file__))
@lru_cache(maxsize=2) @run_once
def kitty_exe() -> str: def kitty_exe() -> str:
rpath = sys._xoptions.get('bundle_exe_dir') rpath = sys._xoptions.get('bundle_exe_dir')
if not rpath: if not rpath:
@ -93,11 +93,8 @@ del _get_config_dir
defconf = os.path.join(config_dir, 'kitty.conf') defconf = os.path.join(config_dir, 'kitty.conf')
@lru_cache(maxsize=2) @run_once
def cache_dir() -> str: def cache_dir() -> str:
override: Optional[str] = getattr(cache_dir, 'override_dir', None)
if override:
return override
if 'KITTY_CACHE_DIRECTORY' in os.environ: if 'KITTY_CACHE_DIRECTORY' in os.environ:
candidate = os.path.abspath(os.environ['KITTY_CACHE_DIRECTORY']) candidate = os.path.abspath(os.environ['KITTY_CACHE_DIRECTORY'])
elif is_macos: elif is_macos:

View File

@ -3,7 +3,6 @@
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net> # 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 typing import Any, Dict, List, NamedTuple, Optional, Sequence
from .boss import Boss from .boss import Boss
@ -13,6 +12,7 @@ from .cli_stub import LaunchCLIOptions
from .constants import resolve_custom_file from .constants import resolve_custom_file
from .fast_data_types import patch_color_profiles, set_clipboard_string from .fast_data_types import patch_color_profiles, set_clipboard_string
from .tabs import Tab from .tabs import Tab
from .types import run_once
from .utils import find_exe, read_shell_environment, set_primary_selection from .utils import find_exe, read_shell_environment, set_primary_selection
from .window import Watchers, Window from .window import Watchers, Window
@ -27,7 +27,7 @@ class LaunchSpec(NamedTuple):
args: List[str] args: List[str]
@lru_cache(maxsize=2) @run_once
def options_spec() -> str: def options_spec() -> str:
return ''' return '''
--window-title --title --window-title --title

View File

@ -6,7 +6,6 @@
import os import os
import posixpath import posixpath
from contextlib import suppress from contextlib import suppress
from functools import lru_cache
from typing import ( from typing import (
Any, Generator, Iterable, List, NamedTuple, Optional, Tuple, cast 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 .conf.utils import to_cmdline
from .config import KeyAction, parse_key_action from .config import KeyAction, parse_key_action
from .constants import config_dir from .constants import config_dir
from .guess_mime_type import guess_type
from .types import run_once
from .typing import MatchType from .typing import MatchType
from .utils import expandvars, log_error from .utils import expandvars, log_error
from .guess_mime_type import guess_type
class MatchCriteria(NamedTuple): class MatchCriteria(NamedTuple):
@ -175,7 +175,7 @@ def actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Genera
return return
@lru_cache(maxsize=2) @run_once
def load_open_actions() -> Tuple[OpenAction, ...]: def load_open_actions() -> Tuple[OpenAction, ...]:
try: try:
f = open(os.path.join(config_dir, 'open-actions.conf')) 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, RemoteCommand, all_command_names, command_for_name,
display_subcommand_help, parse_subcommand_cli display_subcommand_help, parse_subcommand_cli
) )
from .types import run_once
@lru_cache(maxsize=2) @run_once
def match_commands() -> Tuple[str, ...]: def match_commands() -> Tuple[str, ...]:
all_commands = tuple(sorted(x.replace('_', '-') for x in all_command_names())) all_commands = tuple(sorted(x.replace('_', '-') for x in all_command_names()))
return tuple(sorted(all_commands + ('exit', 'help', 'quit'))) return tuple(sorted(all_commands + ('exit', 'help', 'quit')))

View File

@ -2,7 +2,10 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net> # 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): class ParsedShortcut(NamedTuple):
@ -50,3 +53,36 @@ class SingleKey(NamedTuple):
ConvertibleToNumbers = Union[str, bytes, int, float] 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 .options_stub import Options
from .rgb import Color, to_color from .rgb import Color, to_color
from .types import ConvertibleToNumbers from .types import ConvertibleToNumbers, run_once
from .typing import AddressFamily, PopenType, Socket, StartupCtx from .typing import AddressFamily, PopenType, Socket, StartupCtx
BASE = os.path.dirname(os.path.abspath(__file__)) 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) return sorted(iterable, key=alphanum_key)
@lru_cache(maxsize=2) @run_once
def get_editor() -> List[str]: def get_editor() -> List[str]:
import shlex import shlex
import shutil import shutil
@ -512,7 +512,7 @@ def resolved_shell(opts: Optional[Options] = None) -> List[str]:
return ans return ans
@lru_cache(maxsize=2) @run_once
def system_paths_on_macos() -> List[str]: def system_paths_on_macos() -> List[str]:
entries, seen = [], set() entries, seen = [], set()

View File

@ -180,12 +180,11 @@ def make_send_command(screen):
class TestGraphics(BaseTest): class TestGraphics(BaseTest):
def setUp(self): def setUp(self):
cache_dir.override_dir = tempfile.mkdtemp() cache_dir.set_override(tempfile.mkdtemp())
cache_dir.cache_clear()
def tearDown(self): def tearDown(self):
os.rmdir(cache_dir.override_dir) os.rmdir(cache_dir())
cache_dir.override_dir = None cache_dir.clear_override()
def test_xor_data(self): def test_xor_data(self):