[Zope3-checkins] CVS: Zope3/src/zodb/code - class_.py:1.6.2.3
Jeremy Hylton
jeremy@zope.com
Fri, 24 Jan 2003 16:55:00 -0500
Update of /cvs-repository/Zope3/src/zodb/code
In directory cvs.zope.org:/tmp/cvs-serv18777/code
Modified Files:
Tag: new-pickle-branch
class_.py
Log Message:
Add a hook for the _p_jar class attribute that causes _p_activate() to
be called when it is assigned. See comments for a detailed rationale.
Fix the repr() of unbound PersistentMethods.
=== Zope3/src/zodb/code/class_.py 1.6.2.2 => 1.6.2.3 ===
--- Zope3/src/zodb/code/class_.py:1.6.2.2 Fri Jan 24 13:20:43 2003
+++ Zope3/src/zodb/code/class_.py Fri Jan 24 16:54:58 2003
@@ -126,6 +126,17 @@
class ExtClassDataDescr(DataMixin, ExtClassDescr):
pass
+class ExtClassHookDataDescr(ExtClassDataDescr):
+ # Calls a hook when clsset() is called.
+
+ def __init__(self, name, descr, val, hook):
+ super(ExtClassHookDataDescr, self).__init__(name, descr, val)
+ self.hook = hook
+
+ def clsset(self, val):
+ self.val = val
+ self.hook()
+
# The next three classes conspire to make a PersistentFunction
# behave like a method when found in a class's __dict__.
@@ -138,12 +149,10 @@
def __repr__(self):
if self.im_self is None:
- kind = "unbound"
+ fmt = "<persistent unbound method %s.%s>"
else:
- kind = "bound"
- return ("<persistent %s method %s.%s of %s>"
- % (kind, self.im_class.__name__, self.im_func.__name__,
- self.im_self))
+ fmt = "<persistent bound method %%s.%%s of %s>" % (self.im_self,)
+ return fmt % (self.im_class.__name__, self.im_func.__name__)
def __call__(self, *args, **kwargs):
if self.im_self is None:
@@ -196,13 +205,24 @@
class PersistentClassMetaClass(PersistentMetaClass):
- # an attempt to make persistent classes look just like other
+ # 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
+ # The chief limitation of this approach is that class.attr won't
# always behave the way it does for normal classes
+ # A persistent class can never be a ghost, because there are too
+ # many places where Python will attempt to inspect the class
+ # without using getattr(). As a result, it would be impossible to
+ # guarantee that the class would be unghostified at the right
+ # time. It's really difficult to guarantee this property without
+ # help from the connection, because a ghost can't be unghosted
+ # until after the connection sets its _p_jar.
+
+ # The hack solution is to have a hook for _p_jar that activates
+ # the object the first time it is set.
+
__implements__ = IPersistent
_pc_init = False
@@ -214,6 +234,7 @@
def __new__(meta, name, bases, dict, state=UPTODATE):
cls = super(PersistentClassMetaClass, meta).__new__(
meta, name, bases, dict)
+
# helper functions
def extend_attr(attr, v):
prev = findattr(cls, attr, None)
@@ -224,7 +245,6 @@
setattr(cls, attr, ExtClassMethodDescr(attr, prev, m))
extend_attr("_p_oid", None)
- extend_attr("_p_jar", None)
extend_attr("_p_atime", time.time() % 86400)
extend_attr("_p_state", state)
# XXX A persistent class needs a proprety for _p_changed
@@ -234,6 +254,12 @@
extend_meth("_p_activate", meta._p_activate)
extend_meth("_p_deactivate", meta._p_activate)
+ # Create a descriptor that calls _p_activate() when _p_jar is set.
+ inst_jar_descr = findattr(cls, "_p_jar", None)
+ setattr(cls, "_p_jar",
+ ExtClassHookDataDescr("_p_jar", inst_jar_descr, None,
+ getattr(cls, "_p_activate")))
+
for k, v in dict.items():
if isinstance(v, PersistentFunction):
setattr(cls, k, PersistentDescriptor(cls, v))
@@ -246,13 +272,6 @@
extend_attr("__implements__", meta.__implements__)
cls._pc_init = True
- # A persistent class can never be a ghost, because there are too
- # many places where Python will attempt to inspect the class
- # without using getattr(). As a result, it would be impossible
- # to guarantee that the class would be unghostified at the
- # right time.
- if state == GHOST:
- cls._p_activate()
return cls
def __getattribute__(cls, name):
@@ -315,20 +334,36 @@
for k, v in dict.items():
setattr(cls, k, PersistentDescriptor(cls, v))
+ # XXX Should the object get marked as a ghost when it is, in fact,
+ # not a ghost? The most obvious answer is no. But if we don't
+ # then we need some other attribute that can be used to handle
+ # invalidations of classes and make _p_activate() work as expected.
+ # Need to decide on a good answer.
+
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 == GHOST:
+ # The logic here is:
+ # If the class hasn't finished executing __new__(), don't
+ # try to load its state.
+ # If the class has a jar but no oid, it's a new object
+ # and doesn't have state in the database.
+
+ # XXX Why would an object be marked a ghost, have a jar, and
+ # not have an oid?
+ if cls._p_state == GHOST and cls._pc_init:
dm = cls._p_jar
- if dm is not None:
+ if dm is not None and cls._p_oid:
cls._p_state = CHANGED
try:
dm.setstate(cls)
finally:
# XXX Should really put in special inconsistent state
cls._p_state = UPTODATE
+ else:
+ pass # XXX should log here
# Methods below here are not wrapped to be class-only attributes.
# They are available as methods of classes using this metaclass.