Bring back the ability to say some_iterator/first/bar and some_iterator/last/foo

by providing PathIterator which (every much like the old PathIterator.Iterator
class) handles its own traversal (only now we use the Zope 3 traversal hooks).
parent ad3114d3
...@@ -17,16 +17,18 @@ for Python expressions, string literals, and paths. ...@@ -17,16 +17,18 @@ for Python expressions, string literals, and paths.
$Id$ $Id$
""" """
from zope.interface import implements
from zope.tales.tales import ExpressionEngine, Context, Iterator from zope.tales.tales import ExpressionEngine, Context, Iterator
from zope.tales.expressions import PathExpr, StringExpr, NotExpr from zope.tales.expressions import PathExpr, StringExpr, NotExpr
from zope.tales.expressions import DeferExpr, SubPathExpr from zope.tales.expressions import DeferExpr, SubPathExpr
from zope.tales.expressions import SimpleModuleImporter from zope.tales.expressions import SimpleModuleImporter
from zope.tales.pythonexpr import PythonExpr from zope.tales.pythonexpr import PythonExpr
from zope.traversing.interfaces import ITraversable
from zope.traversing.adapters import traversePathElement from zope.traversing.adapters import traversePathElement
from zope.contentprovider.tales import TALESProviderExpression from zope.contentprovider.tales import TALESProviderExpression
import OFS.interfaces
from zExceptions import NotFound, Unauthorized from zExceptions import NotFound, Unauthorized
from OFS.interfaces import ITraversable
from Products.PageTemplates import ZRPythonExpr from Products.PageTemplates import ZRPythonExpr
from Products.PageTemplates.DeferExpr import LazyExpr from Products.PageTemplates.DeferExpr import LazyExpr
from Products.PageTemplates.GlobalTranslationService import getGlobalTranslationService from Products.PageTemplates.GlobalTranslationService import getGlobalTranslationService
...@@ -52,7 +54,7 @@ def boboTraverseAwareSimpleTraverse(object, path_items, econtext): ...@@ -52,7 +54,7 @@ def boboTraverseAwareSimpleTraverse(object, path_items, econtext):
while path_items: while path_items:
name = path_items.pop() name = path_items.pop()
if ITraversable.providedBy(object): if OFS.interfaces.ITraversable.providedBy(object):
try: try:
object = object.restrictedTraverse(name) object = object.restrictedTraverse(name)
except (NotFound, Unauthorized), e: except (NotFound, Unauthorized), e:
...@@ -82,8 +84,7 @@ class ZopeContext(Context): ...@@ -82,8 +84,7 @@ class ZopeContext(Context):
context = self.contexts.get('context') context = self.contexts.get('context')
return getGlobalTranslationService().translate( return getGlobalTranslationService().translate(
domain, msgid, mapping=mapping, domain, msgid, mapping=mapping,
context=context, context=context, default=default)
default=default)
class ZopeEngine(ExpressionEngine): class ZopeEngine(ExpressionEngine):
...@@ -97,8 +98,6 @@ class ZopeEngine(ExpressionEngine): ...@@ -97,8 +98,6 @@ class ZopeEngine(ExpressionEngine):
class ZopeIterator(Iterator): class ZopeIterator(Iterator):
__allow_access_to_unprotected_subobjects__ = True
# The things below used to be attributes in # The things below used to be attributes in
# ZTUtils.Iterator.Iterator, however in zope.tales.tales.Iterator # ZTUtils.Iterator.Iterator, however in zope.tales.tales.Iterator
# they're methods. We need BBB on the Python level so we redefine # they're methods. We need BBB on the Python level so we redefine
...@@ -122,27 +121,23 @@ class ZopeIterator(Iterator): ...@@ -122,27 +121,23 @@ class ZopeIterator(Iterator):
def item(self): def item(self):
return super(ZopeIterator, self).item() return super(ZopeIterator, self).item()
# The following things were in ZTUtils.Iterator.Iterator but # This method was on the old ZTUtils.Iterator.Iterator class but
# aren't anymore in zope.tales.tales.Iterator. For a good reason. # isn't part of the spec. We'll support it for a short
# They're just insane. # deprecation period.
# BBB 2005/05/01 -- to be removed after 12 months # BBB 2005/05/01 -- to be removed after 12 months
@property @property
@deprecate("The 'nextIndex' method has been deprecated and will disappear " @deprecate("The 'nextIndex' method has been deprecated and will disappear "
"in Zope 2.12. Use 'iterator.index+1' instead.") "in Zope 2.12. Use 'iterator.index+1' instead.")
def nextIndex(self): def nextIndex(self):
return self.index + 1 return self.index + 1
@deprecate("The 'first' method has been deprecated and will disappear " # 'first' and 'last' are Zope 2 enhancements to the TALES iterator
"in Zope 2.12. Use the 'start' property instead.") # spec. See help/tal-repeat.stx for more info
def first(self, name=None): def first(self, name=None):
if self.start: if self.start:
return True return True
return not self.same_part(name, self._last, self.item) return not self.same_part(name, self._last_item, self.item)
@deprecate("The 'last' method has been deprecated and will disappear "
"in Zope 2.12. Use the 'end' property instead.")
def last(self, name=None): def last(self, name=None):
if self.end: if self.end:
return True return True
...@@ -154,9 +149,47 @@ class ZopeIterator(Iterator): ...@@ -154,9 +149,47 @@ class ZopeIterator(Iterator):
no = object() no = object()
return getattr(ob1, name, no) == getattr(ob2, name, no) is not no return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
# 'first' needs to have access to the last item in the loop
def next(self):
if self._nextIndex > 0:
self._last_item = self.item
return super(ZopeIterator, self).next()
class PathIterator(ZopeIterator):
"""A TALES Iterator with the ability to use first() and last() on
subpaths of elements."""
# we want to control our own traversal so that we can deal with
# 'first' and 'last' when they appear in path expressions
implements(ITraversable)
def traverse(self, name, furtherPath):
if name in ('first', 'last'):
method = getattr(self, name)
# it's important that 'name' becomes a copy because we'll
# clear out 'furtherPath'
name = furtherPath[:]
if not name:
name = None
# make sure that traversal ends here with us
furtherPath[:] = []
return method(name)
return getattr(self, name)
def same_part(self, name, ob1, ob2):
if name is None:
return ob1 == ob2
if isinstance(name, basestring):
name = name.split('/')
try:
ob1 = boboTraverseAwareSimpleTraverse(ob1, name, None)
ob2 = boboTraverseAwareSimpleTraverse(ob2, name, None)
except LookupError:
return False
return ob1 == ob2
def createZopeEngine(): def createZopeEngine():
e = ZopeEngine() e = ZopeEngine()
e.iteratorFactory = ZopeIterator e.iteratorFactory = PathIterator
for pt in ZopePathExpr._default_type_names: for pt in ZopePathExpr._default_type_names:
e.registerType(pt, ZopePathExpr) e.registerType(pt, ZopePathExpr)
e.registerType('string', StringExpr) e.registerType('string', StringExpr)
......
...@@ -18,8 +18,8 @@ $Id$ ...@@ -18,8 +18,8 @@ $Id$
""" """
import zope.deferredimport import zope.deferredimport
zope.deferredimport.deprecated( zope.deferredimport.deprecated(
"It has been renamed to ZopeIterator and moved to the " "It has been renamed to PathIterator and moved to the "
"Products.PageTemplates.Expressions module. This reference will be " "Products.PageTemplates.Expressions module. This reference will be "
"gone in Zope 2.12.", "gone in Zope 2.12.",
PathIterator = "Products.PageTemplates.Expressions:ZopeIterator" PathIterator = "Products.PageTemplates.Expressions:PathIterator"
) )
...@@ -125,15 +125,6 @@ class HTMLTests(zope.component.testing.PlacelessSetup, unittest.TestCase): ...@@ -125,15 +125,6 @@ class HTMLTests(zope.component.testing.PlacelessSetup, unittest.TestCase):
self.assert_expected(self.folder.t, 'Loop1.html') self.assert_expected(self.folder.t, 'Loop1.html')
def checkFancyLoop(self): def checkFancyLoop(self):
# XXX This checks among others for a feature of the
# PathIterator which lets you do something like this in a path
# expr: iterator/last/bla. This would check whether the
# current item in the iteration was the last one with a
# particular 'bla' attribute value. I wonder whether anyone
# actually needs this? I vote for ripping it out. We can
# provide BBB for a certain deprecation period by enabling a
# specialized ITraversable adapter for ZopeIterator that works
# similar to the old PathIterator.
self.assert_expected(self.folder.t, 'Loop2.html') self.assert_expected(self.folder.t, 'Loop2.html')
def checkGlobalsShadowLocals(self): def checkGlobalsShadowLocals(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment