Skip to content

Commit

Permalink
Added meta/grokkers and test base
Browse files Browse the repository at this point in the history
  • Loading branch information
trollfot committed Apr 28, 2012
1 parent 98f6463 commit 02db61a
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/grokcore/catalog/ftests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# this is a package
43 changes: 43 additions & 0 deletions src/grokcore/catalog/ftests/test_grok_functional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-

import unittest
import grokcore.catalog

from pkg_resources import resource_listdir
from zope.testing import doctest
from zope.app.appsetup.testlayer import ZODBLayer


FunctionalLayer = ZODBLayer(grokcore.catalog)


def suiteFromPackage(name):
files = resource_listdir(__name__, name)
suite = unittest.TestSuite()
for filename in files:
if not filename.endswith('.py'):
continue
if filename == '__init__.py':
continue

dottedname = 'grokcore.catalog.ftests.%s.%s' % (name, filename[:-3])
test = doctest.DocTestSuite(
dottedname,
extraglobs=dict(getRootFolder=FunctionalLayer.getRootFolder),
optionflags=(doctest.ELLIPSIS+
doctest.NORMALIZE_WHITESPACE+
doctest.REPORT_NDIFF)
)
test.layer = FunctionalLayer

suite.addTest(test)
return suite

def test_suite():
suite = unittest.TestSuite()
for name in []:
suite.addTest(suiteFromPackage(name))
return suite

if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
134 changes: 134 additions & 0 deletions src/grokcore/catalog/meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
##############################################################################
#
# Copyright (c) 2006-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""grokcore.catalog meta
"""
import grokcore.component
import grokcore.site
import martian
import zope.component

from grokcore.catalog.components import IndexesClass
from grokcore.site import site
from martian.error import GrokError
from zope.catalog.catalog import Catalog
from zope.catalog.interfaces import ICatalog
from zope.exceptions.interfaces import DuplicationError
from zope.intid import IntIds
from zope.intid.interfaces import IIntIds
from zope.lifecycleevent.interfaces import IObjectAddedEvent


class IndexesGrokker(martian.InstanceGrokker):
"""Grokker for index bundles."""
martian.component(IndexesClass)

def grok(self, name, factory, module_info, config, **kw):
site = grokcore.site.site.bind().get(factory)
context = grokcore.component.context.bind().get(
factory, module_info.getModule())
catalog_name = grokcore.component.name.bind().get(factory)

if site is None:
raise GrokError(
"No site specified for grok.Indexes "
"subclass in module %r. "
"Use grokcore.site.site() to specify."
% module_info.getModule(), factory)

indexes = getattr(factory, '__grok_indexes__', None)
if indexes is None:
return False

subscriber = IndexesSetupSubscriber(
catalog_name, indexes, context, module_info)

subscribed = (site, IObjectAddedEvent)
config.action(
discriminator=None,
callable=zope.component.provideHandler,
args=(subscriber, subscribed))
return True


class IndexesSetupSubscriber(object):
"""Helper that sets up indexes when their Grok site is created.
Each `grokcore.catalog.Indexes` class serves as an assertion that,
whenever an instance of its `grokcore.site.site()` is created,
the given list of indexes should be generated as well.
But a long period of time could elapse between when the application
starts (and its indexes are grokked), and the moment, maybe days or
weeks later, when a new instance of that `grokcore.site.Site` is created.
Hence this `IndexesSetupSubscriber`:
it can be instantiated at grokking time with the index information,
and then registered with the Component Architecture as an event that
should be fired later, whenever the right kind of `grok.Site` is
instantiated. At that point its `__call__` method is kicked off and
it makes sure the index catalogs get created properly.
"""
def __init__(self, catalog_name, indexes, context, module_info):
self.catalog_name = catalog_name
self.indexes = indexes
self.context = context
self.module_info = module_info

def __call__(self, site, event):
# make sure we have an intids
self._createIntIds(site)

# get the catalog
catalog = self._createCatalog(site)

# now install indexes
for name, index in self.indexes.items():
try:
index.setup(catalog, name, self.context, self.module_info)
except DuplicationError:
raise GrokError(
"grok.Indexes in module %r causes "
"creation of catalog index %r in catalog %r, "
"but an index with that name is already present." %
(self.module_info.getModule(), name, self.catalog_name),
None)

def _createCatalog(self, site):
"""Create the catalog if needed and return it.
If the catalog already exists, return that.
"""
catalog = zope.component.queryUtility(
ICatalog, name=self.catalog_name, context=site, default=None)
if catalog is not None:
return catalog
catalog = Catalog()
setupUtility = zope.component.getUtility(
grokcore.site.interfaces.IUtilityInstaller)
setupUtility(site, catalog, ICatalog, name=self.catalog_name)
return catalog

def _createIntIds(self, site):
"""Create intids if needed, and return it.
"""
intids = zope.component.queryUtility(
IIntIds, context=site, default=None)
if intids is not None:
return intids
intids = IntIds()
setupUtility = zope.component.getUtility(
grokcore.site.interfaces.IUtilityInstaller)
setupUtility(site, intids, IIntIds)
return intids
9 changes: 9 additions & 0 deletions src/grokcore/catalog/meta.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok">

<!-- Load the grokkers -->
<include package="grokcore.component" file="meta.zcml" />
<grok:grok package=".meta" />

</configure>
26 changes: 26 additions & 0 deletions src/grokcore/catalog/testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
##############################################################################
#
# Copyright (c) 2007-2008 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Grok test helpers
"""
from zope.configuration.config import ConfigurationMachine
from grokcore.component import zcml


def grok(module_name):
config = ConfigurationMachine()
zcml.do_grok('grokcore.component.meta', config)
zcml.do_grok('grokcore.site.meta', config)
zcml.do_grok('grokcore.catalog.meta', config)
zcml.do_grok(module_name, config)
config.execute_actions()
1 change: 1 addition & 0 deletions src/grokcore/catalog/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# make this directory a package
57 changes: 57 additions & 0 deletions src/grokcore/catalog/tests/test_grok.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-

import re
import unittest
import zope.component.eventtesting

from pkg_resources import resource_listdir
from zope.testing import doctest, cleanup, renormalizing


def setUpZope(test):
zope.component.eventtesting.setUp(test)


def cleanUpZope(test):
cleanup.cleanUp()


checker = renormalizing.RENormalizing([
# str(Exception) has changed from Python 2.4 to 2.5 (due to
# Exception now being a new-style class). This changes the way
# exceptions appear in traceback printouts.
(re.compile(r"ConfigurationExecutionError: <class '([\w.]+)'>:"),
r'ConfigurationExecutionError: \1:'),
])


def suiteFromPackage(name):
files = resource_listdir(__name__, name)
suite = unittest.TestSuite()
for filename in files:
if not filename.endswith('.py'):
continue
if filename.endswith('_fixture.py'):
continue
if filename == '__init__.py':
continue

dottedname = 'grokcore.catalog.tests.%s.%s' % (name, filename[:-3])
test = doctest.DocTestSuite(dottedname,
setUp=setUpZope,
tearDown=cleanUpZope,
checker=checker,
optionflags=doctest.ELLIPSIS+
doctest.NORMALIZE_WHITESPACE)

suite.addTest(test)
return suite

def test_suite():
suite = unittest.TestSuite()
for name in []:
suite.addTest(suiteFromPackage(name))
return suite

if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

0 comments on commit 02db61a

Please sign in to comment.