Skip to content

Commit

Permalink
Rely on method_cache to cache method results.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Aug 5, 2023
1 parent 578fc8e commit 0a05a1a
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
15 changes: 5 additions & 10 deletions zipp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
import posixpath
import zipfile
import itertools
import contextlib
import pathlib
import re

from .py310compat import text_encoding
from .glob import translate

from ._functools import save_method_args
from ._functools import save_method_args, method_cache


__all__ = ['Path']
Expand Down Expand Up @@ -156,17 +155,13 @@ class FastLookup(CompleteDirs):
dirs exist and are resolved rapidly.
"""

@method_cache
def namelist(self):
with contextlib.suppress(AttributeError):
return self.__names
self.__names = super().namelist()
return self.__names
return super().namelist()

@method_cache
def _name_set(self):
with contextlib.suppress(AttributeError):
return self.__lookup
self.__lookup = super()._name_set()
return self.__lookup
return super()._name_set()


def _extract_text_encoding(encoding=None, *args, **kwargs):
Expand Down
60 changes: 60 additions & 0 deletions zipp/_functools.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import collections
import functools
import types

from typing import TypeVar, Callable


# from jaraco.functools 3.5.2
Expand All @@ -14,3 +17,60 @@ def wrapper(self, *args, **kwargs):
return method(self, *args, **kwargs)

return wrapper


# from jaraco.functools 3.5.2
CallableT = TypeVar("CallableT", bound=Callable[..., object])


def method_cache(
method: CallableT,
cache_wrapper: Callable[
[CallableT], CallableT
] = functools.lru_cache(), # type: ignore[assignment]
) -> CallableT:
def wrapper(self: object, *args: object, **kwargs: object) -> object:
# it's the first call, replace the method with a cached, bound method
bound_method: CallableT = types.MethodType( # type: ignore[assignment]
method, self
)
cached_method = cache_wrapper(bound_method)
setattr(self, method.__name__, cached_method)
return cached_method(*args, **kwargs)

# Support cache clear even before cache has been created.
wrapper.cache_clear = lambda: None # type: ignore[attr-defined]

return ( # type: ignore[return-value]
_special_method_cache(method, cache_wrapper) or wrapper
)


def _special_method_cache(method, cache_wrapper):
"""
Because Python treats special methods differently, it's not
possible to use instance attributes to implement the cached
methods.
Instead, install the wrapper method under a different name
and return a simple proxy to that wrapper.
https://github.com/jaraco/jaraco.functools/issues/5
"""
name = method.__name__
special_names = '__getattr__', '__getitem__'
if name not in special_names:
return

wrapper_name = '__cached' + name

def proxy(self, *args, **kwargs):
if wrapper_name not in vars(self):
bound = types.MethodType(method, self)
cache = cache_wrapper(bound)
setattr(self, wrapper_name, cache)
else:
cache = getattr(self, wrapper_name)
return cache(*args, **kwargs)

return proxy

0 comments on commit 0a05a1a

Please sign in to comment.