[Zope3-checkins] CVS: Zope3/src/zope/app/services - globalmodule.py:1.1.2.1 module.py:1.21.2.1 module.zcml:1.4.18.1 registration.py:1.20.4.1 service.py:1.36.4.1 servicenames.py:1.13.2.1

Fred L. Drake, Jr. fred at zope.com
Thu Jan 15 15:50:52 EST 2004


Update of /cvs-repository/Zope3/src/zope/app/services
In directory cvs.zope.org:/tmp/cvs-serv1563/src/zope/app/services

Modified Files:
      Tag: zope3-fdrake-globalized-modules-branch
	module.py module.zcml registration.py service.py 
	servicenames.py 
Added Files:
      Tag: zope3-fdrake-globalized-modules-branch
	globalmodule.py 
Log Message:
Checkpointing the current state of the module globalization work on a branch
so it does not get lost.  See http://dev.zope.org/Zope3/ModulesAreGlobal.


=== Added File Zope3/src/zope/app/services/globalmodule.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Global module service.

$Id: globalmodule.py,v 1.1.2.1 2004/01/15 20:50:21 fdrake Exp $
"""

import os

from persistence import Persistent
from persistence.dict import PersistentDict

from zodb.code import module

from zope.interface import implements

from zope.app import zapi
from zope.app.container.contained import Contained
from zope.app.interfaces.annotation import IAttributeAnnotatable
from zope.app.interfaces.services import registration as interfaces
from zope.app.interfaces.services.module import IModuleManager
from zope.app.interfaces.services.module import IModuleRegistration
from zope.app.interfaces.services.module import IModuleService
from zope.app.interfaces.services.service import ILocalService
from zope.app.services.registration import NameComponentRegistry
from zope.app.services.registration import NamedComponentRegistration
from zope.app.services.registration import RegistrationStatusProperty
from zope.app.services.servicenames import Modules


class ModuleService(NameComponentRegistry, Persistent, Contained):

    implements(IModuleService, ILocalService, IAttributeAnnotatable)

    def createRegistrations(self, name):
        """See INameRegistry"""
        # This must be redefined since we have to create a special
        # type of registration stack.
        try:
            stack = self._bindings[name]
        except KeyError:
            parts = name.split(".")
            for x in range(len(parts), 0, -1):
                n = _dotjoin(parts[:x])
                stack = self._bindings.get(n)
                if stack is None:
                    stack = GlobalRegistrationStack(self, n)
                    self._bindings[n] = stack
                    self._p_changed = 1
                else:
                    break
            stack = self._bindings[name]
        return stack

    def queryActiveComponent(self, name, default=None):
        stack = self._bindings.get(name)
        if stack is None:
            return default
        registration = stack.active()
        if registration is None:
            return default
        if IModuleRegistration.isImplementedBy(registration):
            return stack.module
        else:
            return registration.getComponent()

    def findModule(self, name):
        stack = self._bindings.get(name)
        if stack is None:
            return None
        mod = stack.module
        if mod is not None:
            keys = mod.__dict__.keys()
            keys.sort()
            if "__builtins__" in keys:
                keys.remove("__builtins__")
            if keys == ["__doc__", "__name__"]:
                # this is an empty virtual package; don't allow the import
                mod = None
        return mod

    def resolve(self, name):
        l = name.rfind('.')
        mod = self.findModule(name[:l])
        return getattr(mod, name[l+1:])

    # GlobalRegistrationStack calls back to these to allow the set of
    # modules to be properly maintained.

    def activateRegistration(self, stack, registration):
        if IModuleRegistration.isImplementedBy(registration):
            component = stack.module
        else:
            component = registration.getComponent()
        pkgname, attrname = _split_name(registration.name)
        while pkgname:
            pkg = self._bindings[pkgname].getModule()
            d = pkg.__dict__
            if attrname in d:
                # XXX something is here that we don't expect; scream
                assert d[attrname] is component
                break
            if "__file__" in d:
                raise ValueError(
                    "cannot nest modules in the global package structure;"
                    " %s is a module, not a package" % pkgname)
            setattr(pkg, attrname, component)
            component = pkg
            pkgname, attrname = _split_name(pkgname)

    def deactivateRegistration(self, stack, registration):
        pkgname, attrname = _split_name(registration.name)
        while pkgname:
            pkg = self._bindings[pkgname].module
            delattr(pkg, attrname)
            pkgname, attrname = _split_name(pkgname)


_dotjoin = ".".join

def _split_name(name):
    # split a dotted name into a module.attr pair
    x = name.rfind(".")
    if x < 0:
        return None, name
    else:
        return name[:x], name[x+1:]



class GlobalModuleStatusProperty(RegistrationStatusProperty):

    """Descriptor for a property that affects a global registration."""

    def _get_service(self, registration):
        """Return the Module service.  There can be only one."""
        return zapi.getService(registration, Modules)


class GlobalModuleRegistration(NamedComponentRegistration):
    """Registration object that modifies the global module service
    instead of a local service."""

    implements(IModuleRegistration)

    label = "modules"
    serviceType = Modules

    status = GlobalModuleStatusProperty()

    def getInterface(self):
        return IModuleManager


class GlobalRegistrationStack(Persistent, Contained):
    """Registration registry implementation with module semantics.

    The invariants for _data are as follows:

    1. None occurs exactly once

    2. No value occurs more than once

    3. Each value except None is an object implementing IRegistration
       for a persistent module or a persistent schema.
    """

    implements(interfaces.IRegistrationStack)

    def __init__(self, container, name):
        # This part is like a "normal" registration stack:
        self.__parent__ = container
        self.__name__ = name
        self._data = None,
        self.module = None

    def register(self, registration):
        # add inactive registration
        if registration not in self._data:
            if not interfaces.INamedComponentRegistration.isImplementedBy(
                registration):
                raise TypeError("global registrations must implement"
                                " INamedComponentRegistration")
            self._data += (registration, )

    def unregister(self, registration):
        data = self._data
        if data[0] is registration:
            self._deactivate(registration)
            data = data[1:]
            self._activate(data[0])
        else:
            # Remove it from our data
            data = tuple([item for item in data if item is not registration])
        # Write data back
        self._data = data

    def registered(self, registration):
        return registration in self._data

    def activate(self, registration):
        data = self._data
        if data[0] is registration:
            return # already in the state we want
        if registration in data:
            # registered, but not active
            self._deactivate(data[0])
            # Insert it in front, removing it from back
            data = [item for item in data if item is not registration]
            data.insert(0, registration)
            # Write data back
            self._data = tuple(data)
            self._activate(registration)
        else:
            raise ValueError(
                "Registration to be activated is not registered",
                registration)

    def deactivate(self, registration):
        data = self._data
        if registration not in data:
            raise ValueError(
                "Registration to be deactivated is not registered",
                registration)
        if data[0] is not registration:
            return # already inactive
        self._deactivate(registration)
        # Move it to the end
        data = data[1:] + data[:1]
        self._activate(data[0])
        # Write data back
        self._data = data

    def active(self):
        return self._data[0]

    def __nonzero__(self):
        return len(self._data) > 1

    def info(self, keep_dummy=False):
        result = [{'id': registration and registration.componentPath or "",
                   'active': False,
                   'registration': registration,
                  }
                  for registration in self._data
                 ]
        result[0]['active'] = True
        if not keep_dummy:
            # Throw away dummy:
            result = [x for x in result if x['registration'] is not None]
        return result

    def getModule(self):
        if self.module is None:
            self.module = module.PersistentModule(self.__name__)
            self.module.__doc__ = None
        return self.module

    # Helpers to make sure everything needed to activate or deactivate
    # a registration is handled consistently, regardless of why the
    # activation or deactivation occurred.

    def _activate(self, registration):
        if registration is None:
            return
        # activate a registration, updating the stored module if the
        # registration if for a module
        registration.activated()
        if IModuleRegistration.isImplementedBy(registration):
            localmod = registration.getComponent()
            mod = self.getModule()
            mod.__file__ = registration.componentPath
            module.compileModule(mod, self.__parent__, localmod.source)
        self.__parent__.activateRegistration(self, registration)

    def _deactivate(self, registration):
        if registration is None:
            return
        registration.deactivated()
        if IModuleRegistration.isImplementedBy(registration):
            mod = self.module
            builtins = mod.__dict__.get("__builtins__")
            mod.__dict__.clear()
            if builtins is not None:
                mod.__builtins__ = builtins
            mod.__doc__ = None
            mod.__name__ = self.__name__
        self.__parent__.deactivateRegistration(self, registration)


=== Zope3/src/zope/app/services/module.py 1.21 => 1.21.2.1 ===
--- Zope3/src/zope/app/services/module.py:1.21	Thu Jan 15 11:08:05 2004
+++ Zope3/src/zope/app/services/module.py	Thu Jan 15 15:50:21 2004
@@ -16,27 +16,43 @@
 $Id$
 """
 
