[Zope-CVS] CVS: Products/AdaptableStorage/zodb - .cvsignore:1.1 ASConnection.py:1.1 ASDB.py:1.1 ASStorage.py:1.1 OIDEncoder.py:1.1 StaticResource.py:1.1 ZODBResource.py:1.1 __init__.py:1.1 consts.py:1.1 serial_public.py:1.1

Shane Hathaway shane@zope.com
Wed, 27 Nov 2002 13:37:10 -0500


Update of /cvs-repository/Products/AdaptableStorage/zodb
In directory cvs.zope.org:/tmp/cvs-serv12157/zodb

Added Files:
	.cvsignore ASConnection.py ASDB.py ASStorage.py OIDEncoder.py 
	StaticResource.py ZODBResource.py __init__.py consts.py 
	serial_public.py 
Log Message:
Moved the latest AdaptableStorage work out of the private repository.
It took a long time, but I moved it as soon as all the unit tests
passed and I felt that all the interface names and conventions were
good enough.

Documentation is still minimal, but now I think the system is finally
straight enough in my head to write down. :-) If you want a sneak
peek, the interfaces have some docstrings, if you're looking for a
"tree" view, while the OpenOffice diagram presents something of a
"forest" view.

Also note that I'm trying a new coding convention.  The "public"
module in each package defines exactly which objects should be
exported from the package.  This solves a few problems with imports
such as doubling of names and shadowing of modules.  Overall, the
"public" module makes it easier to tell which classes are supposed to
be used by other packages, and makes it easier for other packages to
use the public classes.  See what you think.



=== Added File Products/AdaptableStorage/zodb/.cvsignore ===
*.pyc


