[Checkins] SVN: zc.relationship/branches/1.1/src/zc/relationship/ svn merge -r77396:77397 svn+ssh://svn.zope.org/repos/main/zc.relationship/trunk

Gary Poster gary at zope.com
Wed Jul 4 11:52:58 EDT 2007


Log message for revision 77399:
  svn merge -r77396:77397 svn+ssh://svn.zope.org/repos/main/zc.relationship/trunk

Changed:
  U   zc.relationship/branches/1.1/src/zc/relationship/container.txt
  U   zc.relationship/branches/1.1/src/zc/relationship/interfaces.py
  U   zc.relationship/branches/1.1/src/zc/relationship/shared.py

-=-
Modified: zc.relationship/branches/1.1/src/zc/relationship/container.txt
===================================================================
--- zc.relationship/branches/1.1/src/zc/relationship/container.txt	2007-07-04 15:34:36 UTC (rev 77398)
+++ zc.relationship/branches/1.1/src/zc/relationship/container.txt	2007-07-04 15:52:57 UTC (rev 77399)
@@ -792,12 +792,12 @@
 Even the relationship containers themselves can be nodes in a relationship
 container.
 
-	>>> container1 = app['container1'] = Container()
-	>>> container2 = app['container2'] = Container()
-	>>> rel = Relationship((container1,), (container2,))
-	>>> container.add(rel)
-	>>> container.isLinked(container1, container2)
-	True
+    >>> container1 = app['container1'] = Container()
+    >>> container2 = app['container2'] = Container()
+    >>> rel = Relationship((container1,), (container2,))
+    >>> container.add(rel)
+    >>> container.isLinked(container1, container2)
+    True
 
 Exposing Unresolved Tokens
 --------------------------
@@ -810,3 +810,256 @@
 The containers include three methods for these sorts of use cases:
 `findTargetTokens`, `findSourceTokens`, and `findRelationshipTokens`.  They
 take the same arguments as their similarly-named cousins.
+
+Convenience classes
+-------------------
+
+Three convenience classes exist for relationships with a single source and/or a
+single target only.
+
+One-To-One Relationship
+~~~~~~~~~~~~~~~~~~~~~~~
+
+A `OneToOneRelationship` relates a single source to a single target.
+
+    >>> from zc.relationship.shared import OneToOneRelationship
+    >>> rel = OneToOneRelationship(app['ob20'], app['ob21'])
+
+    >>> verifyObject(interfaces.IOneToOneRelationship, rel)
+    True
+
+All container methods work as for the general many-to-many relationship.  We
+repeat some of the tests defined in the main section above (all relationships
+defined there are actually one-to-one relationships).
+
+    >>> container.add(rel)
+    >>> container.add(OneToOneRelationship(app['ob21'], app['ob22']))
+    >>> container.add(OneToOneRelationship(app['ob21'], app['ob23']))
+    >>> container.add(OneToOneRelationship(app['ob20'], app['ob23']))
+    >>> container.add(OneToOneRelationship(app['ob20'], app['ob24']))
+    >>> container.add(OneToOneRelationship(app['ob22'], app['ob25']))
+    >>> rel = OneToOneRelationship(app['ob25'], app['ob21'])
+    >>> container.add(rel)
+
+`findTargets`
+
+    >>> sorted(o.id for o in container.findTargets(app['ob20'], 2))
+    ['ob21', 'ob22', 'ob23', 'ob24']
+
+`findSources`
+
+    >>> sorted(o.id for o in container.findSources(app['ob21'], 2))
+    ['ob20', 'ob22', 'ob25']
+
+`findRelationships`
+
+    >>> sorted(
+    ...     [repr(rel) for rel in path]
+    ...     for path in container.findRelationships(app['ob21'], maxDepth=2))
+    ...     # doctest: +NORMALIZE_WHITESPACE
+    [['<Relationship from (<Demo ob21>,) to (<Demo ob22>,)>'],
+     ['<Relationship from (<Demo ob21>,) to (<Demo ob22>,)>',
+      '<Relationship from (<Demo ob22>,) to (<Demo ob25>,)>'],
+     ['<Relationship from (<Demo ob21>,) to (<Demo ob23>,)>']]
+
+    >>> sorted(
+    ...     [repr(rel) for rel in path]
+    ...     for path in container.findRelationships(
+    ...         target=app['ob23'], maxDepth=2))
+    ...     # doctest: +NORMALIZE_WHITESPACE
+    [['<Relationship from (<Demo ob20>,) to (<Demo ob21>,)>',
+      '<Relationship from (<Demo ob21>,) to (<Demo ob23>,)>'],
+     ['<Relationship from (<Demo ob20>,) to (<Demo ob23>,)>'],
+     ['<Relationship from (<Demo ob21>,) to (<Demo ob23>,)>'],
+     ['<Relationship from (<Demo ob25>,) to (<Demo ob21>,)>',
+      '<Relationship from (<Demo ob21>,) to (<Demo ob23>,)>']]
+
+    >>> list(container.findRelationships(
+    ...      app['ob20'], app['ob25'], maxDepth=None))
+    ...     # doctest: +NORMALIZE_WHITESPACE
+    [(<Relationship from (<Demo ob20>,) to (<Demo ob21>,)>,
+      <Relationship from (<Demo ob21>,) to (<Demo ob22>,)>,
+      <Relationship from (<Demo ob22>,) to (<Demo ob25>,)>)]
+    
+    >>> list(
+    ...     [repr(rel) for rel in path]
+    ...     for path in container.findRelationships(
+    ...         app['ob20'], maxDepth=None)
+    ...         if interfaces.ICircularRelationshipPath.providedBy(path))
+    ...     # doctest: +NORMALIZE_WHITESPACE
+    [['<Relationship from (<Demo ob20>,) to (<Demo ob21>,)>',
+      '<Relationship from (<Demo ob21>,) to (<Demo ob22>,)>',
+      '<Relationship from (<Demo ob22>,) to (<Demo ob25>,)>',
+      '<Relationship from (<Demo ob25>,) to (<Demo ob21>,)>']]
+
+`isLinked`
+
+    >>> container.isLinked(source=app['ob20'])
+    True
+    >>> container.isLinked(target=app['ob24'])
+    True
+    >>> container.isLinked(source=app['ob24'])
+    False
+    >>> container.isLinked(target=app['ob20'])
+    False
+    >>> container.isLinked(app['ob20'], app['ob22'], maxDepth=2)
+    True
+    >>> container.isLinked(app['ob20'], app['ob25'], maxDepth=2)
+    False
+
+`remove`
+
+    >>> res = list(container.findTargets(app['ob22'], None)) # before removal
+    >>> res[:2]
+    [<Demo ob25>, <Demo ob21>]
+    >>> container.remove(rel)
+    >>> list(container.findTargets(app['ob22'], None)) # after removal
+    [<Demo ob25>]
+
+`reindex`
+
+    >>> rel = iter(container.findRelationships(
+    ...     app['ob21'], app['ob23'])).next()[0]
+
+    >>> rel.target
+    <Demo ob23>
+    >>> rel.target = app['ob24'] # this calls reindex
+    >>> rel.target
+    <Demo ob24>
+
+    >>> rel.source
+    <Demo ob21>
+    >>> rel.source = app['ob22'] # this calls reindex
+    >>> rel.source
+    <Demo ob22>
+
+ManyToOneRelationship
+~~~~~~~~~~~~~~~~~~~~~
+
+A `ManyToOneRelationship` relates multiple sources to a single target.
+
+    >>> from zc.relationship.shared import ManyToOneRelationship
+    >>> rel = ManyToOneRelationship((app['ob22'], app['ob26']), app['ob24'])
+
+    >>> verifyObject(interfaces.IManyToOneRelationship, rel)
+    True
+
+    >>> container.add(rel)
+    >>> container.add(ManyToOneRelationship(
+    ...     (app['ob26'], app['ob23']),
+    ...     app['ob20']))
+
+The relationship diagram now looks like this::
+
+        ob20              (ob22, obj26)       (ob26, obj23)
+        |   |\                  |                   |
+      ob21  | |               obj24               obj20
+      |     | |
+    ob22    | ob23
+      |  \  |
+    ob25  ob24
+
+We created a cycle for obj20 via obj23.
+
+    >>> sorted(o.id for o in container.findSources(app['ob24'], None))
+    ['ob20', 'ob21', 'ob22', 'ob23', 'ob26']
+
+    >>> sorted(o.id for o in container.findSources(app['ob20'], None))
+    ['ob20', 'ob23', 'ob26']
+
+    >>> list(container.findRelationships(app['ob20'], app['ob20'], None))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    [cycle(<Relationship from (<Demo ob20>,) to (<Demo ob23>,)>,
+           <Relationship from (<Demo ob26>, <Demo ob23>) to (<Demo ob20>,)>)]
+    >>> list(container.findRelationships(
+    ...     app['ob20'], app['ob20'], 2))[0].cycled
+    [{'source': <Demo ob20>}]
+
+The `ManyToOneRelationship`'s `sources` attribute is mutable, while it's
+`targets` attribute is immutable.
+
+    >>> rel.sources
+    (<Demo ob22>, <Demo ob26>)
+    >>> rel.sources = [app['ob26'], app['ob24']]
+
+    >>> rel.targets
+    (<Demo ob24>,)
+    >>> rel.targets = (app['ob22'],)
+    Traceback (most recent call last):
+    ...
+    AttributeError: can't set attribute
+
+But the relationship has an additional mutable `target` attribute.
+
+    >>> rel.target
+    <Demo ob24>
+    >>> rel.target = app['ob22']
+
+OneToManyRelationship
+~~~~~~~~~~~~~~~~~~~~~
+
+A `OneToManyRelationship` relates a single source to multiple targets.
+
+    >>> from zc.relationship.shared import OneToManyRelationship
+    >>> rel = OneToManyRelationship(app['ob22'], (app['ob20'], app['ob27']))
+
+    >>> verifyObject(interfaces.IOneToManyRelationship, rel)
+    True
+
+    >>> container.add(rel)
+    >>> container.add(OneToManyRelationship(
+    ...     app['ob20'],
+    ...     (app['ob23'], app['ob28'])))
+
+The updated diagram looks like this::
+
+        ob20              (ob26, obj24)       (ob26, obj23)
+        |   |\                  |                   |
+      ob21  | |               obj22               obj20
+      |     | |                 |                   |
+    ob22    | ob23        (ob20, obj27)       (ob23, obj28)
+      |  \  |
+    ob25  ob24
+
+Alltogether there are now three cycles for ob22.
+
+    >>> sorted(o.id for o in container.findTargets(app['ob22']))
+    ['ob20', 'ob24', 'ob25', 'ob27']
+    >>> sorted(o.id for o in container.findTargets(app['ob22'], None))
+    ['ob20', 'ob21', 'ob22', 'ob23', 'ob24', 'ob25', 'ob27', 'ob28']
+
+    >>> sorted(o.id for o in container.findTargets(app['ob20']))
+    ['ob21', 'ob23', 'ob24', 'ob28']
+    >>> sorted(o.id for o in container.findTargets(app['ob20'], None))
+    ['ob20', 'ob21', 'ob22', 'ob23', 'ob24', 'ob25', 'ob27', 'ob28']
+
+    >>> sorted(container.findRelationships(app['ob22'], app['ob22'], None))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    [cycle(<Relationship from (<Demo ob22>,) to (<Demo ob20>, <Demo ob27>)>,
+           <Relationship from (<Demo ob20>,) to (<Demo ob21>,)>,
+           <Relationship from (<Demo ob21>,) to (<Demo ob22>,)>),
+     cycle(<Relationship from (<Demo ob22>,) to (<Demo ob20>, <Demo ob27>)>,
+           <Relationship from (<Demo ob20>,) to (<Demo ob24>,)>,
+           <Relationship from (<Demo ob26>, <Demo ob24>) to (<Demo ob22>,)>),
+     cycle(<Relationship from (<Demo ob22>,) to (<Demo ob24>,)>,
+           <Relationship from (<Demo ob26>, <Demo ob24>) to (<Demo ob22>,)>)]
+
+The `OneToManyRelationship`'s `targets` attribute is mutable, while it's
+`sources` attribute is immutable.
+
+    >>> rel.targets
+    (<Demo ob20>, <Demo ob27>)
+    >>> rel.targets = [app['ob28'], app['ob21']]
+
+    >>> rel.sources
+    (<Demo ob22>,)
+    >>> rel.sources = (app['ob23'],)
+    Traceback (most recent call last):
+    ...
+    AttributeError: can't set attribute
+
+But the relationship has an additional mutable `source` attribute.
+
+    >>> rel.source
+    <Demo ob22>
+    >>> rel.target = app['ob23']

