[Zope-Checkins] CVS: Zope3/lib/python/Persistence - Function.py:1.1 Module.py:1.1

Jeremy Hylton jeremy@zope.com
Fri, 21 Jun 2002 15:06:48 -0400


Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv9075

Added Files:
	Function.py Module.py 
Log Message:
Minimally functional persistent modules.


=== Added File Zope3/lib/python/Persistence/Function.py ===
"""Persistent functions."""

import new
import sys

from Persistence import Persistent

def get_code_args(co):
    """Return args from code object suitable for passing to constructor."""
    return (co.co_argcount,
            co.co_nlocals,
            co.co_stacksize,
            co.co_flags,
            co.co_code,
            co.co_consts,
            co.co_names,
            co.co_varnames,
            co.co_filename,
            co.co_name,
            co.co_firstlineno,
            co.co_lnotab,
            co.co_freevars,
            co.co_cellvars)
    
class PersistentFunction(Persistent):

    def __init__(self, func, module):
        self._func = func
        self._module = module
        self._code = {}

    def __call__(self, *args, **kwargs):
        return self._func(*args, **kwargs)

    def __getstate__(self):
        # If func_dict is empty, store None to avoid creating a dict
        # unnecessarily when the function is unpickled
        # XXX new.function doesn't accept a closure
        func_state = self._func.func_defaults, self._func.func_dict or None

        # Store the code separately from the function
        code = self._func.func_code

        # The code object is can only be reused in an interpreter
        # running the same version of Python and with the same
        # __debug__ value.  Store code in a dict keyed by these two values.

        # The code object is only valid if the module hasn't been
        # changed.  If the module is changed, then the code object
        # must be reloaded from the module.  To detect this change,
        # store the serial number of the module.
        key = sys.version_info, __debug__
        if key not in self._code:
            self._code[key] = get_code_args(code), self._module._p_serial

        return func_state, self._code, self._module

    def __setstate__(self, (func, code, mod)):
        self._code = code
        self._module = mod

        # recreate the code object
        code = None
        key = sys.version_info, __debug__
        co_args, serial = self._code.get(key, (None, None))
        if serial is not None:
            if serial != mod._p_serial:
                del self._code[key]
            else:
                code = new.code(*co_args)
        if code is None:
            # XXX get it from the module
            assert False, "not implemented"

        func_defaults, func_dict = func
        func = new.function(code, mod.__dict__, func_defaults)
        if func_dict:
            func.func_dict.update(func_dict)
        self._func = func


=== Added File Zope3/lib/python/Persistence/Module.py ===
"""Persistent Module."""

import ihooks
from new import function
import sys

from Persistence import Persistent
from Persistence.Function import PersistentFunction
from Persistence.BTrees.OOBTree import OOBTree

from Transaction import get_transaction

class PersistentModule(Persistent):

    def __init__(self, name):
        self.__name__ = name

    def __getstate__(self):
        d = self.__dict__.copy()
        del d["__builtins__"]
        return d

    def __setstate__(self, state):
        import __builtin__
        state["__builtins__"] = state
        self.__dict__.update(state)

class PersistentModuleLoader(ihooks.BasicModuleLoader):

    __super_init = ihooks._Verbose.__init__
    __super_find_module = ihooks.BasicModuleLoader.find_module
    __super_load_module = ihooks.BasicModuleLoader.load_module

    def __init__(self, modules, verbose=0):
        self.__super_init(verbose)
        self.modules = modules

    def find_module(self, name, path=None):
        if path is None:
            mod = self.modules.get(name)
            if mod is not None:
                return mod
        return self.__super_find_module(name, path)

    def load_module(self, name, stuff):
        if isinstance(stuff, PersistentModule):
            assert stuff.__name__ == name, (stuff, stuff.__name__, name)
            return stuff
        return self.__super_load_module(name, stuff)

    def modules_dict(self):
        return sys.modules

class PersistentModuleImporter(ihooks.ModuleImporter):

    __super_init = ihooks.ModuleImporter.__init__

    def __init__(self, root, name="modules", verbose=None):
        # setup storage for module objects and source in root
        self._root = root
        self._name = name
        pair = self._root.get(name, None)
        if pair is None:
            modules = OOBTree()
            sources = OOBTree()
            self._root[name] = modules, sources
            get_transaction().commit()
        else:
            modules, sources = pair
        self._modules = modules
        self._sources = sources

        self.__super_init(PersistentModuleLoader(self._modules), verbose)

    def module_from_file(self, name, path):
        assert not self.modules.has_key(name)
        f = open(path, "rb")
        src = f.read()
        f.close()
        self.module_from_source(name, src)

    def module_from_source(self, name, source):
        m = self._modules[name] = PersistentModule(name)
        self._sources[name] = source
        exec source in m.__dict__
        self.fixup(m)

    def fixup(self, m):
        # Converts function objects in module m to persistent functions
        # XXX should do classes, too
        for k, v in m.__dict__.items():
            if isinstance(v, function):
                m.__dict__[k] = PersistentFunction(v, m)