=== Added File Products/AdaptableStorage/zodb/ASConnection.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.
#
##############################################################################
"""Extension of the ZODB Connection class

$Id: ASConnection.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""

import sys
from time import time
from types import StringType, TupleType

import ZODB
from ZODB.Connection import Connection, StringIO, Unpickler, Pickler, \
     ConflictError, ReadConflictError, ExtensionKlass, LOG, ERROR

from consts import SERIAL0
from serial_public import IKeyedObjectSystem


class ASConnection (Connection):
    """DomainMapper-driven Connection

    Uses a domain mapper to serialize the state of objects before
    pickling, and to deserialize objects based on the pickled
    state.

    The domain mapper might, for example, serialize all objects as
    tabular records.
    """
    _volatile = None  # { oid -> 1 }
    _domain_mapper = None

    no_mtime_available = 1  # Flag recognized by the PersistentExtra base class

    __implements__ = (IKeyedObjectSystem,
                      getattr(Connection, '__implements__', ()))


    def getOIDInfo(self, oid):
        mapper_name, key = self._db._oid_encoder.decode(oid)
        domain_mapper = self._domain_mapper
        if domain_mapper is None:
            domain_mapper = self._db._domain_resource.access(self)
            self._domain_mapper = domain_mapper
        return domain_mapper.getMapper(mapper_name), key


    def close(self):
        db = self._db
        try:
            Connection.close(self)
        finally:
            if db is not None and self._domain_mapper is not None:
                self._domain_mapper = None
                db._domain_resource.release(self)


    def _persistent_load(self, oid, class_info=None):

        __traceback_info__=oid

        # Note that this impl. never expects tuple OIDs like Connection does.
        assert isinstance(oid, StringType)

        obj = self._cache.get(oid, None)
        if obj is not None:
            return obj

        if class_info and class_info[1] is None:
            klass = class_info[0]
            # Quick instance reference.  We know all we need to know
            # to create the instance wo hitting the db, so go for it!
            if isinstance(klass, TupleType):
                module, name = klass
                try: klass=self._db._classFactory(self, module, name)
                except:
                    # Eek, we couldn't get the class. Hm.
                    # Maybe there is more current data in the
                    # object's actual record!
                    return self[oid]

            object=klass.__basicnew__()
            object._p_oid=oid
            object._p_jar=self
            object._p_changed=None

            self._cache[oid] = object

            return object

        # We don't have enough info for fast loading.  Load the whole object.
        return self[oid]


    def mayBegin(self, transaction):
        if hasattr(self, '_begun') and not self._begun:
            self._storage.tpc_begin(transaction)
            self._begun = 1


    def commit(self, object, transaction):
        if object is self:
            self.mayBegin(transaction)
            # We registered ourself.  Execute a commit action, if any.
            if self._Connection__onCommitActions is not None:
                method_name, args, kw = \
                             self._Connection__onCommitActions.pop(0)
                apply(getattr(self, method_name), (transaction,) + args, kw)
            return
        oid=object._p_oid
        #invalid=self._invalidated.get
        invalid = self._invalid
        if oid is None or object._p_jar is not self:
            # new object
            oid = self.new_oid()
            object._p_jar=self
            object._p_oid=oid
            self._creating.append(oid)

        elif object._p_changed:
            if (
                (invalid(oid) and not hasattr(object, '_p_resolveConflict'))
                or
                invalid(None)
                ):
                raise ConflictError(object=object)
            self._invalidating.append(oid)

        else:
            # Nothing to do
            return

        self.mayBegin(transaction)

        stack=[object]

        # Create a special persistent_id that passes T and the subobject
        # stack along:
        #
        # def persistent_id(object,
        #                   self=self,
        #                   stackup=stackup, new_oid=self.new_oid):
        #     if (not hasattr(object, '_p_oid') or
        #         type(object) is ClassType): return None
        #
        #     oid=object._p_oid
        #
        #     if oid is None or object._p_jar is not self:
        #         oid = self.new_oid()
        #         object._p_jar=self
        #         object._p_oid=oid
        #         stackup(object)
        #
        #     klass=object.__class__
        #
        #     if klass is ExtensionKlass: return oid
        #
        #     if hasattr(klass, '__getinitargs__'): return oid
        #
        #     module=getattr(klass,'__module__','')
        #     if module: klass=module, klass.__name__
        #
        #     return oid, klass

        file=StringIO()
        seek=file.seek
        pickler=Pickler(file,1)
        # SDH: external references are computed in a different way.
        # pickler.persistent_id=new_persistent_id(self, stack.append)
        dbstore=self._storage.store
        file=file.getvalue
        cache=self._cache
        get=cache.get
        dump=pickler.dump
        clear_memo=pickler.clear_memo


        version=self._version

        while stack:
            object=stack[-1]
            del stack[-1]
            oid=object._p_oid
            serial=getattr(object, '_p_serial', '\0\0\0\0\0\0\0\0')
            if serial == '\0\0\0\0\0\0\0\0':
                # new object
                self._creating.append(oid)
            else:
                #XXX We should never get here
                # SDH: Actually it looks like we should, but only
                # for the first object on the stack.
                if (
                    (invalid(oid) and
                     not hasattr(object, '_p_resolveConflict'))
                    or
                    invalid(None)
                    ):
                    raise ConflictError(object=object)
                self._invalidating.append(oid)

            klass = object.__class__

            if klass is ExtensionKlass:
                # SDH: not supported.
                raise NotImplementedError, "Unable to store ZClass instances"
            else:
                if hasattr(klass, '__getinitargs__'):
                    args = object.__getinitargs__()
                    len(args) # XXX Assert it's a sequence
                else:
                    args = None # New no-constructor protocol!

                module=getattr(klass,'__module__','')
                if module: klass=module, klass.__name__
                __traceback_info__=klass, oid, self._version
                # SDH: hook in the serializer.
                # state=object.__getstate__()
                mapper, key = self.getOIDInfo(oid)
                ser = mapper.getSerializer()
                state, ext_refs = ser.serialize(mapper, key, object, self)
                if ext_refs:
                    oid_encoder = self._db._oid_encoder
                    for (ext_mapper, ext_key, ext_ref) in ext_refs:
                        if (not ext_ref._p_serial
                            or ext_ref._p_serial == SERIAL0):
                            ext_oid = oid_encoder.encode(ext_mapper, ext_key)
                            if ext_ref._p_jar:
                                if ext_ref._p_jar != self:
                                    raise InvalidObjectReference
                            else:
                                ext_ref._p_jar = self
                            if ext_ref._p_oid:
                                if ext_ref._p_oid != ext_oid:
                                    raise StorageError('Conflicting OIDs')
                            else:
                                ext_ref._p_oid = ext_oid
                            stack.append(ext_ref)

            seek(0)
            clear_memo()
            dump((klass,args))
            dump(state)
            p=file(1)
            s=dbstore(oid,serial,p,version,transaction)
            # Put the object in the cache before handling the
            # response, just in case the response contains the
            # serial number for a newly created object
            try: cache[oid]=object
            except:
                # Dang, I bet its wrapped:
                if hasattr(object, 'aq_base'):
                    cache[oid]=object.aq_base
                else:
                    raise

            self._handle_serial(s, oid)



    def setstate(self, object):
        oid=object._p_oid

        if self._storage is None:
            msg = "Shouldn't load state for %s when the connection is closed" % `oid`
            LOG('ZODB',ERROR, msg)
            raise RuntimeError(msg)

        try:
            p, serial = self._storage.load(oid, self._version)

            # XXX this is quite conservative!
            # We need, however, to avoid reading data from a transaction
            # that committed after the current "session" started, as
            # that might lead to mixing of cached data from earlier
            # transactions and new inconsistent data.
            #
            # Note that we (carefully) wait until after we call the
            # storage to make sure that we don't miss an invaildation
            # notifications between the time we check and the time we
            # read.
            #invalid = self._invalidated.get
            invalid = self._invalid
            if invalid(oid) or invalid(None):
                if not hasattr(object.__class__, '_p_independent'):
                    get_transaction().register(self)
                    raise ReadConflictError(object=object)
                invalid=1
            else:
                invalid=0

            file=StringIO(p)
            unpickler=Unpickler(file)
            # SDH: external references are reassembled elsewhere.
            # unpickler.persistent_load=self._persistent_load
            unpickler.load()
            state = unpickler.load()

            # SDH: Let the object mapping do the state setting.
            # if hasattr(object, '__setstate__'):
            #     object.__setstate__(state)
            # else:
            #     d=object.__dict__
            #     for k,v in state.items(): d[k]=v
            mapper, key = self.getOIDInfo(oid)
            ser = mapper.getSerializer()
            ser.deserialize(mapper, key, object, self, state)

            if mapper.isVolatile():
                v = self._volatile
                if v is None:
                    v = {}
                    self._volatile = v
                v[oid] = 1

            object._p_serial=serial

            if invalid:
                if object._p_independent():
                    try: del self._invalidated[oid]
                    except KeyError: pass
                else:
                    get_transaction().register(self)
                    raise ConflictError(object=object)

        except ConflictError:
            raise
        except:
            LOG('ZODB',ERROR, "Couldn't load state for %s" % `oid`,
                error=sys.exc_info())
            raise


    def __repr__(self):
        if self._version:
            ver = ' (in version %s)' % `self._version`
        else:
            ver = ''
        return '<%s at %08x%s>' % (self.__class__.__name__, id(self), ver)


    # IKeyedObjectSystem implementation

    def loadStub(self, mapper_name, key, class_info=None):
        oid = self._db._oid_encoder.encode(mapper_name, key)
        return self._persistent_load(oid, class_info)

    def identifyObject(self, object):
        oid = object._p_oid
        if oid is None:
            return None
        return self._db._oid_encoder.decode(oid)

    def newKey(self):
        return self.new_oid()


    ### Volitalile object expiration ###

    def invalidateVolatileObjects(self):
        """Requests invalidation of the loaded volatile objects.
        """
        v = self._volatile
        if not v:
            return
        self._invalidated.update(v)
        self._volatile.clear()


    # Hook some opportunities to remove volatile objects.

    def _setDB(self, odb):
        self.invalidateVolatileObjects()
        Connection._setDB(self, odb)

    def tpc_abort(self, transaction):
        self.invalidateVolatileObjects()
        Connection.tpc_abort(self, transaction)

    def tpc_finish(self, transaction):
        self.invalidateVolatileObjects()
        Connection.tpc_finish(self, transaction)

    def sync(self):
        self.invalidateVolatileObjects()
        Connection.sync(self)



=== Added File Products/AdaptableStorage/zodb/ASDB.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.
#
##############################################################################
"""Extension of the ZODB DB class

