Commit 1f93b15a authored by Jim Fulton's avatar Jim Fulton

Import what we can from ZODB.persistentclass to avoid duplication.

parent b94034a5
......@@ -18,135 +18,23 @@ IMPORTANT -- This module is private to ZClasses and experimetal.
$Id$
"""
# Notes:
#
# Persistent classes are non-ghostable. This has some interesting
# ramifications:
#
# - When an object is invalidated, it must reload it's state
#
# - When an object is loaded from the database, it's state must be
# loaded. Unfortunately, there isn't a clear signal when an object is
# loaded from the database. This should probably be fixed.
#
# In the mean time, we need to infer. This should be viewed as a
# short term hack.
#
# Here's the strategy we'll use:
#
# - We'll have a need to be loaded flag that we'll set in
# __new__, through an extra argument.
#
# - When setting _p_oid and _p_jar, if both are set and we need to be
# loaded, then we'll load out state.
#
# - We'll use _p_changed is None to indicate that we're in this state.
#
import ExtensionClass
import ZODB.persistentclass
class _p_DataDescr(object):
# Descr used as base for _p_ data. Data are stored in
# _p_class_dict.
def __init__(self, name):
self.__name__ = name
def __get__(self, inst, cls):
if inst is None:
return self
if '__global_persistent_class_not_stored_in_DB__' in inst.__dict__:
raise AttributeError, self.__name__
return inst._p_class_dict.get(self.__name__)
def __set__(self, inst, v):
inst._p_class_dict[self.__name__] = v
def __delete__(self, inst):
raise AttributeError, self.__name__
class _p_oid_or_jar_Descr(_p_DataDescr):
# Special descr for _p_oid and _p_jar that loads
# state when set if both are set and and _p_changed is None
#
# See notes above
def __set__(self, inst, v):
get = inst._p_class_dict.get
if v == get(self.__name__):
return
inst._p_class_dict[self.__name__] = v
jar = get('_p_jar')
if (jar is not None
and get('_p_oid') is not None
and get('_p_changed') is None
):
jar.setstate(inst)
class _p_ChangedDescr(object):
# descriptor to handle special weird emantics of _p_changed
def __get__(self, inst, cls):
if inst is None:
return self
return inst._p_class_dict['_p_changed']
def __set__(self, inst, v):
if v is None:
return
inst._p_class_dict['_p_changed'] = bool(v)
def __delete__(self, inst):
inst._p_invalidate()
class _p_MethodDescr(object):
"""Provide unassignable class attributes
"""
def __init__(self, func):
self.func = func
def __get__(self, inst, cls):
if inst is None:
return cls
return self.func.__get__(inst, cls)
def __set__(self, inst, v):
raise AttributeError, self.__name__
def __delete__(self, inst):
raise AttributeError, self.__name__
special_class_descrs = '__dict__', '__weakref__'
def _p_maybeupdate(self, name):
get = self._p_class_dict.get
data_manager = get('_p_jar')
if (
(data_manager is not None)
and
(get('_p_oid') is not None)
and
(get('_p_changed') == False)
):
self._p_changed = True
data_manager.register(self)
# See the comments in ZODB.persistentclass
class ZClassPersistentMetaClass(ExtensionClass.ExtensionClass):
_p_jar = _p_oid_or_jar_Descr('_p_jar')
_p_oid = _p_oid_or_jar_Descr('_p_oid')
_p_changed = _p_ChangedDescr()
_p_serial = _p_DataDescr('_p_serial')
# For weird reasons having to do with restrictions on built-in
# types, we can't subclass
# ZODB.persistentclass.PersistentMetaClass, so we do the next best
# thing:
for name in ('_p_jar', '_p_oid', '_p_changed', '_p_serial',
'__getnewargs__', '_p_maybeupdate', '_p_deactivate',
'_p_invalidate', '__getstate__', '_p_activate', ):
locals()[name] = ZODB.persistentclass.PersistentMetaClass.__dict__[
name]
def __new__(self, name, bases, cdict, _p_changed=False):
......@@ -168,46 +56,15 @@ class ZClassPersistentMetaClass(ExtensionClass.ExtensionClass):
ExtensionClass.pmc_init_of(result)
return result
def __getnewargs__(self):
return self.__name__, self.__bases__, {}, None
__getnewargs__ = _p_MethodDescr(__getnewargs__)
def __setattr__(self, name, v):
if not ((name.startswith('_p_') or name.startswith('_v'))):
_p_maybeupdate(self, name)
self._p_maybeupdate(name)
super(ZClassPersistentMetaClass, self).__setattr__(name, v)
def __delattr__(self, name):
if not ((name.startswith('_p_') or name.startswith('_v'))):
_p_maybeupdate(self, name)
self._p_maybeupdate(name)
super(ZClassPersistentMetaClass, self).__delattr__(name)
def _p_deactivate(self):
# persistent classes can't be ghosts
pass
_p_deactivate = _p_MethodDescr(_p_deactivate)
def _p_invalidate(self):
# reset state
self._p_class_dict['_p_changed'] = None
self._p_jar.setstate(self)
_p_invalidate = _p_MethodDescr(_p_invalidate)
def __getstate__(self):
return (self.__bases__,
dict([(k, v) for (k, v) in self.__dict__.items()
if not (k.startswith('_p_')
or k.startswith('_v_')
or k in special_class_descrs
)
]),
)
__getstate__ = _p_MethodDescr(__getstate__)
def __setstate__(self, state):
try:
......@@ -224,13 +81,14 @@ class ZClassPersistentMetaClass(ExtensionClass.ExtensionClass):
_p_class_dict = self._p_class_dict
self._p_class_dict = {}
to_remove = [k for k in self.__dict__
if ((k not in cdict)
and
(k not in special_class_descrs)
and
(k != '_p_class_dict')
)]
to_remove = [
k for k in self.__dict__
if ((k not in cdict)
and
(k not in ZODB.persistentclass.special_class_descrs)
and
(k != '_p_class_dict')
)]
for k in to_remove:
delattr(self, k)
......@@ -244,9 +102,4 @@ class ZClassPersistentMetaClass(ExtensionClass.ExtensionClass):
self._p_changed = False
__setstate__ = _p_MethodDescr(__setstate__)
def _p_activate(self):
self._p_jar.setstate(self)
_p_activate = _p_MethodDescr(_p_activate)
__setstate__ = ZODB.persistentclass._p_MethodDescr(__setstate__)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment