summaryrefslogtreecommitdiff
path: root/repoze
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-08-23 00:03:29 +0000
committerChris McDonough <chrism@agendaless.com>2008-08-23 00:03:29 +0000
commit434c0514dc7dd9c7881e1d30db4d988680220af2 (patch)
tree34cb5395ec0628ec216e54d863bc90fb8902ba5a /repoze
parentbe16f6b5c59043a164120171a5b0926d158959fd (diff)
downloadpyramid-434c0514dc7dd9c7881e1d30db4d988680220af2.tar.gz
pyramid-434c0514dc7dd9c7881e1d30db4d988680220af2.tar.bz2
pyramid-434c0514dc7dd9c7881e1d30db4d988680220af2.zip
- 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.
Diffstat (limited to 'repoze')
-rw-r--r--repoze/bfg/path.py15
-rw-r--r--repoze/bfg/registry.py5
-rw-r--r--repoze/bfg/template.py14
-rw-r--r--repoze/bfg/tests/fixtureapp/another.zcml12
-rw-r--r--repoze/bfg/tests/fixtureapp/configure.zcml3
-rw-r--r--repoze/bfg/tests/test_zcml.py183
-rw-r--r--repoze/bfg/zcml.py85
7 files changed, 302 insertions, 15 deletions
diff --git a/repoze/bfg/path.py b/repoze/bfg/path.py
new file mode 100644
index 000000000..fcd317fba
--- /dev/null
+++ b/repoze/bfg/path.py
@@ -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__))
+
diff --git a/repoze/bfg/registry.py b/repoze/bfg/registry.py
index e02e5124e..9301b950b 100644
--- a/repoze/bfg/registry.py
+++ b/repoze/bfg/registry.py
@@ -1,4 +1,5 @@
import threading
+
import zope.component
from zope.component import getGlobalSiteManager
@@ -6,11 +7,11 @@ from zope.component.interfaces import ComponentLookupError
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 @@ def makeRegistry(filename, package, options=None, lock=threading.Lock()):
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)
diff --git a/repoze/bfg/template.py b/repoze/bfg/template.py
index de049f5fa..05331aad9 100644
--- a/repoze/bfg/template.py
+++ b/repoze/bfg/template.py
@@ -1,5 +1,4 @@
import os
-import sys
from webob import Response
@@ -10,6 +9,7 @@ from zope.component import getSiteManager
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 @@ def get_processor(xslt_fn, auto_reload=False):
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 @@ def render_transform_to_response(path, node, **kw):
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
-
diff --git a/repoze/bfg/tests/fixtureapp/another.zcml b/repoze/bfg/tests/fixtureapp/another.zcml
new file mode 100644
index 000000000..f29e7e59e
--- /dev/null
+++ b/repoze/bfg/tests/fixtureapp/another.zcml
@@ -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>
diff --git a/repoze/bfg/tests/fixtureapp/configure.zcml b/repoze/bfg/tests/fixtureapp/configure.zcml
index dfedda7bb..a56c1cbdf 100644
--- a/repoze/bfg/tests/fixtureapp/configure.zcml
+++ b/repoze/bfg/tests/fixtureapp/configure.zcml
@@ -18,4 +18,7 @@
request_type=".views.IDummy"
/>
+ <include file="another.zcml"/>
+
+
</configure>
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 663022796..5fa5d6290 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -136,6 +136,189 @@ class TestSampleApp(unittest.TestCase, PlacelessSetup):
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
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index 343abc1d7..0bb049516 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -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 zope.schema import TextLine
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 @@ class IViewDirective(Interface):
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
+