$Id: ASDB.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""


from ZODB.DB import DB, Transaction, cPickle, cStringIO, allocate_lock

from ASConnection import ASConnection
from OIDEncoder import OIDEncoder


class ASDB (DB):
    """DomainMapper-driven Database
    """

    klass = ASConnection

    # SDH: two extra args.
    def __init__(self, storage, domain_resource, oid_encoder=None,
                 pool_size=7,
                 cache_size=400,
                 cache_deactivate_after=60,
                 version_pool_size=3,
                 version_cache_size=100,
                 version_cache_deactivate_after=10,
                 ):
        """Create an object database.
        """
        # Allocate locks:
        l=allocate_lock()
        self._a=l.acquire
        self._r=l.release

        # Setup connection pools and cache info
        self._pools={},[]
        self._temps=[]
        self._pool_size=pool_size
        self._cache_size=cache_size
        self._cache_deactivate_after = cache_deactivate_after
        self._version_pool_size=version_pool_size
        self._version_cache_size=version_cache_size
        self._version_cache_deactivate_after = version_cache_deactivate_after

        self._miv_cache={}

        # Setup storage
        self._storage=storage
        storage.registerDB(self, None)
        if not hasattr(storage,'tpc_vote'): storage.tpc_vote=lambda *args: None

        if oid_encoder is None:
            oid_encoder = OIDEncoder()
        self._oid_encoder = oid_encoder
        self._domain_resource = domain_resource

        # Pass through methods:
        for m in ('history',
                  'supportsUndo', 'supportsVersions', 'undoLog',
                  'versionEmpty', 'versions'):
            setattr(self, m, getattr(storage, m))

        if hasattr(storage, 'undoInfo'):
            self.undoInfo=storage.undoInfo



