[Zope3-checkins] CVS: Zope3/src/zodb/code - patch.py:1.7 class_.py:1.10
Jeremy Hylton
jeremy@zope.com
Thu, 30 Jan 2003 13:50:17 -0500
Update of /cvs-repository/Zope3/src/zodb/code
In directory cvs.zope.org:/tmp/cvs-serv11359
Modified Files:
patch.py class_.py
Log Message:
A collection of small improvements.
Set _pc_init to False inside constructor, although I'm not entirely
certain why it is necessary. Sometimes a class would get loaded
through a new connection or invalidated and its _pc_init would be
True.
Remove unneeded __call__(). A class is never a ghost.
Add comment and commented-out test of the need for _p_changed.
First steps towards a more useful __getstate__().
Remove stale comment in patch.TypeWrapper.
Re-enable testing of _p_oid attribute.
=== Zope3/src/zodb/code/patch.py 1.6 => 1.7 ===
--- Zope3/src/zodb/code/patch.py:1.6 Tue Jan 28 14:23:42 2003
+++ Zope3/src/zodb/code/patch.py Thu Jan 30 13:50:15 2003
@@ -132,24 +132,7 @@
class TypeWrapper(Wrapper):
def unwrap(self, bases, dict):
- # XXX Add Persistent to the list of bases so that type (the
- # base class of PersistentClassMetaClass) will create the
- # correct C layout.
-
- # We must maintain a linearizable MRO when adding Persistent
- # to list of bases. In particular, object is in Persistent's
- # __bases__ to Persistent must occur before object in the
- # new class's __bases__.
-
- if not Persistent in bases:
- if object in bases:
- L = list(bases)
- i = L.index(object)
- newbases = bases[:i] + (Persistent,) + bases[i:]
- else:
- newbases = bases + (Persistent,)
-
- return PersistentClassMetaClass(self._obj.__name__, newbases, dict)
+ return PersistentClassMetaClass(self._obj.__name__, bases, dict)
def registerWrapper(atype, wrapper, unwrap_thunk):
"""Register a patch wrapper for an external object type."""
@@ -194,6 +177,10 @@
# They are classes, but can't be pickled as globals because
# pickle looks in sys.modules and the persistent import
# doesn't use sys.modules.
+
+ # If we find a class, pickle it via save_type()
+ if isinstance(obj, PersistentClassMetaClass):
+ return None
# XXX Is this safe in all cases?
oid = getattr(obj, "_p_oid", marker)
@@ -213,10 +200,14 @@
self.write(self.put(memo_len))
self.memo[id(atype)] = memo_len, None
else:
- self.save_global(atype)
+ if isinstance(atype, PersistentClassMetaClass):
+ self.save_pers(self.persistent_id(atype, True))
+ else:
+ self.save_global(atype)
dispatch[TypeType] = save_type
dispatch[ClassType] = save_type
+ dispatch[PersistentClassMetaClass] = save_type
def save_function(self, func):
if pickle.whichmodule(func, func.__name__) == "__main__":
=== Zope3/src/zodb/code/class_.py 1.9 => 1.10 ===
--- Zope3/src/zodb/code/class_.py:1.9 Tue Jan 28 17:03:43 2003
+++ Zope3/src/zodb/code/class_.py Thu Jan 30 13:50:15 2003
@@ -197,8 +197,6 @@
self._func)
-# XXX is missing necessary for findattr?
-# None might be sufficient
_missing = object()
def findattr(cls, attr, default):
@@ -209,6 +207,10 @@
return o
return default
+class StateChangeDataDescr(ExtClassDataDescr):
+ # A data descriptor for _p_changed.
+ pass
+
class PersistentClassMetaClass(PersistentMetaClass):
# An attempt to make persistent classes look just like other
@@ -231,8 +233,6 @@
__implements__ = IPersistent
- _pc_init = False
-
# A class is normally created in the UPTODATE state, but when a
# new ghost is created for it the serialization machinery passes
# GHOST instead of UPTODATE. See __getnewargs__().
@@ -240,6 +240,7 @@
def __new__(meta, name, bases, dict, state=UPTODATE):
cls = super(PersistentClassMetaClass, meta).__new__(
meta, name, bases, dict)
+ cls._pc_init = False
# helper functions
def extend_attr(attr, v):
@@ -253,9 +254,6 @@
extend_attr("_p_oid", None)
extend_attr("_p_atime", time.time() % 86400)
extend_attr("_p_state", state)
- # XXX A persistent class needs a proprety for _p_changed
- # so that it can be used to register with the transaction
- # manager.
extend_attr("_p_changed", None)
extend_meth("_p_activate", meta._p_activate)
extend_meth("_p_deactivate", meta._p_activate)
@@ -280,13 +278,6 @@
cls._pc_init = True
return cls
- def __call__(cls, *args, **kwargs):
- # type's __call__() uses tp_init directly, so it bypasses our
- # __getattribute__() hook. So we must guarantee that a class
- # is loaded before it is called.
- cls._p_activate()
- return super(PersistentClassMetaClass, cls).__call__(*args, **kwargs)
-
def __getattribute__(cls, name):
# XXX I'm not sure I understand this code any more.
super_meth = super(PersistentClassMetaClass, cls).__getattribute__
@@ -303,6 +294,9 @@
cls._p_atime = int(time.time() % 86400)
return super_meth(name)
+ # XXX There needs to be an _p_changed flag so that classes get
+ # registered with the txn when they are modified.
+
def __setattr__(cls, attr, val):
if not attr.startswith("_pc_") and cls._pc_init:
descr = cls.__dict__.get(attr)
@@ -310,42 +304,51 @@
set = getattr(descr, "__set__", None)
if set is not None:
set(None, val)
+## cls._p_changed = True
return
super(PersistentClassMetaClass, cls).__setattr__(attr, val)
def __delattr__(cls, attr):
if attr.startswith('_p_'):
- if attr == "_p_changed":
- # this means something special
- pass
- else:
- return
+ # XXX what should happen with these?
+ return
super(PersistentClassMetaClass, cls).__delattr__(attr)
def __repr__(cls):
return "<persistent class %s.%s>" % (cls.__module__,
cls.__name__)
- # XXX This stuff only handles methods, not data members, but it's
- # a start.
-
- # The state of a persistent class is its persistent functions.
- # They need to be extracted from the methods when the class is pickled
- # and put back into descriptors when they are unpickled.
-
- # How to recognize other class attributes that need to be pickled?
+ # It should be possible for getstate / setstate to deal with
+ # arbitrary class attributes. That goal is hard to achieve,
+ # because there are several funny descriptors that need to
+ # be handled specially.
def __getstate__(cls):
dict = {}
+
for k in cls.__dict__.keys():
v = getattr(cls, k)
if isinstance(v, PersistentMethod):
dict[k] = v.im_func
+ continue
+ if (k in ["__module__", "__weakref__", "__dict__"]
+ or k.startswith("_p_") or k.startswith("_pc_")):
+ continue
+ # XXX The following test isn't right because overriding
+ # must be allowed, but I haven't figured that out yet.
+ # __getstate__ and __setstate__ might be overridden
+ # __implements__ might be overridden
+ if k in ["__getstate__", "__setstate__", "__implements__"]:
+ continue
+ dict[k] = v
return dict
def __setstate__(cls, dict):
for k, v in dict.items():
- setattr(cls, k, PersistentDescriptor(cls, v))
+ if isinstance(v, PersistentFunction):
+ setattr(cls, k, PersistentDescriptor(cls, v))
+ else:
+ setattr(cls, k, 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
@@ -376,7 +379,7 @@
# XXX Should really put in special inconsistent state
cls._p_state = UPTODATE
else:
- pass # XXX should log here
+ print id(cls), "dm", dm, "oid", cls._p_oid
# Methods below here are not wrapped to be class-only attributes.
# They are available as methods of classes using this metaclass.