[Repoze-checkins] r1648 - in repoze.bfg/trunk: . repoze/bfg repoze/bfg/tests repoze/bfg/tests/fixtureapp

Chris McDonough chrism at agendaless.com
Fri Aug 22 20:03:29 EDT 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Fri Aug 22 20:03:29 2008
New Revision: 1648

Log:
  - Read and write a pickled ZCML actions list, stored as
    ``configure.zcml.pck`` next to the applications's "normal" 
    configuration file.  A given bfg app will usually start faster
    if it's able to read the pickle data.  It fails gracefully
    to reading the real ZCML file if it cannot read the pickle.



Added:
   repoze.bfg/trunk/repoze/bfg/path.py   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/another.zcml   (contents, props changed)
Modified:
   repoze.bfg/trunk/CHANGES.txt
   repoze.bfg/trunk/repoze/bfg/registry.py
   repoze.bfg/trunk/repoze/bfg/template.py
   repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/configure.zcml
   repoze.bfg/trunk/repoze/bfg/tests/test_zcml.py
   repoze.bfg/trunk/repoze/bfg/zcml.py

Modified: repoze.bfg/trunk/CHANGES.txt
==============================================================================
--- repoze.bfg/trunk/CHANGES.txt	(original)
+++ repoze.bfg/trunk/CHANGES.txt	Fri Aug 22 20:03:29 2008
@@ -1,3 +1,11 @@
+Next release
+
+  - Read and write a pickled ZCML actions list, stored as
+    ``configure.zcml.pck`` next to the applications's "normal" 
+    configuration file.  A given bfg app will usually start faster
+    if it's able to read the pickle data.  It fails gracefully
+    to reading the real ZCML file if it cannot read the pickle.
+
 0.3.1 (8/20/2008)
 
   - Generated application differences: ``make_app`` entry point

Added: repoze.bfg/trunk/repoze/bfg/path.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/path.py	Fri Aug 22 20:03:29 2008
@@ -0,0 +1,15 @@
+import os
+import sys
+
+def caller_path(path, level=2):
+    if not os.path.isabs(path):
+        package_globals = sys._getframe(level).f_globals
+        package_name = package_globals['__name__']
+        package = sys.modules[package_name]
+        prefix = package_path(package)
+        path = os.path.join(prefix, path)
+    return path
+
+def package_path(package):
+    return os.path.abspath(os.path.dirname(package.__file__))
+

Modified: repoze.bfg/trunk/repoze/bfg/registry.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/registry.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/registry.py	Fri Aug 22 20:03:29 2008
@@ -1,4 +1,5 @@
 import threading
+
 import zope.component
 
 from zope.component import getGlobalSiteManager
@@ -6,11 +7,11 @@
 from zope.component.interfaces import IComponentLookup
 from zope.component.registry import Components
 from zope.component import getSiteManager as original_getSiteManager
-from zope.configuration import xmlconfig
 
 from zope.interface import implements
 
 from repoze.bfg.interfaces import ISettings
+from repoze.bfg.zcml import zcml_configure
 
 class ThreadLocalRegistryManager(threading.local):
     registry = getGlobalSiteManager()
@@ -52,7 +53,7 @@
         registry_manager.set(registry)
         original_getSiteManager.sethook(getSiteManager)
         zope.component.getGlobalSiteManager = registry_manager.get
-        xmlconfig.file(filename, package=package)
+        zcml_configure(filename, package=package)
         if options is None:
             options = {}
         settings = Settings(options)

Modified: repoze.bfg/trunk/repoze/bfg/template.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/template.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/template.py	Fri Aug 22 20:03:29 2008
@@ -1,5 +1,4 @@
 import os
-import sys
 
 from webob import Response
 
@@ -10,6 +9,7 @@
 from zope.interface import classProvides
 from zope.interface import implements
 
+from repoze.bfg.path import caller_path
 from repoze.bfg.interfaces import ITemplateFactory
 from repoze.bfg.interfaces import ITemplate
 from repoze.bfg.interfaces import INodeTemplate
@@ -63,9 +63,6 @@
     xslt_pool.processors[xslt_fn] = proc
     return proc
 
-def package_path(package):
-    return os.path.abspath(os.path.dirname(package.__file__))
-
 def registerTemplate(type, template, path):
     try:
         sm = getSiteManager()
@@ -134,13 +131,4 @@
     result = render_transform(path, node, **kw)
     return Response(result)
 
-def caller_path(path):
-    if not os.path.isabs(path):
-        package_globals = sys._getframe(2).f_globals
-        package_name = package_globals['__name__']
-        package = sys.modules[package_name]
-        prefix = package_path(package)
-        path = os.path.join(prefix, path)
-    return path
-
     

Added: repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/another.zcml
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/another.zcml	Fri Aug 22 20:03:29 2008
@@ -0,0 +1,12 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+    xmlns:bfg="http://namespaces.repoze.org/bfg"
+	           i18n_domain="repoze.bfg">
+
+  <bfg:view
+      view=".views.fixture_view"
+      for="*"
+      name="another.html"
+      permission="repoze.view"
+      />
+
+</configure>

