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