[Checkins] SVN: grokcore.component/trunk/ change
determine_module_component so it takes an interface instead of
Martijn Faassen
faassen at infrae.com
Sat May 3 11:12:04 EDT 2008
Log message for revision 86233:
change determine_module_component so it takes an interface instead of
a list of base classes.
Also make Context implement IContext, so we can hook up things to that
instead of to the general base class.
Changed:
U grokcore.component/trunk/CHANGES.txt
U grokcore.component/trunk/src/grokcore/component/components.py
A grokcore.component/trunk/src/grokcore/component/interfaces.py
U grokcore.component/trunk/src/grokcore/component/meta.py
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py
A grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py
U grokcore.component/trunk/src/grokcore/component/tests/test_grok.py
U grokcore.component/trunk/src/grokcore/component/util.py
-=-
Modified: grokcore.component/trunk/CHANGES.txt
===================================================================
--- grokcore.component/trunk/CHANGES.txt 2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/CHANGES.txt 2008-05-03 15:12:03 UTC (rev 86233)
@@ -4,7 +4,11 @@
1.1 (unreleased)
----------------
-* ...
+* ``determine_module_component`` now looks for classes that implement
+ a certain interface (such as ``IContext``), instead of taking a list
+ of classes. If looking for ``IContext``, it still will find
+ ``Context`` subclasses, as these were also made to implement
+ ``IContext``.
1.0.1 (2008-05-02)
------------------
Modified: grokcore.component/trunk/src/grokcore/component/components.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/components.py 2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/components.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -13,6 +13,10 @@
##############################################################################
"""Grok components"""
+from zope.interface import implements
+
+from grokcore.component.interfaces import IContext
+
class Adapter(object):
def __init__(self, context):
self.context = context
@@ -24,4 +28,4 @@
pass
class Context(object):
- pass
+ implements(IContext)
Added: grokcore.component/trunk/src/grokcore/component/interfaces.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/interfaces.py (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/interfaces.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,18 @@
+from zope.interface import Interface
+
+class IContext(Interface):
+ """Marker interface for auto-association of context.
+
+ The ``grok.context()`` directive is used to associate adapters with the
+ class or interface they adapt. If there is only a single possible context
+ object to adapt to in a module, you can leave out this directive and
+ let the adapter associate automatically.
+
+ If you want to make an object to be a candidate for this automatic
+ association, you can subclass from ``grokcore.component.Context``.
+ This implements this ``IContext`` directive.
+
+ In some cases, you don't want to mix in a base class. You can instead
+ mark up your class with ``zope.interface.implements(IContext)`` to make
+ it a candidate for auto-association.
+ """
Modified: grokcore.component/trunk/src/grokcore/component/meta.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/meta.py 2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/meta.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -25,6 +25,7 @@
from grokcore.component.util import determine_module_component
from grokcore.component.util import determine_class_component
from grokcore.component.util import check_provides_one
+from grokcore.component.interfaces import IContext
def get_context(module_info, factory):
return determine_class_component(module_info, factory,
@@ -55,7 +56,7 @@
def grok(self, name, module, module_info, config, **kw):
context = determine_module_component(module_info, 'grok.context',
- [grokcore.component.Context])
+ IContext)
module.__grok_context__ = context
return True
Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components.txt 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,32 @@
+Scanning for the context object
+-------------------------------
+
+Let's import a module that contains no ``Context`` subclass, nor classes
+that implement ``IContext``::
+
+ >>> import grokcore.component.tests.scan_for_module_components_fixture as tests
+ >>> from grokcore.component.interfaces import IContext
+
+We shouldn't see any classes that are contexts::
+
+ >>> from grokcore.component.util import scan_for_module_components
+ >>> scan_for_module_components(tests.test1, IContext)
+ []
+
+Now we look at a module with a single ``Context`` subclass::
+
+ >>> scan_for_module_components(tests.test2, IContext)
+ [<class 'grokcore.component.tests.scan_for_module_components_fixture.test2.MyContext'>]
+
+Now we'll look at a module with a single class that implements ``IContext``::
+
+ >>> scan_for_module_components(tests.test3, IContext)
+ [<class 'grokcore.component.tests.scan_for_module_components_fixture.test3.MyContext'>]
+
+Let's finish by looking at a module which defines multiple contexts::
+
+ >>> len(scan_for_module_components(tests.test4, IContext))
+ 4
+
+
+
Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/__init__.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,4 @@
+from grokcore.component.tests.scan_for_module_components_fixture import test1
+from grokcore.component.tests.scan_for_module_components_fixture import test2
+from grokcore.component.tests.scan_for_module_components_fixture import test3
+from grokcore.component.tests.scan_for_module_components_fixture import test4
Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test1.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,16 @@
+# this is a module that has deliberately NO Context subclass, nor a class
+# that implements IContext class. It is used to write a scan_for_context.txt
+# test.
+
+import os
+
+foo = "Bar"
+
+class Qux(object):
+ pass
+
+class Hallo:
+ pass
+
+qux = Qux()
+hallo = Hallo()
Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test2.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,20 @@
+# this is a module has a single Context subclass in it.
+# this module is used in a scan_for_context.txt test.
+
+import os
+from grokcore.component import Context
+
+foo = "Bar"
+
+class Qux(object):
+ pass
+
+class Hallo:
+ pass
+
+class MyContext(Context):
+ pass
+
+qux = Qux()
+hallo = Hallo()
+mycontext = MyContext()
Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test3.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,21 @@
+# this is a module has a single Context subclass in it.
+# this module is used in a scan_for_context.txt test.
+
+import os
+from grokcore.component.interfaces import IContext
+from zope.interface import implements
+
+foo = "Bar"
+
+class Qux(object):
+ pass
+
+class Hallo:
+ pass
+
+class MyContext(object):
+ implements(IContext)
+
+qux = Qux()
+hallo = Hallo()
+mycontext = MyContext()
Added: grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py (rev 0)
+++ grokcore.component/trunk/src/grokcore/component/tests/scan_for_module_components_fixture/test4.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -0,0 +1,31 @@
+# this is a module has a single Context subclass in it.
+# this module is used in a scan_for_context.txt test.
+
+import os
+from grokcore.component import Context
+from grokcore.component.interfaces import IContext
+from zope.interface import implements
+
+foo = "Bar"
+
+class Qux(object):
+ pass
+
+class Hallo:
+ pass
+
+class MyContext(object):
+ implements(IContext)
+
+class MyContext2(Context):
+ pass
+
+class MyContext3(MyContext):
+ pass
+
+class MyContext4(MyContext2):
+ pass
+
+qux = Qux()
+hallo = Hallo()
+mycontext = MyContext()
Modified: grokcore.component/trunk/src/grokcore/component/tests/test_grok.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/tests/test_grok.py 2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/tests/test_grok.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -48,6 +48,10 @@
for name in ['adapter', 'directive', 'grokker', 'order', 'testsetup',
'util', 'utility', 'view', 'event']:
suite.addTest(suiteFromPackage(name))
+
+ suite.addTest(doctest.DocFileSuite(
+ 'scan_for_module_components.txt',
+ optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE))
return suite
if __name__ == '__main__':
Modified: grokcore.component/trunk/src/grokcore/component/util.py
===================================================================
--- grokcore.component/trunk/src/grokcore/component/util.py 2008-05-03 15:09:33 UTC (rev 86232)
+++ grokcore.component/trunk/src/grokcore/component/util.py 2008-05-03 15:12:03 UTC (rev 86233)
@@ -17,8 +17,10 @@
from zope import component, interface
from martian.error import GrokError
-from martian.util import class_annotation, scan_for_classes
+from martian.util import class_annotation, defined_locally, isclass
+from grokcore.component.interfaces import IContext
+
def check_adapts(class_):
if component.adaptedBy(class_) is None:
raise GrokError("%r must specify which contexts it adapts "
@@ -66,16 +68,31 @@
raise GrokError("Multiple possible %ss for %r, please use "
"%s." % (component_name, factory, component_directive),
factory)
+
+def scan_for_module_components(module, iface):
+ """Given a module, scan for classes that that implements an interface.
+ """
+ result = set()
+ for name in dir(module):
+ if name.startswith('__grok_'):
+ continue
+ obj = getattr(module, name)
+ if not defined_locally(obj, module.__name__):
+ continue
+
+ if isclass(obj) and iface.implementedBy(obj):
+ result.add(obj)
+ return list(result)
-def determine_module_component(module_info, annotation, classes):
+def determine_module_component(module_info, annotation, iface):
"""Determine module-level component.
The module-level component can be set explicitly using the
annotation (such as grok.context).
If there is no annotation, the module-level component is determined
- by scanning for subclasses of any in the list of classes.
-
+ by scanning for classes that implement an interface.
+
If there is no module-level component, the module-level component is
None.
@@ -84,7 +101,7 @@
If there are more than one module-level component, AMBIGUOUS_COMPONENT
is returned.
"""
- components = scan_for_classes(module_info.getModule(), classes)
+ components = scan_for_module_components(module_info.getModule(), iface)
if len(components) == 0:
component = None
elif len(components) == 1:
More information about the Checkins
mailing list