Modified: zc.relationship/branches/1.1/src/zc/relationship/interfaces.py
===================================================================
--- zc.relationship/branches/1.1/src/zc/relationship/interfaces.py	2007-07-04 15:34:36 UTC (rev 77398)
+++ zc.relationship/branches/1.1/src/zc/relationship/interfaces.py	2007-07-04 15:52:57 UTC (rev 77399)
@@ -169,15 +169,15 @@
 class IMutableRelationship(IRelationship):
     """An asymmetric relationship.  Sources and targets can be changed."""
 
-class ISourceRelationship(IRelationship):
+class ISourceRelationship(IMutableRelationship):
 
     source = interface.Attribute(
         """the source for this object.  Mutable""")
 
-class ITargetRelationship(IRelationship):
+class ITargetRelationship(IMutableRelationship):
 
-    source = interface.Attribute(
-        """the source for this object.  Mutable""")
+    target = interface.Attribute(
+        """the target for this object.  Mutable""")
 
 class IOneToOneRelationship(ISourceRelationship, ITargetRelationship):
     pass

Modified: zc.relationship/branches/1.1/src/zc/relationship/shared.py
===================================================================
--- zc.relationship/branches/1.1/src/zc/relationship/shared.py	2007-07-04 15:34:36 UTC (rev 77398)
+++ zc.relationship/branches/1.1/src/zc/relationship/shared.py	2007-07-04 15:52:57 UTC (rev 77399)
@@ -149,7 +149,7 @@
     interface.implements(interfaces.IManyToOneRelationship)
 
     def __init__(self, sources, target):
