[Zope3-checkins] CVS: Zope3/src/zodb/code - class_.py:1.1.2.1 module.py:1.1.2.2 patch.py:1.1.2.2 class.py:NONE

Tim Peters tim.one@comcast.net
Mon, 23 Dec 2002 15:25:00 -0500


Update of /cvs-repository/Zope3/src/zodb/code
In directory cvs.zope.org:/tmp/cvs-serv1466/src/zodb/code

Modified Files:
      Tag: NameGeddon-branch
	module.py patch.py 
Added Files:
      Tag: NameGeddon-branch
	class_.py 
Removed Files:
      Tag: NameGeddon-branch
	class.py 
Log Message:
Renamed class.py to class_.py, and fixed relevant imports.


=== Added File Zope3/src/zodb/code/class_.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""Persistent Classes."""

from persistence import Persistent, PersistentMetaClass
from persistence.persistence import UPTODATE, CHANGED, STICKY, GHOST
from persistence.interfaces import IPersistent
from zodb.code.function import PersistentFunction

import new
from types import FunctionType as function
import time

# XXX There is a lot of magic here to give classes and instances
# separate sets of attributes.  This code should be documented, as it
# it quite delicate, and it should be move to a separate module.

__metaclass__ = type

class ExtClassDescr:
    """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:

    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:

    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:

    _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

# The next three classes conspire to make a PersistentFunction
# behave like a method when found in a class's __dict__.

class PersistentMethod:
    """Make PersistentFunctions into methods."""
    def __init__(self, klass, inst, func):
        self.im_class = klass
        self.im_self = inst
        self.im_func = func

    def __repr__(self):
        if self.im_self is None:
            kind = "unbound"
        else:
            kind = "bound"
        return ("<persistent %s method %s.%s of %s>"
                % (kind, self.im_class.__name__, self.im_func.__name__,
                   self.im_self))

    def __call__(self, *args, **kwargs):
        if self.im_self is None:
            if not isinstance(args[0], self.im_class):
                raise TypeError("unbound method %s() must be called "
                                "with %s instance as first argument ("
                                "got %s instead)" % (self.im_func.__name__,
                                                     self.im_class.__name__,
                                                     type(args[0]).__name__))
        else:
            return self.im_func(self.im_self, *args, **kwargs)

class PersistentDescriptor:

    def __init__(self, objclass, func):
        self.__name__ = func.__name__
        self.__doc__ = func.__doc__
        self.__objclass__ = objclass
        self._func = func
        # Delegate __getstate__ and __setstate__ to the persistent func.
        # The patch module will use these methods to update persistent
        # methods in place.
        self.__getstate__ = func.__getstate__
        self.__setstate__ = func.__setstate__

    def __repr__(self):
        return "<descriptor %s.%s>" % (self.__objclass__.__name__,
                                       self.__name__)

    def __get__(self, object, klass=None):
        if object is None:
            return PersistentMethod(klass or self.__objclass__, None,
                                    self._func)
        else:
            return PersistentMethod(klass or self.__objclass__, object,
                                    self._func)


# 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 PersistentClassMetaClass(PersistentMetaClass):

    # 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 = False

    def __new__(meta, name, bases, dict):
        cls = super(PersistentClassMetaClass, 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_attr("_p_state", UPTODATE)
        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__)

        for k, v in dict.items():
            if isinstance(v, PersistentFunction):
                setattr(cls, k, PersistentDescriptor(cls, v))
        
        cls._pc_init = True
        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):
        # XXX I'm not sure I understand this code any more.
        super_meth = super(PersistentClassMetaClass, cls).__getattribute__

        # If we are initializing the class, don't trying to check variables
        # like _p_state, since they may not be initialized.
        if not super_meth("_pc_init"):
            return super_meth(name)
        if (name[0] == "_" and
            not (name.startswith("_p_") or name.startswith("_pc_") or
                 name == "__dict__")):
            if cls._p_state == GHOST:
                cls._p_activate()
                cls._p_atime = int(time.time() % 86400)
        return super_meth(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(PersistentClassMetaClass, cls).__setattr__(attr, val)

    def __delattr__(cls, attr):
        if attr.startswith('_p_'):
            if attr == "_p_changed":
                # this means something special
                pass
            else:
                return
        super(PersistentClassMetaClass, 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

    # Methods below here are not wrapped to be class-only attributes.
    # They are available as methods of classes using this metaclass.

    def __getnewargs__(cls):
        return cls.__name__, cls.__bases__, {}

    def _p_newstate(cls, acls):
        # Update a class's __dict__ in place.  Must use setattr and
        # delattr because __dict__ is a read-only proxy.
        # XXX This doesn't handle __methods__ correctly.
        def getkeys(cls):
            L = [n for n in cls.__dict__.keys()
                 if not (n.startswith("__") and n.endswith("__"))]
            d = {}
            for elt in L:
                d[elt] = True
            return d
        oldnames = getkeys(cls)
        newnames = getkeys(acls)
        for name in oldnames:
            if not name in newnames:
                delattr(cls, name)
        for name in newnames:
            setattr(cls, name, acls.__dict__[name])



=== Zope3/src/zodb/code/module.py 1.1.2.1 => 1.1.2.2 ===
--- Zope3/src/zodb/code/module.py:1.1.2.1	Mon Dec 23 14:30:48 2002
+++ Zope3/src/zodb/code/module.py	Mon Dec 23 15:24:59 2002
@@ -22,7 +22,7 @@
 
 from persistence import Persistent
 from persistence.persistence import GHOST
-from zodb.code.class import PersistentClassMetaClass
+from zodb.code.class_ import PersistentClassMetaClass
 from zodb.code.function import PersistentFunction
 from zodb.code.interfaces import IPersistentModuleManager
 from zodb.code.interfaces \


=== Zope3/src/zodb/code/patch.py 1.1.2.1 => 1.1.2.2 ===
--- Zope3/src/zodb/code/patch.py:1.1.2.1	Mon Dec 23 14:30:48 2002
+++ Zope3/src/zodb/code/patch.py	Mon Dec 23 15:24:59 2002
@@ -82,7 +82,7 @@
 import pickle
 from types import *
 
-from zodb.code.class import PersistentClassMetaClass, PersistentDescriptor
+from zodb.code.class_ import PersistentClassMetaClass, PersistentDescriptor
 from zodb.code.function import PersistentFunction
 from persistence import Persistent
 

=== Removed File Zope3/src/zodb/code/class.py ===