Modified: repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/configure.zcml
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/configure.zcml	(original)
+++ repoze.bfg/trunk/repoze/bfg/tests/fixtureapp/configure.zcml	Fri Aug 22 20:03:29 2008
@@ -18,4 +18,7 @@
       request_type=".views.IDummy"
       />
 
+  <include file="another.zcml"/>
+
+
 </configure>

Modified: repoze.bfg/trunk/repoze/bfg/tests/test_zcml.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/tests/test_zcml.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/tests/test_zcml.py	Fri Aug 22 20:03:29 2008
@@ -136,6 +136,189 @@
         new = cPickle.loads(dumped)
         self.assertEqual(len(actions), len(new))
 
+class TestZCMLPickling(unittest.TestCase, PlacelessSetup):
+    i = 0
+    def setUp(self):
+        self.tempdir = None
+        PlacelessSetup.setUp(self)
+        import sys
+        import os
+        import tempfile
+        from repoze.bfg.path import package_path
+        from repoze.bfg.tests import fixtureapp as package
+        import shutil
+        tempdir = tempfile.mkdtemp()
+        modname = 'myfixture%s' % self.i
+        self.i += 1
+        self.packagepath = os.path.join(tempdir, modname)
+        fixturedir = package_path(package)
+        pckname = os.path.join(fixturedir, 'configure.zcml.pck')
+        if os.path.isfile(pckname):
+            os.remove(pckname)
+        shutil.copytree(fixturedir, self.packagepath)
+        sys.path.insert(0, tempdir)
+        self.module = __import__(modname)
+        self.tempdir = tempdir
+
+    def tearDown(self):
+        PlacelessSetup.tearDown(self)
+        import sys
+        import shutil
+        if self.module is not None:
+            del sys.modules[self.module.__name__]
+        if self.tempdir is not None:
+            sys.path.pop(0)
+            shutil.rmtree(self.tempdir)
+
+    def test_file_configure(self):
+        import os
+        import cPickle
+        from repoze.bfg.zcml import file_configure
+        self.assertEqual(False, file_configure('configure.zcml', self.module))
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.failUnless(os.path.exists(picklename))
+        actions = cPickle.load(open(picklename, 'rb'))
+        self.failUnless(actions)
+
+    def test_file_configure_nonexistent_configure_dot_zcml(self):
+        import os
+        from repoze.bfg.zcml import file_configure
+        os.remove(os.path.join(self.packagepath, 'configure.zcml'))
+        self.assertRaises(IOError, file_configure, 'configure.zcml',
+                          self.module)
+
+    def test_file_configure_pickling_error(self):
+        import os
+        from repoze.bfg.zcml import file_configure
+        def dumpfail(actions, f):
+            raise IOError
+        self.assertEqual(False,
+                      file_configure('configure.zcml', self.module, dumpfail))
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.failIf(os.path.exists(picklename))
+
+    def test_zcml_configure_writes_pickle_when_none_exists(self):
+        import os
+        import cPickle
+        from repoze.bfg.zcml import zcml_configure
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.failUnless(os.path.exists(picklename))
+        actions = cPickle.load(open(picklename, 'rb'))
+        self.failUnless(actions)
+
+    def test_zcml_configure_uses_file_configure_with_bad_pickle1(self):
+        import os
+        import cPickle
+        from repoze.bfg.zcml import zcml_configure
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        f = open(picklename, 'wb')
+        cPickle.dump((), f)
+        f.close()
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_file_configure_with_bad_pickle2(self):
+        import os
+        from repoze.bfg.zcml import zcml_configure
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        f = open(picklename, 'wb')
+        f.write('garbage')
+        f.close()
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_file_configure_with_outofdate_pickle1(self):
+        import os
+        import cPickle
+        import time
+        from repoze.bfg.zcml import zcml_configure
+        basename = os.path.join(self.packagepath, 'configure.zcml')
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+        self.failUnless(os.path.exists(picklename))
+        actions = cPickle.load(open(picklename, 'rb'))
+        self.failUnless(actions)
+        os.utime(basename, (-1, time.time() + 100))
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_file_configure_with_outofdate_pickle2(self):
+        import os
+        import cPickle
+        import time
+        from repoze.bfg.zcml import zcml_configure
+        basename = os.path.join(self.packagepath, 'another.zcml')
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+        self.failUnless(os.path.exists(picklename))
+        actions = cPickle.load(open(picklename, 'rb'))
+        self.failUnless(actions)
+        os.utime(basename, (-1, time.time() + 100))
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_file_configure_with_missing_dependent(self):
+        import os
+        import cPickle
+        from repoze.bfg.zcml import zcml_configure
+        from zope.configuration.xmlconfig import ZopeXMLConfigurationError
+        basename = os.path.join(self.packagepath, 'another.zcml')
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+        self.failUnless(os.path.exists(picklename))
+        actions = cPickle.load(open(picklename, 'rb'))
+        self.failUnless(actions)
+        os.remove(basename)
+        self.assertRaises(ZopeXMLConfigurationError, zcml_configure,
+                          'configure.zcml', self.module)
+
+    def test_zcml_configure_uses_file_configure_with_bad_version(self):
+        import os
+        from repoze.bfg.zcml import zcml_configure
+        from repoze.bfg.zcml import PVERSION
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        f = open(picklename, 'wb')
+        import cPickle
+        data = (PVERSION+1, 0, [])
+        cPickle.dump(data, open(picklename, 'wb'))
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_file_configure_with_bad_time(self):
+        import os
+        from repoze.bfg.zcml import zcml_configure
+        from repoze.bfg.zcml import PVERSION
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        f = open(picklename, 'wb')
+        import cPickle
+        data = (PVERSION, None, [])
+        cPickle.dump(data, open(picklename, 'wb'))
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_file_configure_with_bad_actions(self):
+        import os
+        from repoze.bfg.zcml import zcml_configure
+        from repoze.bfg.zcml import PVERSION
+        import time
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        f = open(picklename, 'wb')
+        import cPickle
+        data = (PVERSION, time.time()+500, None)
+        cPickle.dump(data, open(picklename, 'wb'))
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+
+    def test_zcml_configure_uses_good_pickle(self):
+        import os
+        import cPickle
+        import time
+        from repoze.bfg.zcml import zcml_configure
+        from repoze.bfg.zcml import PVERSION
+        basename = os.path.join(self.packagepath, 'another.zcml')
+        picklename = os.path.join(self.packagepath, 'configure.zcml.pck')
+        self.assertEqual(False, zcml_configure('configure.zcml', self.module))
+        self.failUnless(os.path.exists(picklename))
+        actions = cPickle.load(open(picklename, 'rb'))
+        self.failUnless(actions)
+        actions = (PVERSION, time.time()+100, actions[2])
+        cPickle.dump(actions, open(picklename, 'wb'))
+        self.assertEqual(True, zcml_configure('configure.zcml', self.module))
+
 class Dummy:
     pass
 