-        super(OneToManyRelationship, self).__init__(sources, (target,))
+        super(ManyToOneRelationship, self).__init__(sources, (target,))
 
     @apply
     def sources():
@@ -253,10 +253,14 @@
                  filter=None):
         tokenize = self.relationIndex.tokenizeQuery
         if source is not None:
+            if target is not None:
+                targetQuery = tokenize({'target': target})
+            else:
+                targetQuery = None
             return self.relationIndex.isLinked(
                 tokenize({'source': source}),
                 maxDepth, filter and ResolvingFilter(filter, self),
-                target is not None and tokenize({'target': target}) or None,
+                targetQuery,
                 targetFilter=minDepthFilter(minDepth))
         elif target is not None:
             return self.relationIndex.isLinked(
@@ -289,10 +293,14 @@
                                minDepth=None, filter=None):
         tokenize = self.relationIndex.tokenizeQuery
         if source is not None:
+            if target is not None:
+                targetQuery = tokenize({'target': target})
+            else:
+                targetQuery = None
             res = self.relationIndex.findRelationshipTokenChains(
                 tokenize({'source': source}),
                 maxDepth, filter and ResolvingFilter(filter, self),
-                target and tokenize({'target': target}),
+                targetQuery,
                 targetFilter=minDepthFilter(minDepth))
             return self._forward(res)
         elif target is not None:



More information about the Checkins mailing list