[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/srpg/Source.Python/master/src/core/modules/core/core_cache.cpp [Back]  [Original]

/**
* =============================================================================
* Source Python
* Copyright (C) 2012-2019 Source Python Development Team.  All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program.  If not, see .
*
* As a special exception, the Source Python Team gives you permission
* to link the code of this program (as well as its derivative works) to
* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
* by the Valve Corporation.  You must obey the GNU General Public License in
* all respects for all other code used.  Additionally, the Source.Python
* Development Team grants this exception to all derivative works.
*/

//-----------------------------------------------------------------------------
// Includes.
//-----------------------------------------------------------------------------
#include "core_cache.h"
#include "export_main.h"


//-----------------------------------------------------------------------------
// CCachedProperty class.
//-----------------------------------------------------------------------------
CCachedProperty::CCachedProperty(
	object fget=object(), object fset=object(), object fdel=object(), object doc=object(),
	boost::python::tuple args=boost::python::tuple(), object kwargs=object())
{
	set_getter(fget);
	set_setter(fset);
	set_deleter(fdel);

	m_doc = doc;

	m_args = args;

	if (!kwargs.is_none())
		m_kwargs = extract(kwargs);
	else
		m_kwargs = dict();
}


object CCachedProperty::_callable_check(object function, const char *szName)
{
	if (!function.is_none() && !PyCallable_Check(function.ptr()))
		BOOST_RAISE_EXCEPTION(
			PyExc_TypeError,
			"The given %s function is not callable.", szName
		);

	return function;
}


object CCachedProperty::get_getter()
{
	return m_fget;
}

object CCachedProperty::set_getter(object fget)
{
	m_fget = _callable_check(fget, "getter");
	return fget;
}


object CCachedProperty::get_setter()
{
	return m_fset;
}

object CCachedProperty::set_setter(object fset)
{
	m_fset = _callable_check(fset, "setter");
	return fset;
}


object CCachedProperty::get_deleter()
{
	return m_fdel;
}

object CCachedProperty::set_deleter(object fdel)
{
	m_fdel = _callable_check(fdel, "deleter");
	return fdel;
}


str CCachedProperty::get_name()
{
	return m_name;
}

object CCachedProperty::get_owner()
{
	return m_owner;
}


object CCachedProperty::get_cached_value(object instance)
{
	if (!m_name || m_owner.is_none())
		BOOST_RAISE_EXCEPTION(
			PyExc_AttributeError,
			"Unable to retrieve the value of an unbound property."
		);

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

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

	if (!pValue) {
		const char *szName = extract(m_name);
		BOOST_RAISE_EXCEPTION(
			PyExc_KeyError,
			"No cached value found for '%s'.",
			szName
		)
	}

	return object(handle(borrowed(pValue)));
}

void CCachedProperty::set_cached_value(object instance, object value)
{
	if (!m_name || m_owner.is_none())
		BOOST_RAISE_EXCEPTION(
			PyExc_AttributeError,
			"Unable to assign the value of an unbound property."
		);

	if (!PyObject_IsInstance(instance.ptr(), m_owner.ptr())) {
		const char *szOwner = extract(m_owner.attr("__qualname__"));
		BOOST_RAISE_EXCEPTION(
			PyExc_TypeError,
			"Given instance is not of type '%s'.",
			szOwner
		)
	}

	if (PyGen_Check(value.ptr())) {
		return;
	}

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

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

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

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

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

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

	if (PyErr_Occurred()) {
		if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
			throw_error_already_set();
		}

		PyErr_Clear();
	}
}


object CCachedProperty::bind(object self, object owner, str name)
{
	CCachedProperty &pSelf = extract(self);

	if (owner.is_none() && !name) {
		BOOST_RAISE_EXCEPTION(
			PyExc_ValueError,
			"Must provide a name and an owner."
		)
	}

	owner.attr(name) = self;
	pSelf.__set_name__(owner, name);
	return self;
}


void CCachedProperty::__set_name__(object owner, str name)
{
	if (m_name || !m_owner.is_none())
	{
		const char *szName = extract(str(".").join(make_tuple(m_owner.attr("__qualname__"), m_name)));
		BOOST_RAISE_EXCEPTION(
			PyExc_RuntimeError,
			"This property was already bound as \"%s\".",
			szName
		)
	}

	m_name = name;
	m_owner = owner;
}

object CCachedProperty::__get__(object self, object instance, object owner=object())
{
	if (instance.is_none())
		return self;

	CCachedProperty &pSelf = extract(self);
	object value;

	try
	{
		value = pSelf.get_cached_value(instance);
	}
	catch (...)
	{
		if (!PyErr_ExceptionMatches(PyExc_KeyError))
			throw_error_already_set();

		PyErr_Clear();

		object getter = pSelf.get_getter();
		if (getter.is_none())
			BOOST_RAISE_EXCEPTION(
				PyExc_AttributeError,
				"Unable to retrieve the value of a property that have no getter function."
			);

		value = getter(
			*(make_tuple(handle(borrowed(instance.ptr()))) + pSelf.m_args),
			**pSelf.m_kwargs
		);

		pSelf.set_cached_value(instance, value);
	}

	return value;
}


void CCachedProperty::__set__(object instance, object value)
{
	if (m_fset.is_none())
		BOOST_RAISE_EXCEPTION(
			PyExc_AttributeError,
			"Unable to assign the value of a property that have no setter function."
		);

	object result = m_fset(
		*(make_tuple(handle(borrowed(instance.ptr())), value) + m_args),
		**m_kwargs
	);

	if (!result.is_none())
		set_cached_value(instance, result);
	else
		delete_cached_value(instance);
}

void CCachedProperty::__delete__(object instance)
{
	if (!m_fdel.is_none())
		m_fdel(
			*(make_tuple(handle(borrowed(instance.ptr()))) + m_args),
			**m_kwargs
		);

	delete_cached_value(instance);
}

object CCachedProperty::__call__(object self, object fget)
{
	CCachedProperty &pSelf = extract(self);
	pSelf.set_getter(fget);
	return self;
}

object CCachedProperty::__getitem__(str item)
{
	return m_kwargs[item];
}

void CCachedProperty::__setitem__(str item, object value)
{
	m_kwargs[item] = value;
}


CCachedProperty *CCachedProperty::wrap_descriptor(
	object descriptor, object owner, str name,
	boost::python::tuple args, object kwargs)
{
	CCachedProperty *pProperty = new CCachedProperty(
		descriptor.attr("__get__"), descriptor.attr("__set__"), descriptor.attr("__delete__"),
		descriptor.attr("__doc__"), args, kwargs
	);

	pProperty->__set_name__(owner, name);

	return pProperty;
}

Web Proxy Viewer  |  New URL  |  Original Page