All computer source code presented on this page, unless it includes attribution to another author, is provided by Ed Halley under the Artistic License. Use such code freely and without any expectation of support. I would like to know if you make anything cool with the code, or need questions answered.
python/
    bindings.py
    boards.py
    buzz.py
    cache.py
    cards.py
    constraints.py
    csql.py
    english.py
    getopts.py
    gizmos.py
    goals.py
    improv.py
    interpolations.py
    namespaces.py
    nihongo.py
    nodes.py
    octalplus.py
    patterns.py
    persist.py
    physics.py
    pids.py
    pieces.py
    quizzes.py
    recipes.py
    relays.py
    romaji.py
    ropen.py
    sheets.py
    strokes.py
    subscriptions.py
    svgbuild.py
    testing.py
    things.py
    timing.py
    ucsv.py
    useful.py
    uuid.py
    vectors.py
    weighted.py
java/
    CSVReader.java
    CSVWriter.java
    GlobFilenameFilter.java
    RegexFilenameFilter.java
    StringBufferOutputStream.java
    ThreadSet.java
    Throttle.java
    TracingThread.java
    Utf8ConsoleTest.java
    droid/
        ArrangeViewsTouchListener.java
        DownloadFileTask.java
perl/
    CVQM.pm
    Kana.pm
    Typo.pm
cxx/
    CCache.h
    equalish.cpp
Download persist.py
# persist - general storage-depot persistence helpers

'''

Some general storage-depot persistence helper classes.

ABSTRACT

    Builds on pickle, creating a depot or repository of uniquely
    identified persistent objects.  Each object has its own permanent
    unchanging identity with id(), and that identity is used in the
    filesystem or database as a key to retrieve it later.

AUTHOR

    Ed Halley (ed@halley.cc) 12 December 2006

'''

#----------------------------------------------------------------------------

class Identified:
    """A mix-in class for anything you need to have a stable unique identity.

       class MyClass (MyBaseClass, Identified):
           pass

       myobj = MyClass()
       print myobj.id()

    To give your MyClass instances a globally unique identity, add this
    class to your bases.  The instance will gain a method .id() to
    retrieve the unique identifier for the instance.  The identifier
    value itself resides in a private attribute ._id for serialization,
    which should not be modified by any application code.
    """

    def allocid(self):
        import uuid
        return uuid.uuid1()

    def id(self):
        if not hasattr(self, '_id'):
            self._id = self.allocid()
        return self._id

#----------------------------------------------------------------------------

class Persistent:
    """A mix-in class for anything you need to persist in a data store.

       class MyClass (MyBaseClass, Persistent):
           pass

       s = Store()

       myobj = MyClass()
       myobj.commit(s) # attaches to store

       myobj.commit()

    To manage your MyClass instances in a persistent store, add this
    class to your bases.  The instance will gain one method, .commit(),
    to attach the instance to the store and also flush any uncommitted
    modifications.  As a whole, your MyClass definition must be able to
    be serialized with the pickle module.
    """

    def commit(self, store=None):
        store = getattr(self, '_store', store)
        if store: self._store = store
        store = Store.find(store)
        store.commit(self)

#----------------------------------------------------------------------------

import cPickle as pickle
import os

class Store:
    """A system to manage any number of persistent, identified objects.

    The stored objects must include the Identified and Persistent bases.
    """

    _stores = { } # name : store

    @classmethod
    def find(cls, name):
        if not name in Store._stores:
            return Store(name)
        return Store._stores[name]

    def __init__(self, name=None):
        self._saved = set([])
        self._awake = { }
        if not name:
             name = 'Store' + str(len(Store._stores))
        self.name = name
        Store._stores[name] = self
        self.startup()

    def startup(self):
        # override for external storage
        self._storage = { }
        self._saved = set(self._storage.keys())

    def load(self, id):
        # override for external storage
        blob = self._storage[id]
        return blob

    def save(self, id, blob):
        # override for external storage
        self._storage[id] = blob
        return id

    def kill(self, id):
        # override for external storage
        self._storage.pop(id)

    def add(self, item):
        if not isinstance(item, Identified):
            raise TypeError, 'Stored objects must include Identified base type.'
        if not isinstance(item, Persistent):
            raise TypeError, 'Stored objects must include Persistent base type.'
        if getattr(item, '_store', None):
            raise KeyError, 'Object already kept in a Store.'
        id = item.id()
        self._saved.add(id)
        self._awake[id] = item
        item._store = self.name
        self.commit(id)
        return id

    def destroy(self, id):
        if not id in self._saved:
            raise KeyError, 'Id not known in store ' + self.name
        self.kill(id)
        self._saved.remove(id)
        if id in self._awake:
            self._awake.pop(id)

    def fetch(self, id):
        if isinstance(id, Identified):
            id = id.id()
        if not id in self._saved:
            raise KeyError, 'Id not known in store ' + self.name
        if not id in self._awake:
            blob = self.load(id)
            item = self.deserialize(blob)
            self._awake[id] = item
        return self._awake[id]

    def __getitem__(self, id):
        "As a convenience, referring to mystore[id] calls mystore.fetch(id)."
        return self.fetch(id)

    def __setitem__(self, id, item):
        raise TypeError, 'Add new items to the Store with add(item).'

    def serialize(self, id):
        item = self.fetch(id)
        blob = pickle.dumps(item)
        # See exception notes below.
        return blob

    def deserialize(self, blob):
        item = pickle.loads(blob)
        return item

    def commit(self, id=None):
        if id is None:
            for id in self._awake:
                self.commit(id)
            return
        if isinstance(id, Identified):
            id = id.id()
        if not id in self._saved:
            raise KeyError, 'Id not known in store ' + self.name
        if not id in self._awake:
            return
        blob = self.serialize(id)
        self.save(id, blob)
        return id

    def clone(self, id):
        if isinstance(id, Identified):
            id = id.id()
        if not id in self._saved:
            raise ValueError, 'id not known in store ' + self.name
        if id in self._awake:
            self.commit(id)
        blob = self.load(id)
        other = self.deserialize(blob)
        other._id = other.allocid()
        self._saved.add(other._id)
        self._awake[other._id] = other
        other.commit()
        return other

#
# Possible errors or exceptions raised:
#
# PicklingError: Can't pickle <class 'X'>: attribute lookup X failed
# The above message may indicate X is not at top level of a module.
# (Can't embed a persistent class in a def or a class, for example.)
#

#----------------------------------------------------------------------------

def fetch(id):
    "A global routine attempts to fetch one or more objects from all stores."
    if isinstance(id, list) or isinstance(id, tuple) or isinstance(id, set):
        return [ fetch(x) for x in id ]
    for store in Store._stores.values():
        if id in store._saved:
            return store.fetch(id)
    return None



Contact Ed Halley by email at ed@halley.cc.
Text, code, layout and artwork are Copyright © 1996-2013 Ed Halley.
Copying in whole or in part, with author attribution, is expressly allowed.
Any references to trademarks are illustrative and are controlled by their respective owners.
Make donations with PayPal - it's fast, free and secure!