+import re
+
+from keyword import iskeyword
+
 from persistence import Persistent
 from zodb.code.module import PersistentModule, compileModule
-from zope.app.event import function
-from zope.app.interfaces.annotation import IAttributeAnnotatable
-from zope.app.interfaces.file import IFileFactory
-from zope.app.interfaces.services.module import IModuleManager
-from zope.fssync.server.entryadapter import ObjectEntryAdapter, AttrMapping
+
+from zope.fssync.server.entryadapter import ObjectEntryAdapter
 from zope.fssync.server.interfaces import IObjectFile
 from zope.interface import implements
 from zope.security.proxy import trustedRemoveSecurityProxy
+
+from zope.app import zapi
 from zope.app.container.contained import Contained
+from zope.app.event import function
+from zope.app.interfaces.annotation import IAttributeAnnotatable
+from zope.app.interfaces.file import IFileFactory
+from zope.app.interfaces.services.module import IModuleManager
+from zope.app.interfaces.services.registration import ActiveStatus
+from zope.app.services.servicenames import Modules
+
 
 class Manager(Persistent, Contained):
 
     implements(IModuleManager, IAttributeAnnotatable)
 
-    def __init__(self, name, source):
-        self.name = name
+    def __init__(self, source):
         self._source = None
         self.source = source
 
+    def name(self):
+        name = self.__name__
+        if name.endswith(".py"):
+            name = name[:-3]
+        return name
+    name = property(name)
+
     def __setstate__(self, state):
         manager = state.get('_manager')
         if manager is None:
@@ -44,7 +60,6 @@
 
         # We need to convert an old-style manager
         self._module = manager._module
-        self.name = manager.name
         self._source = manager.source
         self._recompile = False
 
@@ -53,7 +68,9 @@
             mod = self._module
         except AttributeError:
             mod = self._module = PersistentModule(self.name)
-
+        else:
+            if mod.__name__ != self.name:
+                mod.__name__ = self.name
 
         folder = self.__parent__
 
@@ -64,8 +81,21 @@
         # When we do support untrusted code, we're going to have to do
         # something different.
         folder = trustedRemoveSecurityProxy(folder)
+        regmgr = folder.getRegistrationManager()
+        modules = zapi.getService(self, Modules)
+        mypath = zapi.getPath(self)
 
         compileModule(mod, folder, self.source)
+        non_local = None
+        for registration in regmgr.values():
+            if (registration.componentPath == mypath
+                and registration.status == ActiveStatus):
+                # Get the global module that needs to be updated and
+                # re-execute the source there as well:
+                mod = modules.findModule(registration.name)
+                if non_local is None:
+                    non_local = NonLocalImportRegistry(folder)
+                compileModule(mod, non_local, self.source)
         self._recompile = False
 
     def getModule(self):
@@ -82,11 +112,72 @@
             self._recompile = True
     source = property(_get_source, _set_source)
 
+    def getSize(self):
+        return len(self._source)
+
+    def __name__(self):
+        return self.__dict__.get("__name__")
+    def _set__name__(self, name):
+        # check
+        if "." in name:
+            base, ext = name.split(".", 1)
+            if not isident(base):
+                raise ValueError("invalid module name: %r"
+                                 " (first part must be a Python identifier)"
+                                 % name)
+            if ext != "py":
+                raise ValueError("invalid module name: %r"
+                                 " (extension must be '.py')"
+                                 % name)
+        elif not isident(name):
+            raise ValueError("invalid module name: %r" % name)
+        oldname = self.__name__
+        if oldname is None:
+            # too early to re-execute, so just return (we don't have
+            # source yet!)
+            self.__dict__["__name__"] = name
+            self._p_changed = 1
+            self._recompile = True
+            return
+        oldpath = zapi.getPath(self)
+        self.__dict__["__name__"] = name
+        self._p_changed = 1
+        newpath = zapi.getPath(self)
+        mod = getattr(self, "_module", None)
+        recompile = mod is not None and not self._recompile
+        # update componentPath for registrations of this module
+        regmgr = self.__parent__.getRegistrationManager()
+        for registration in regmgr.values():
+            if registration.componentPath == oldpath:
+                registration.componentPath = newpath
+                recompile = True
+        if recompile:
+            self.execute()
+    __name__ = property(__name__, _set__name__)
+
 
 # Hack to allow unpickling of old Managers to get far enough for __setstate__
 # to do it's magic:
 Registry = Manager
 
+
+_ident_rx = re.compile("[a-zA-Z_][a-zA-Z_0-9]*$")
+
+def isident(name):
+    """Return true iff name is a valid Python identifier."""
+    return _ident_rx.match(name) is not None and not iskeyword(name)
+
+
+class NonLocalImportRegistry:
+    def __init__(self, context):
+        self.context = context
+
+    def findModule(self, name):
+        if name == "__folder__" or name.startswith("__folder__."):
+            return None
+        return self.context._find_non_folder_module(name)
+
+
 class ModuleAdapter(ObjectEntryAdapter):
 
     implements(IObjectFile)
@@ -95,10 +186,8 @@
         return self.context.source
 
     def setBody(self, source):
-        self.context.update(source)
-
-    def extra(self):
-        return AttrMapping(self.context, ("name",))
+        self.context.source = source
+        self.context.execute()
 
 
 class ModuleFactory(object):
@@ -110,8 +199,8 @@
 
     def __call__(self, name, content_type, data):
         assert name.endswith(".py")
-        name = name[:-3]
-        m = Manager(name, data)
+        m = Manager(data)
+        m.__name__ = name
         m.__parent__ = self.context
         m.execute()
         return m


=== Zope3/src/zope/app/services/module.zcml 1.4 => 1.4.18.1 ===
--- Zope3/src/zope/app/services/module.zcml:1.4	Fri Aug 22 16:02:20 2003
+++ Zope3/src/zope/app/services/module.zcml	Thu Jan 15 15:50:21 2004
@@ -25,10 +25,50 @@
     permission="zope.ManageContent"
     />
 
+<adapter
+    for="zope.app.interfaces.services.module.IModuleManager"
+    provides="zope.app.interfaces.size.ISized"
+    factory="zope.app.size.DefaultSized"
+    permission="zope.Public"
+    />
+
+<!-- Global module support -->
+<serviceType
+    id="Modules"
+    interface="zope.app.interfaces.services.module.IModuleService"
+    />
+
+<content class="zope.app.services.globalmodule.ModuleService">
+    <require
+        permission="zope.ManageServices"
+        interface=
+            "zope.app.interfaces.services.registration.INameComponentRegistry"
+        />
+</content>
+
+<content class="zope.app.services.globalmodule.GlobalModuleRegistration">
+    <require
+        permission="zope.ManageServices"
+        interface=
+            "zope.app.interfaces.services.module.IModuleRegistration"
+        set_schema=
+            "zope.app.interfaces.services.module.IModuleRegistration"
+        />
+</content>
+
+<content class="zope.app.services.globalmodule.GlobalRegistrationStack">
+  <require
+      permission="zope.ManageServices"
+      interface=
+      "zope.app.interfaces.services.registration.IRegistrationStack"
+      />
+</content>
+
+
 <!-- Enable import of persistent modules -->
 <event:subscribe
-  subscriber=".module.installPersistentModuleImporter"
-  event_types="zope.app.interfaces.event.IProcessStartingEvent"
-  />
+    subscriber=".module.installPersistentModuleImporter"
+    event_types="zope.app.interfaces.event.IProcessStartingEvent"
+    />
 
 </configure>