Modified: repoze.bfg/trunk/repoze/bfg/zcml.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/zcml.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/zcml.py	Fri Aug 22 20:03:29 2008
@@ -1,3 +1,11 @@
+import cPickle
+import os
+from os.path import realpath
+import time
+
+from zope.configuration import xmlconfig
+import zope.configuration.config
+
 from zope.component.zcml import handler
 from zope.component.interface import provideInterface
 from zope.configuration.exceptions import ConfigurationError
@@ -10,6 +18,7 @@
 from repoze.bfg.interfaces import IRequest
 from repoze.bfg.interfaces import IViewPermission
 from repoze.bfg.interfaces import IView
+from repoze.bfg.path import package_path
 
 from repoze.bfg.security import ViewPermissionFactory
 
@@ -84,4 +93,80 @@
         required=False
         )
 
+PVERSION = 0
+
+def pickle_name(name, package):
+    path = package_path(package)
+    basename = os.path.join(path, name)
+    return os.path.join(path, basename + '.pck')
+
+def zcml_configure(name, package, load=cPickle.load):
+    """ Execute pickled zcml actions or fall back to parsing from file
+    """
+    pckname = pickle_name(name, package)
+
+    if not (os.path.isfile(pckname) or os.path.islink(pckname)):
+        return file_configure(name, package)
+
+    try:
+        vers, ptime, actions = load(open(pckname, 'rb'))
+    except (IOError, cPickle.UnpicklingError, EOFError, TypeError, ValueError):
+        return file_configure(name, package)
+
+    if vers != PVERSION:
+        return file_configure(name, package)
+
+    try:
+        ptime = int(ptime)
+    except:
+        return file_configure(name, package)
+
+    if not hasattr(actions, '__iter__'):
+        return file_configure(name, package)
+
+    files = set()
+    for action in actions:
+        # files list used by pickled action is an element of the tuple
+        try:
+            files.update(action[4])
+        except (TypeError, IndexError):
+            return file_configure(name, package)
+
+    for file in files:
+        if not(os.path.isfile(file) or os.path.islink(file)):
+            return file_configure(name, package)
+
+        mtime = os.stat(realpath(file)).st_mtime
+
+        if  mtime >= ptime:
+            return file_configure(name, package)
+
+    context = zope.configuration.config.ConfigurationMachine()
+    xmlconfig.registerCommonDirectives(context)
+    context.actions = actions
+    context.execute_actions()
+    return True
+
+def file_configure(name, package, dump=cPickle.dump):
+    context = zope.configuration.config.ConfigurationMachine()
+    xmlconfig.registerCommonDirectives(context)
+    context.package = package
+
+    xmlconfig.include(context, name, package)
+    context.execute_actions(clear=False)
+
+    actions = context.actions
+    pckname = pickle_name(name, package)
+
+    try:
+        data = (PVERSION, time.time(), actions)
+        dump(data, open(pckname, 'wb'), -1)
+    except (OSError, IOError, TypeError, cPickle.PickleError):
+        try:
+            os.remove(pckname)
+        except:
+            pass
+
+    return False
+
 


More information about the Repoze-checkins mailing list