Skip to content

Dict traversal and CachedProperty updates. #483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed __dict__ allocation for non-heap types.
Improved cached decorators.
Removed automatic generator exhaustion of cached values.
  • Loading branch information
jordanbriere committed Jun 30, 2023
commit 15176adc4a94df68fde9492f7bbc6c63ee7308dc
56 changes: 15 additions & 41 deletions addons/source-python/packages/source-python/core/cache.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
# ../core/cache.py

"""Provides caching functionality.

.. data:: cached_property
An alias of :class:`core.cache.CachedProperty`.
"""

"""Provides caching functionality."""

# =============================================================================
# >> IMPORTS
# =============================================================================
# Python Imports
# FuncTools
from functools import wraps
# Inspect
from inspect import isdatadescriptor
# Types
from types import MethodType

Expand All @@ -23,7 +20,6 @@
# Source.Python Imports
# Core
from _core._cache import CachedProperty
from _core._cache import cached_property


# =============================================================================
Expand All @@ -39,39 +35,17 @@
# =============================================================================
# >> FUNCTIONS
# =============================================================================
def cached_result(fget):
"""Decorator used to register a cached method.

:param function fget:
Method that is only called once and its result cached for subsequent
calls.
:rtype: CachedProperty
"""
# Get a dummy object as default cache, so that we can cache None
NONE = object()

def getter(self):
"""Getter function that generates the cached method."""
# Set our cache to the default value
cache = NONE

# Wrap the decorated method as the inner function
@wraps(fget)
def wrapper(self, *args, **kwargs):
"""Calls the decorated method and cache the result."""
nonlocal cache

# Did we cache a result already?
if cache is NONE:

# No cache, let's call the wrapped method and cache the result
cache = fget(self, *args, **kwargs)

# Return the cached result
return cache
def cached_property(fget=None, *args, **kwargs):
"""Decorator used to register or wrap a cached property."""
return (CachedProperty
if not isdatadescriptor(fget)
else CachedProperty.wrap_descriptor
)(fget, *args, **kwargs)

# Bind the wrapper function to the passed instance and return it
return MethodType(wrapper, self)

# Return a cached property bound to the getter function
return CachedProperty(getter, doc=fget.__doc__)
def cached_result(fget, *args, **kwargs):
"""Decorator used to register a cached method."""
return CachedProperty(
lambda self: MethodType(fget, self),
*args, **kwargs
)
57 changes: 16 additions & 41 deletions src/core/modules/core/core_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,37 +64,6 @@ object CCachedProperty::_callable_check(object function, const char *szName)
return function;
}

object CCachedProperty::_prepare_value(object value)
{
if (!PyGen_Check(value.ptr()))
return value;

if (getattr(value, "gi_frame").is_none())
BOOST_RAISE_EXCEPTION(
PyExc_ValueError,
"The given generator is exhausted."
);

list values;
while (true)
{
try
{
values.append(value.attr("__next__")());
}
catch(...)
{
if (!PyErr_ExceptionMatches(PyExc_StopIteration))
throw_error_already_set();

PyErr_Clear();
break;
}
}

return values;
}


object CCachedProperty::get_getter()
{
Expand Down Expand Up @@ -152,10 +121,11 @@ object CCachedProperty::get_cached_value(object instance)
);

PyObject *pValue = NULL;
PyObject *pDict = *_PyObject_GetDictPtr(instance.ptr());
PyObject **ppDict = _PyObject_GetDictPtr(instance.ptr());

if (pDict) {
pValue = PyDict_GetItem(pDict, m_name.ptr());

if (ppDict && *ppDict) {
pValue = PyDict_GetItem(*ppDict, m_name.ptr());
}

if (!pValue) {
Expand All @@ -178,25 +148,30 @@ void CCachedProperty::set_cached_value(object instance, object value)
"Unable to assign the value of an unbound property."
);

PyObject *pDict = *_PyObject_GetDictPtr(instance.ptr());
PyObject *pDict = PyObject_GenericGetDict(instance.ptr(), NULL);

if (!pDict) {
pDict = PyObject_GenericGetDict(instance.ptr(), NULL);
Py_DECREF(pDict);
const char *szOwner = extract<const char *>(m_owner.attr("__qualname__"));
BOOST_RAISE_EXCEPTION(
PyExc_AttributeError,
"'%s' object has no attribute '__dict__'",
szOwner
)
}

PyDict_SetItem(pDict, m_name.ptr(), _prepare_value(value).ptr());
PyDict_SetItem(pDict, m_name.ptr(), value.ptr());
Py_XDECREF(pDict);
}

void CCachedProperty::delete_cached_value(object instance)
{
PyObject *pDict = *_PyObject_GetDictPtr(instance.ptr());
PyObject **ppDict = _PyObject_GetDictPtr(instance.ptr());

if (!pDict) {
if (!ppDict && !*ppDict) {
return;
}

PyDict_DelItem(pDict, m_name.ptr());
PyDict_DelItem(*ppDict, m_name.ptr());

if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
Expand Down
1 change: 0 additions & 1 deletion src/core/modules/core/core_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class CCachedProperty
);

static object _callable_check(object function, const char *szName);
static object _prepare_value(object value);

object get_getter();
object set_getter(object fget);
Expand Down
2 changes: 0 additions & 2 deletions src/core/modules/core/core_cache_wrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,4 @@ void export_cached_property(scope _cache)
)
)
.staticmethod("wrap_descriptor");

scope().attr("cached_property") = scope().attr("CachedProperty");
}