[Zope3-checkins] CVS: Zope3/lib/python/Persistence - Class.py:1.1 Module.py:1.10
Jeremy Hylton
jeremy@zope.com
Thu, 18 Jul 2002 18:41:58 -0400
Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv27240/lib/python/Persistence
Modified Files:
Module.py
Added Files:
Class.py
Log Message:
First steps towards persistent classes.
Some trivial tests succeed, but there is much work to do.
=== Added File Zope3/lib/python/Persistence/Class.py ===
"""Persistent Classes."""
from Persistence import Persistent
from Persistence.cPersistence import UPTODATE, CHANGED, STICKY, GHOST
from Persistence.IPersistent import IPersistent
from Persistence.Function import PersistentFunction
import new
from types import FunctionType as function
import time
class ExtClassDescr(object):
"""Maintains seperate class and instance descriptors for an attribute.
This allows a class to provide methods and attributes without
intefering with normal use of instances. The class and its
instances can each have methods with the same name.
This does interfere with introspection on the class.
"""
def __init__(self, name, instdescr):
self.name = name
self.instdescr = instdescr
def __get__(self, obj, cls):
if obj is None:
return self.clsget(cls)
else:
return self.instdescr.__get__(obj, cls)
def __set__(self, obj, val):
if obj is None:
self.clsset(val)
else:
if self.instdescr is None:
raise AttributeError, self.name
return self.instdescr.__set__(obj, val)
def __delete__(self, obj):
if self.instdescr is None:
raise AttributeError, self.name
return self.instdescr.__delete__(obj)
# subclass should override
def clsget(self, cls):
pass
def clsset(self, val):
pass
def clsdelete(self):
pass
class MethodMixin(object):
def __init__(self, name, descr, func):
super(MethodMixin, self).__init__(name, descr)
self.func = func
def clsget(self, cls):
def f(*args, **kwargs):
try:
return self.func(cls, *args, **kwargs)
except TypeError:
print `self.func`, `cls`, `args`, `kwargs`
raise
return f
class DataMixin(object):
def __init__(self, name, descr, val):
super(DataMixin, self).__init__(name, descr)
self.val = val
def clsget(self, cls):
return self.val
def clsset(self, val):
self.val = val
def clsdelete(self):
del self.val
class ExtClassObject(object):
_missing = object()
def __init__(self, name, instdescr):
self.name = name
self.instdescr = instdescr
def __get__(self, obj, cls):
if obj is None:
return self.clsget(cls)
else:
return self.instdescr.__get__(obj, cls)
def __set__(self, obj, cls):
if obj is None:
return self.clsset(cls)
else:
if self.instdescr is None:
raise AttributeError, self.name
return self.instdescr.__set__(obj, cls)
def __delete__(self, obj, cls):
if obj is None:
return self.clsdelete(cls)
else:
if self.instdescr is None:
raise AttributeError, self.name
return self.instdescr.__delete__(obj, cls)
class ExtClassMethodDescr(MethodMixin, ExtClassDescr):
pass
class ExtClassDataDescr(DataMixin, ExtClassDescr):
pass
# XXX is missing necessary for findattr?
# None might be sufficient
_missing = object()
def findattr(cls, attr, default):
"""Walk the mro of cls to find attr."""
for c in cls.__mro__:
o = c.__dict__.get(attr, _missing)
if o is not _missing:
return o
return default
class PersistentMetaClass(type):
# an attempt to make persistent classes look just like other
# persistent objects by providing class attributes and methods
# that behave like the persistence machinery.
# the chief limitation of this approach is that class.attr won't
# always behave the way it does for normal classes
__implements__ = IPersistent
_pc_init = 0
def __new__(meta, name, bases, dict):
cls = super(PersistentMetaClass, meta).__new__(meta, name, bases, dict)
# helper functions
def extend_attr(attr, v):
prev = findattr(cls, attr, None)
setattr(cls, attr, ExtClassDataDescr(attr, prev, v))
def extend_meth(attr, m):
prev = findattr(cls, attr, None)
setattr(cls, attr, ExtClassMethodDescr(attr, prev, m))
extend_attr("_p_oid", None)
extend_attr("_p_jar", None)
extend_meth("_p_activate", meta._p_activate)
extend_meth("_p_deactivate", meta._p_activate)
extend_meth("__getstate__", meta.__getstate__)
extend_meth("__setstate__", meta.__setstate__)
extend_attr("__implements__", meta.__implements__)
cls._pc_init = 1
return cls
def fixup(cls, mod):
for k, v in cls.__dict__.items():
if isinstance(v, function):
setattr(cls, k, PersistentFunction(v, mod))
def __getattribute__(cls, name):
if (name[0] == "_" and
not (name.startswith("_p_") or name.startswith("_pc_") or
name == "__dict__")):
if cls._p_state is None:
cls._p_activate()
cls._p_atime = int(time.time() % 86400)
return super(PersistentMetaClass, cls).__getattribute__(name)
def __setattr__(cls, attr, val):
if not attr.startswith("_pc_") and cls._pc_init:
descr = cls.__dict__.get(attr)
if descr is not None:
set = getattr(descr, "__set__", None)
if set is not None:
set(None, val)
return
super(PersistentMetaClass, cls).__setattr__(attr, val)
def __delattr__(cls, attr):
if attr.startswith('_p_'):
if attr == "_p_changed":
# this means something special
pass
else:
return
super(PersistentMetaClass, cls).__delattr__(attr)
def __getstate__(cls):
dict = {}
for k, v in cls.__dict__.items():
if hasattr(v, '_p_oid'):
dict[k] = v
return dict
def __setstate__(cls, dict):
for k, v in dict.items():
setattr(cls, k, v)
def _p_deactivate(cls):
# do nothing but mark the state change for now
cls._p_state = GHOST
def _p_activate(cls):
if cls._p_state is None:
dm = cls._p_jar
if dm is not None:
# reactivate
cls._p_state = UPTODATE
class PersistentBaseClass(Persistent):
__metaclass__ = PersistentMetaClass
def _test():
global PC, P
class PC(Persistent):
__metaclass__ = PersistentMetaClass
def __init__(self):
self.x = 1
def inc(self):
self.x += 1
def __int__(self):
return self.x
class P(PersistentBaseClass):
def __init__(self):
self.x = 1
def inc(self):
self.x += 1
def __int__(self):
return self.x
if __name__ == "__main__":
_test()
=== Zope3/lib/python/Persistence/Module.py 1.9 => 1.10 ===
import sys
from Persistence import Persistent
+from Persistence.Class import PersistentMetaClass
from Persistence.Function import PersistentFunction
from Persistence.BTrees.OOBTree import OOBTree
@@ -39,9 +40,6 @@
def __getstate__(self):
d = self.__dict__.copy()
del d["__builtins__"]
- # XXX must guarantee nothing reachable from d has
- # a reference to d, unless there is a Persistent object
- # between the two references.
return d
def __setstate__(self, state):
@@ -104,7 +102,8 @@
self.module_from_source(name, src)
def module_from_source(self, name, source):
- self._modules[name] = PersistentModule(name)
+ mod = PersistentModule(name)
+ self._modules[name] = mod
self.update_module(name, source)
def update_module(self, name, source):
@@ -122,6 +121,9 @@
for k, v in new.items():
if isinstance(v, function):
v = new[k] = PersistentFunction(v, module)
+ elif isinstance(v.__class__, PersistentMetaClass):
+ v.__class__.fixup(module)
+ # XXX need to check for classes that are not persistent!
old_v = old.get(k)
if old_v is not None: