[Commits] (pje) Spike: define query framework interfaces, and implement the "filters" layer

commits at osafoundation.org commits at osafoundation.org
Fri Mar 11 16:32:50 PST 2005

Commit by: pje
Modified files:
internal/Spike/src/spike/interfaces.py None 1.1
internal/Spike/src/spike/query.py None 1.1
internal/Spike/src/spike/models.py 1.4 1.5
internal/Spike/src/spike/overview.txt 1.11 1.12
internal/Spike/src/spike/schema.py 1.7 1.8
internal/Spike/src/spike/tests/test_query.py None 1.1
internal/Spike/src/spike/tests/__init__.py 1.8 1.9

Log message:
Spike: define query framework interfaces, and implement the "filters" layer
(i.e. boolean algebra on criteria).  Sets and entity classes are now usable
as filters, with support for union, intersect, difference, and negation.
However, the boolean algebra framework does not yet support iteration or 
subscriptions on results, only membership testing.  

ViewCVS links:

Index: internal/Spike/src/spike/overview.txt
diff -u internal/Spike/src/spike/overview.txt:1.11 internal/Spike/src/spike/overview.txt:1.12
--- internal/Spike/src/spike/overview.txt:1.11	Mon Feb 28 17:09:21 2005
+++ internal/Spike/src/spike/overview.txt	Fri Mar 11 16:32:48 2005
@@ -474,16 +474,16 @@
 to iterate over the entire database, but in practice you'd use filters like::
-    for contact in ws[content.Contact]:
+    for contact in ws.where(content.Contact):
         print contact.fullName
 to iterate over a class extent.  This brings me to the question of the query
 interface.  There are several basic operations that need to be supported:
-    I'm thinking that this can be represented via subscripting, e.g.
-    ``someSet[criterion]``.  The criterion could be a type, a callable, a
-    filter object, or a string from which a filter definition can be parsed.
+    Filters can be invoked with the ``where()`` method on a set, or using
+    intersection (``&`` operator).  The criterion could be a type, a callable,
+    a filter object, or a string from which a filter definition can be parsed.
     For now, though, I'm punting on the question of what filter objects look
     like or what the string syntax is.  We'll just assume that filter objects
     will also be callable, so the only special cases are types and strings.
@@ -497,7 +497,7 @@
     a mapping-like object taking the same number of keys.  So if you did
     something like::
-        byName = ws[Contact].groupedBy(
+        byName = ws.where(Contact).groupedBy(
             Contact.lastName.of, Contact.firstName.of
@@ -552,6 +552,13 @@
     should probably be punted on for now, as there are no specific requirements
+    There ought to be a way for filters to subscribe to items they're applied
+    to, so that they can inform the intersection when the corresponding object
+    changes.  This needs to work even in the case of a filter that's a union
+    or a negation.  And, it needs some thought in order to avoid over-creating
+    event subscriptions (at least for the in-memory implementation).

Index: internal/Spike/src/spike/models.py
diff -u internal/Spike/src/spike/models.py:1.4 internal/Spike/src/spike/models.py:1.5
--- internal/Spike/src/spike/models.py:1.4	Mon Feb 21 17:50:24 2005
+++ internal/Spike/src/spike/models.py	Fri Mar 11 16:32:48 2005
@@ -1,11 +1,12 @@
 """Observable data structures -- see ``models.txt`` for docs"""
 from events import Event
+from query import AbstractFilter
 __all__ = ['Set', 'SetChanged', 'Validation']
-class Set(object):
+class Set(AbstractFilter):
     """Observable collection of unordered unique objects"""
     __slots__ = 'data','type','__weakref__'
@@ -110,6 +111,9 @@
     def __iter__(self):
         return iter(self.data)
+    def __contains__(self,ob):
+        return ob in self.data
 class SetChanged(Event):
     """Set has changed membership"""

Index: internal/Spike/src/spike/tests/__init__.py
diff -u internal/Spike/src/spike/tests/__init__.py:1.8 internal/Spike/src/spike/tests/__init__.py:1.9
--- internal/Spike/src/spike/tests/__init__.py:1.8	Fri Feb 25 19:50:24 2005
+++ internal/Spike/src/spike/tests/__init__.py	Fri Mar 11 16:32:48 2005
@@ -16,6 +16,8 @@
 test_codegen = make_docsuite('codegen.txt')
 test_uuidgen = make_docsuite('uuidgen.txt')
 def all():
     # Return all tests
     return TestSuite( [suite(), slow()] )
@@ -31,7 +33,8 @@
     # Return all dependency-free unit tests
     return TestSuite(
         [test_uuidgen(), test_events(), test_models(), test_schema(),
-            defaultTestLoader.loadTestsFromNames(['pim.tests.suite'])
+            defaultTestLoader.loadTestsFromNames(
+                ['pim.tests.suite', 'spike.tests.test_query'])

Index: internal/Spike/src/spike/schema.py
diff -u internal/Spike/src/spike/schema.py:1.7 internal/Spike/src/spike/schema.py:1.8
--- internal/Spike/src/spike/schema.py:1.7	Fri Feb 25 17:51:45 2005
+++ internal/Spike/src/spike/schema.py	Fri Mar 11 16:32:48 2005
@@ -3,12 +3,13 @@
 __all__ = [
    'Entity', 'Role', 'Relationship', 'One', 'Many', 'NullSet', 'LoadEvent',
    'Enumeration', 'Activator', 'RelationshipClass', 'EnumerationClass',
-   'ActiveDescriptor',
+   'ActiveDescriptor', 'EntityClass',
 from models import Set
 from events import Event
 from types import ClassType
+from query import AbstractFilter
 ClassTypes = type, ClassType
 NullSet = Set(type=())
@@ -403,10 +404,17 @@
+class EntityClass(Activator,AbstractFilter):
+    """Filter support, so that entity classes can be used as query filters"""
+    def __contains__(cls,ob):
+        return isinstance(ob,cls)
 class Entity(object):
     """Required base class for all entity types"""
-    __metaclass__ = Activator
+    __metaclass__ = EntityClass
     def __init__(self,**kw):
         for k,v in kw.items():

More information about the Commits mailing list