=== Added File Products/AdaptableStorage/zodb/ASStorage.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.
#
##############################################################################
"""Storage implementation that loads/stores using a domain mapper.

$Id: ASStorage.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""

import md5
from cPickle import Pickler, Unpickler
from cStringIO import StringIO

from ZODB import POSException, BaseStorage

from consts import VERIFY_CLASSES, SERIAL0, SERIAL1, DEBUG
from OIDEncoder import OIDEncoder


class ASStorage(BaseStorage.BaseStorage):

    def __init__(self, domain_resource, oid_encoder=None,
                 name='AdaptableStorage ZODB backend'):
        self._domain_resource = domain_resource
        if oid_encoder is None:
            oid_encoder = OIDEncoder()
        self._oid_encoder = oid_encoder
        BaseStorage.BaseStorage.__init__(self, name)

    def __len__(self):
        return 1

    def getSize(self):
        # Stub
        return 1

    def hashSerial(self, serial):
        """Turns any object into an 8-byte checksum.

        Note that this is somewhat inefficient and error-prone if the
        input includes anything besides simple objects
        (ints, tuples, strings, etc.)  It seems to work for now.
        """
        s = repr(serial)
        hash = md5.new(s).digest()[:8]
        if hash == SERIAL0:  # Avoid the special value.
            hash = SERIAL1
        if DEBUG:
            print 'hash of %r is %r' % (serial, hash)
        return hash

    def getOIDInfo(self, oid):
        mapper_name, key = self._oid_encoder.decode(oid)
        dm = self._domain_resource.access(self)
        return dm.getMapper(mapper_name), key

    def load(self, oid, version):
        if version:
            raise POSException.Unsupported, "Versions aren't supported"
        self._lock_acquire()
        try:
            mapper, key = self.getOIDInfo(oid)
            full_state, serial = mapper.getGateway().load(mapper, key)
            class_info = mapper.getSerializer().getClassInfo(full_state)
            file = StringIO()
            p = Pickler(file)
            p.dump(class_info)
            p.dump(full_state)
            data = file.getvalue()
            hash = self.hashSerial(serial)
            if DEBUG:
                print 'loaded', `oid`, `hash`
            return data, hash
        finally:
            self._lock_release()

    def store(self, oid, serial_hash, data, version, transaction):
        if transaction is not self._transaction:
            raise POSException.StorageTransactionError(self, transaction)

        if version:
            raise POSException.Unsupported, "Versions aren't supported"

        self._lock_acquire()
        try:
            mapper, key = self.getOIDInfo(oid)
            # First detect conflicts.
            # The "serial" argument, if its value is not 0,
            # was previously generated by hashSerial().
            if DEBUG:
                print 'storing', `oid`, `serial_hash`
            if serial_hash != SERIAL0:
                old_state, old_serial = mapper.getGateway().load(mapper, key)
                old_class_info = mapper.getSerializer().getClassInfo(old_state)
                old_serial_hash = self.hashSerial(old_serial)
                if serial_hash != old_serial_hash:
                    raise POSException.ConflictError("%r != %r" % (
                        serial_hash, old_serial_hash))
            # Now unpickle and store the data.
            file = StringIO(data)
            u = Unpickler(file)
            class_info = u.load()
            state = u.load()
            if VERIFY_CLASSES:
                check = mapper.getSerializer().getClassInfo(state)
                if check and class_info != check:
                    raise RuntimeError, 'Class spec error: %r != %r' % (
                        class_info, check)
            new_serial = mapper.getGateway().store(mapper, key, state)
            new_hash = self.hashSerial(new_serial)
        finally:
            self._lock_release()

        if DEBUG:
            print 'stored', `oid`, `serial_hash`, `new_hash`
        return new_hash

    def new_oid(self):
        raise POSException.StorageError("new_oid() is disabled.")

    def _clear_temp(self):
        pass

    def _finish(self, tid, user, desc, ext):
        pass

    def _abort(self):
        pass

    def pack(self, t, referencesf):
        pass

    def _splat(self):
        """Spit out a string showing state.
        """
        return ''

    def close(self):
        self._domain_resource.release(self)




=== Added File Products/AdaptableStorage/zodb/OIDEncoder.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.
#
##############################################################################
"""Default OID encoder.