=== Zope3/src/zope/app/services/registration.py 1.20 => 1.20.4.1 ===
--- Zope3/src/zope/app/services/registration.py:1.20	Tue Jan 13 14:32:23 2004
+++ Zope3/src/zope/app/services/registration.py	Thu Jan 15 15:50:21 2004
@@ -17,25 +17,27 @@
 """
 __metaclass__ = type
 
-from zope.app import zapi
+import sys
 
 from persistence import Persistent
-from zope.interface import implements
-from zope.fssync.server.interfaces import IObjectFile
+from zope.exceptions import DuplicationError
 from zope.fssync.server.entryadapter import ObjectEntryAdapter
-from zope.proxy import removeAllProxies, getProxiedObject
+from zope.fssync.server.interfaces import IObjectFile
+from zope.interface import implements
+from zope.proxy import removeAllProxies
 from zope.security.checker import InterfaceChecker, CheckerPublic
 from zope.security.proxy import Proxy, trustedRemoveSecurityProxy
-from zope.exceptions import DuplicationError
 from zope.xmlpickle import dumps, loads
 
+from zope.app import zapi
 from zope.app.container.contained import Contained
-from zope.app.container.contained import setitem, contained, uncontained
+from zope.app.container.contained import setitem, uncontained
 from zope.app.interfaces.annotation import IAttributeAnnotatable
 from zope.app.interfaces.container import IAddNotifiable, IRemoveNotifiable
 from zope.app.interfaces.dependable import IDependable, DependencyError
 from zope.app.interfaces.services import registration as interfaces
 from zope.app.interfaces.services.module import IModuleManager
+from zope.app.services.servicenames import Modules
 
 
 class RegistrationStatusProperty(object):
@@ -722,39 +724,60 @@
 
     def findModule(self, name):
         # Used by the persistent modules import hook
+        if name.startswith("__folder__."):
+            return self._find_folder_module(name.split(".", 1)[1])
+        elif name == "__folder__":
+            return FolderPackage(self._find_folder_module)
+        else:
+            return self._find_non_folder_module(name)
+
+    def _find_folder_module(self, name):
+        if "." in name:
+            return None
 
         # Look for a .py file first:
-        manager = self.get(name+'.py')
+        manager = self.get(name + '.py')
         if manager is not None:
-            # found an item with that name, make sure it's a module(manager):
+            # found item with that name, make sure it's a module(manager):
             if IModuleManager.isImplementedBy(manager):
                 return manager.getModule()
 
-        # Look for the module in this folder:
+        # Now look for a module with the .py:
         manager = self.get(name)
         if manager is not None:
-            # found an item with that name, make sure it's a module(manager):
+            # found item with that name, make sure it's a module(manager):
             if IModuleManager.isImplementedBy(manager):
                 return manager.getModule()
 
-
-        # See if out container is a RegistrationManagerContainer:
-        c = self.__parent__
-        if interfaces.IRegistrationManagerContainer.isImplementedBy(c):
-            return c.findModule(name)
-
-        # Use sys.modules in lieu of module service:
-        module = sys.modules.get(name)
-        if module is not None:
-            return module
-
-        raise ImportError(name)
-
+    def _find_non_folder_module(self, name):
+        # Not a __folder__ import; get the module service and ask
+        # that in case there's a global module
+        module = zapi.getService(self, Modules).findModule(name)
+
+        # look in sys.modules if we still haven't found it
+        if module is None:
+            module = sys.modules.get(name)
+        return module
 
     def resolve(self, name):
         l = name.rfind('.')
         mod = self.findModule(name[:l])
         return getattr(mod, name[l+1:])
+
+
+class FolderPackage(type(sys)):
+    """Package that loads modules from the database on attribute access."""
+
+    def __init__(self, loader):
+        super(FolderPackage, self).__init__("__folder__")
+        self.__path__ = []
+        self.__loader = loader
+
+    def __getattr__(self, name):
+        m = self.__loader(name)
+        if m is None:
+            raise AttributeError, name
+        return m
 
 
 class ComponentRegistrationAdapter(ObjectEntryAdapter):


=== Zope3/src/zope/app/services/service.py 1.36 => 1.36.4.1 ===
--- Zope3/src/zope/app/services/service.py:1.36	Tue Jan 13 14:32:23 2004
+++ Zope3/src/zope/app/services/service.py	Thu Jan 15 15:50:21 2004
@@ -241,22 +241,22 @@
 
         return local
 
-    def findModule(wrapped_self, name):
-        # override to pass call up to next service manager
-        mod = super(ServiceManager,
-                    removeAllProxies(wrapped_self)).findModule(name)
-        if mod is not None:
-            return mod
+##    def findModule(wrapped_self, name):
+##        # override to pass call up to next service manager
+##        mod = super(ServiceManager,
+##                    removeAllProxies(wrapped_self)).findModule(name)
+##        if mod is not None:
+##            return mod
 
-        sm = getNextServiceManager(wrapped_self)
-        try:
-            findModule = sm.findModule
-        except AttributeError:
-            # The only service manager that doesn't implement this
-            # interface is the global service manager.  There is no
-            # direct way to ask if sm is the global service manager.
-            return None
-        return findModule(name)
+##        sm = getNextServiceManager(wrapped_self)
+##        try:
+##            findModule = sm.findModule
+##        except AttributeError:
+##            # The only service manager that doesn't implement this
+##            # interface is the global service manager.  There is no
+##            # direct way to ask if sm is the global service manager.
+##            return None
+##        return findModule(name)
 
     def __import(wrapped_self, module_name):
 


=== Zope3/src/zope/app/services/servicenames.py 1.13 => 1.13.2.1 ===
--- Zope3/src/zope/app/services/servicenames.py:1.13	Wed Jan 14 17:55:28 2004
+++ Zope3/src/zope/app/services/servicenames.py	Thu Jan 15 15:50:21 2004
@@ -27,6 +27,7 @@
 EventSubscription = 'Subscription'
 ErrorLogging = 'ErrorLogging'
 HubIds = 'HubIds'
+Modules = 'Modules'
 Permissions = 'Permissions'
 PrincipalAnnotation = 'PrincipalAnnotation'
 Translation = 'Translation'




More information about the Zope3-Checkins mailing list