odps/models/cache.py (106 lines of code) (raw):

#!/usr/bin/env python # -*- coding: utf-8 -*- #!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright 1999-2025 Alibaba Group Holding Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import inspect import weakref from ..compat import six class ObjectCache(object): def __init__(self): self._caches = weakref.WeakValueDictionary() @staticmethod def _get_cache_class(cls): if hasattr(cls, "_cache_class"): cls._cache_class = ObjectCache._get_cache_class(cls._cache_class) return cls._cache_class return cls def _fetch(self, cache_key): client, parent, _, _ = cache_key if parent is None: return self._caches.get(cache_key) ancestor = getattr(parent, "_parent") parent_cls = self._get_cache_class(type(parent)) if parent_cls is None: return None name = getattr(parent, "name", parent_cls.__name__.lower()) parent_cache_key = client, ancestor, parent_cls, name if self._fetch(parent_cache_key): return self._caches.get(cache_key) def _get_cache(self, cls, **kw): kwargs = dict(kw) parent = kwargs.pop("parent", None) or kwargs.pop("_parent", None) name = kwargs.pop(getattr(cls, "_cache_name_arg", "name"), None) client = kwargs.pop("client", None) or kwargs.pop("_client", None) cache_cls = self._get_cache_class(cls) cache_key = client, parent, cache_cls, name obj = None if name is not None: obj = self._fetch(cache_key) if obj is not None: if not frozenset(kwargs).issubset(obj.__slots__): obj = None else: for k, v in six.iteritems(kwargs): setattr(obj, k, v) if obj is not None: return cache_key, obj return cache_key, obj def cache_lazyload(self, func, cls, **kwargs): cache_key, obj = self._get_cache(cls, **kwargs) if obj is not None: return obj obj = func(cls, **kwargs) if not hasattr(cls, "_filter_cache"): self._caches[cache_key] = obj elif cls._filter_cache(func, **obj.extract(**kwargs)): self._caches[cache_key] = obj return obj def cache_container(self, func, cls, **kwargs): parent = kwargs.get("parent") or kwargs.get("_parent") client = kwargs.get("client") or kwargs.get("_client") name = cls.__name__.lower() cache_key = client, parent, cls, name if name is not None: obj = self._fetch(cache_key) if obj is not None: return obj obj = func(cls, **kwargs) self._caches[cache_key] = obj return obj def del_item_cache(self, obj, item): from .core import LazyLoad try: item = obj._get_parent_typed(item) except (AttributeError, NotImplementedError): item = obj[item] client = getattr(item, "_client") parent = getattr(item, "_parent") name = item._name() if name is not None: clz = self._get_cache_class(type(item)) cache_key = client, parent, clz, name if cache_key in self._caches: # make sure original object will be reloaded obj = self._caches[cache_key] if isinstance(obj, LazyLoad): obj.reset() del self._caches[cache_key] _object_cache = ObjectCache() def cache(func): def inner(cls, **kwargs): bases = [base.__name__ for base in inspect.getmro(cls)] if "LazyLoad" in bases: return _object_cache.cache_lazyload(func, cls, **kwargs) elif "Container" in bases: return _object_cache.cache_container(func, cls, **kwargs) return func(cls, **kwargs) inner.__name__ = func.__name__ inner.__doc__ = func.__doc__ return inner def del_cache(func): def inner(obj, item): if func.__name__ == "__delitem__": _object_cache.del_item_cache(obj, item) return func(obj, item) inner.__name__ = func.__name__ inner.__doc__ = func.__doc__ inner._cache_maker = True return inner def cache_parent(cls): cls._cache_class = cls.__bases__[0] return cls