$Id: OIDEncoder.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""


from consts import ROOT_OID
from interfaces.public import IOIDEncoder


class OIDEncoder:
    """Simple OID encoder
    """

    __implements__ = IOIDEncoder

    def __init__(self, root_info=('root', '')):
        self.root_info = root_info

    def decode(self, oid):
        if oid == ROOT_OID:
            return self.root_info
        info = oid.split(':', 1)
        if len(info) < 2:
            info = (info, '')
        return info

    def encode(self, mapper_name, key):
        if (mapper_name, key) == self.root_info:
            return ROOT_OID
        return '%s:%s' % (mapper_name, key)



=== Added File Products/AdaptableStorage/zodb/StaticResource.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.
#
##############################################################################
"""Simple, static resource

$Id: StaticResource.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""

from interfaces.public import IResourceAccess


class StaticResource:

    __implements__ = IResourceAccess

    def __init__(self, r):
        self.r = r

    def access(self, consumer):
        return self.r

    def release(self, consumer):
        pass



=== Added File Products/AdaptableStorage/zodb/ZODBResource.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.
#
##############################################################################
"""Resource loaded from ZODB

$Id: ZODBResource.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""

# TODO: about 50 lines of code ;-)



=== Added File Products/AdaptableStorage/zodb/__init__.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.
#
##############################################################################
"""ZODB package of AdaptableStorage

$Id: __init__.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""



=== Added File Products/AdaptableStorage/zodb/consts.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.
#
##############################################################################
"""Constants for this package.

$Id: consts.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""

DEBUG = 0
VERIFY_CLASSES = 0
VERIFY_SCHEMAS = 0
VERIFY_COMPUTED_OIDS = 0
VERIFY_CREATED_OIDS = 0

SERIAL0 = '\0' * 8
SERIAL1 = '\0' * 7 + '\001'
ROOT_OID = '\0' * 8


=== Added File Products/AdaptableStorage/zodb/serial_public.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.
#
##############################################################################
"""Import of the public classes and interfaces from the serial package.

$Id: serial_public.py,v 1.1 2002/11/27 18:37:08 shane Exp $
"""

from Products.AdaptableStorage.serial.public import *