Log message for revision 100383: Remove all use of ``zope.app.pagetemplate`` by cloning / simplifying code. o Added tests for previously-untested clients.
Changed: U Zope/trunk/ZOPE_APP_DEPENDENCIES.rst U Zope/trunk/doc/CHANGES.rst U Zope/trunk/src/Products/Five/browser/metaconfigure.py U Zope/trunk/src/Products/Five/browser/pagetemplatefile.py U Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt A Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py A Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py -=- Modified: Zope/trunk/ZOPE_APP_DEPENDENCIES.rst =================================================================== --- Zope/trunk/ZOPE_APP_DEPENDENCIES.rst 2009-05-26 00:06:45 UTC (rev 100382) +++ Zope/trunk/ZOPE_APP_DEPENDENCIES.rst 2009-05-26 01:32:41 UTC (rev 100383) @@ -18,10 +18,10 @@ - [_] zope.app.form o Products.Five.form.* -- [_] zope.app.pagetemplate - o Products.PageTemplates.Expressions - o Products.Five.browser.pagetemplatefile - o Products.Five.browser.metaconfigure +- [X] zope.app.pagetemplate + * Products.PageTemplates.Expressions + * Products.Five.browser.pagetemplatefile + * Products.Five.browser.metaconfigure - [_] zope.app.publication o ZPublisher.BaseRequest Modified: Zope/trunk/doc/CHANGES.rst =================================================================== --- Zope/trunk/doc/CHANGES.rst 2009-05-26 00:06:45 UTC (rev 100382) +++ Zope/trunk/doc/CHANGES.rst 2009-05-26 01:32:41 UTC (rev 100383) @@ -11,6 +11,9 @@ Restructuring +++++++++++++ +- Removed all use of ``zope.app.pagetemplate`` by cloning / simplifying + client code. + - Use ``zope.pagetemplate.engine`` instead of ``zope.app.pagetemplate.engine``. (update to versions 3.5.0 and 3.7.0, respectively, along with version 3.8.1 of ``zope.app.publisher``). Modified: Zope/trunk/src/Products/Five/browser/metaconfigure.py =================================================================== --- Zope/trunk/src/Products/Five/browser/metaconfigure.py 2009-05-26 00:06:45 UTC (rev 100382) +++ Zope/trunk/src/Products/Five/browser/metaconfigure.py 2009-05-26 01:32:41 UTC (rev 100383) @@ -22,15 +22,17 @@ from inspect import ismethod from zope import component +from zope.interface import implements from zope.interface import Interface from zope.component.zcml import handler from zope.component.interface import provideInterface from zope.configuration.exceptions import ConfigurationError +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.publisher.interfaces.browser import IBrowserPublisher from zope.publisher.interfaces.browser import IBrowserRequest -from zope.publisher.interfaces.browser import IDefaultBrowserLayer import zope.app.publisher.browser.viewmeta -import zope.app.pagetemplate.simpleviewclass from zope.app.publisher.browser.viewmeta import providesCallable from zope.app.publisher.browser.viewmeta import _handle_menu from zope.app.publisher.browser.viewmeta import _handle_for @@ -405,10 +407,25 @@ def __call__(self): return getattr(self, self.__page_attribute__) -class ViewMixinForTemplates(BrowserView, - zope.app.pagetemplate.simpleviewclass.simple): - pass +class ViewMixinForTemplates(BrowserView): + # Cloned from zope.app.pagetemplate.simpleviewclass.simple + implements(IBrowserPublisher) + def browserDefault(self, request): + return self, () + + def publishTraverse(self, request, name): + if name == 'index.html': + return self.index + + raise NotFound(self, name, request) + + def __getitem__(self, name): + return self.index.macros[name] + + def __call__(self, *args, **kw): + return self.index(*args, **kw) + def makeClassForTemplate(filename, globals=None, used_for=None, bases=(), cdict=None, name=u''): # XXX needs to deal with security from the bases? Modified: Zope/trunk/src/Products/Five/browser/pagetemplatefile.py =================================================================== --- Zope/trunk/src/Products/Five/browser/pagetemplatefile.py 2009-05-26 00:06:45 UTC (rev 100382) +++ Zope/trunk/src/Products/Five/browser/pagetemplatefile.py 2009-05-26 01:32:41 UTC (rev 100383) @@ -16,7 +16,9 @@ $Id$ """ from os.path import basename -from zope.app.pagetemplate import viewpagetemplatefile +from zope.component import getMultiAdapter +from zope.pagetemplate.pagetemplatefile import PageTemplateFile +from zope.pagetemplate.engine import TrustedAppPT from Acquisition import aq_get from AccessControl import getSecurityManager @@ -29,9 +31,14 @@ def getEngine(): return _engine -class ViewPageTemplateFile(viewpagetemplatefile.ViewPageTemplateFile): +class ViewPageTemplateFile(TrustedAppPT, PageTemplateFile): """Page Template used as class variable of views defined as Python classes. """ + def __init__(self, filename, _prefix=None, content_type=None): + _prefix = self.get_path_from_prefix(_prefix) + super(ViewPageTemplateFile, self).__init__(filename, _prefix) + if content_type is not None: + self.content_type = content_type def getId(self): return basename(self.filename) @@ -61,41 +68,69 @@ return getEngine() def pt_getContext(self, instance, request, **kw): - context = super(ViewPageTemplateFile, self).pt_getContext( - instance, request, **kw) + namespace = super(ViewPageTemplateFile, self).pt_getContext(**kw) + namespace['request'] = request + namespace['view'] = instance + namespace['context'] = context = instance.context + namespace['views'] = ViewMapper(context, request) # get the root - obj = context['context'] + obj = context root = None meth = aq_get(obj, 'getPhysicalRoot', None) if meth is not None: root = meth() - context.update(here=obj, - # philiKON thinks container should be the view, - # but BBB is more important than aesthetics. - container=obj, - root=root, - modules=SecureModuleImporter, - traverse_subpath=[], # BBB, never really worked - user = getSecurityManager().getUser() - ) - return context + namespace.update(here=obj, + # philiKON thinks container should be the view, + # but BBB is more important than aesthetics. + container=obj, + root=root, + modules=SecureModuleImporter, + traverse_subpath=[], # BBB, never really worked + user = getSecurityManager().getUser() + ) + return namespace def __get__(self, instance, type): return BoundPageTemplate(self, instance) +class ViewMapper(object): + def __init__(self, ob, request): + self.ob = ob + self.request = request + + def __getitem__(self, name): + return getMultiAdapter((self.ob, self.request), name=name) + # When a view's template is accessed e.g. as template.view, a # BoundPageTemplate object is retured. For BBB reasons, it needs to # support the aq_* methods and attributes known from Acquisition. For # that it also needs to be locatable thru __parent__. -class BoundPageTemplate(viewpagetemplatefile.BoundPageTemplate, - AcquisitionBBB): +class BoundPageTemplate(AcquisitionBBB): + def __init__(self, pt, ob): + object.__setattr__(self, 'im_func', pt) + object.__setattr__(self, 'im_self', ob) + macros = property(lambda self: self.im_func.macros) + filename = property(lambda self: self.im_func.filename) __parent__ = property(lambda self: self.im_self) + def __call__(self, *args, **kw): + if self.im_self is None: + im_self, args = args[0], args[1:] + else: + im_self = self.im_self + return self.im_func(im_self, *args, **kw) + def __setattr__(self, name, v): + raise AttributeError("Can't set attribute", name) + + def __repr__(self): + return "<BoundPageTemplateFile of %r>" % self.im_self + + # BBB ZopeTwoPageTemplateFile = ViewPageTemplateFile Modified: Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt =================================================================== --- Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt 2009-05-26 00:06:45 UTC (rev 100382) +++ Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt 2009-05-26 01:32:41 UTC (rev 100383) @@ -176,9 +176,6 @@ Passing in an argument called instance was supported by the old Five version of ViewPageTemplateFile, so we still need to support it. -In the zope.app.pagetemplate version, the first required argument is called -instance, though. - >>> print template(instance='allowed') <p>The falcon has taken flight</p> Added: Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py =================================================================== --- Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py (rev 0) +++ Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py 2009-05-26 01:32:41 UTC (rev 100383) @@ -0,0 +1,88 @@ +import unittest + +class ViewMixinForTemplatesTests(unittest.TestCase): + + def _getTargetClass(self): + from Products.Five.browser.metaconfigure import ViewMixinForTemplates + return ViewMixinForTemplates + + def _makeOne(self, context=None, request=None): + if context is None: + context = DummyContext() + if request is None: + request = DummyRequest() + return self._getTargetClass()(context, request) + + def test_class_conforms_to_IBrowserPublisher(self): + from zope.interface.verify import verifyClass + from zope.publisher.interfaces.browser import IBrowserPublisher + verifyClass(IBrowserPublisher, self._getTargetClass()) + + def test_browserDefault(self): + request = DummyRequest() + view = self._makeOne(request=request) + self.assertEqual(view.browserDefault(request), (view, ())) + + def test_publishTraverse_not_index_raises_NotFound(self): + from zope.publisher.interfaces import NotFound + request = DummyRequest() + view = self._makeOne(request=request) + self.assertRaises(NotFound, view.publishTraverse, request, 'nonesuch') + + def test_publishTraverse_w_index_returns_index(self): + request = DummyRequest() + view = self._makeOne(request=request) + index = view.index = DummyTemplate() + self.failUnless(view.publishTraverse(request, 'index.html') is index) + + def test___getitem___uses_index_macros(self): + view = self._makeOne() + view.index = index = DummyTemplate() + index.macros = {} + index.macros['aaa'] = aaa = object() + self.failUnless(view['aaa'] is aaa) + + def test___call___no_args_no_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view() + self.failUnless(result is index) + self.assertEqual(index._called_with, ((), {})) + + def test___call___w_args_no_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view('abc') + self.failUnless(result is index) + self.assertEqual(index._called_with, (('abc',), {})) + + def test___call___no_args_w_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view(foo='bar') + self.failUnless(result is index) + self.assertEqual(index._called_with, ((), {'foo': 'bar'})) + + def test___call___no_args_no_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view('abc', foo='bar') + self.failUnless(result is index) + self.assertEqual(index._called_with, (('abc',), {'foo': 'bar'})) + + +class DummyContext: + pass + +class DummyRequest: + pass + +class DummyTemplate: + def __call__(self, *args, **kw): + self._called_with = (args, kw) + return self + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(ViewMixinForTemplatesTests), + )) Added: Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py =================================================================== --- Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py (rev 0) +++ Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py 2009-05-26 01:32:41 UTC (rev 100383) @@ -0,0 +1,315 @@ +import unittest + +class ViewPageTemplateFileTests(unittest.TestCase): + + def setUp(self): + from AccessControl.SecurityManagement import noSecurityManager + noSecurityManager() + + def tearDown(self): + from AccessControl.SecurityManagement import noSecurityManager + noSecurityManager() + + def _getTargetClass(self): + from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile + return ViewPageTemplateFile + + def _makeOne(self, filename, _prefix=None, content_type=None): + return self._getTargetClass()(filename, _prefix, content_type) + + def _makeView(self, context=None, request=None): + if context is None: + context = DummyContext() + if request is None: + request = DummyRequest() + return DummyView(context, request) + + def test_getId_simple_name(self): + vptf = self._makeOne('seagull.pt') + self.assertEqual(vptf.getId(), 'seagull.pt') + self.assertEqual(vptf.id, 'seagull.pt') + + def test_getId_with_path(self): + vptf = self._makeOne('pages/dirpage1.pt') + self.assertEqual(vptf.id, 'dirpage1.pt') + + def test_pt_getEngine(self): + from zope.tales.expressions import DeferExpr + from zope.tales.expressions import NotExpr + from zope.tales.expressions import PathExpr + from zope.tales.expressions import StringExpr + from zope.tales.expressions import Undefs + from zope.tales.pythonexpr import PythonExpr + from zope.contentprovider.tales import TALESProviderExpression + from Products.PageTemplates.DeferExpr import LazyExpr + from Products.PageTemplates.Expressions import ZopePathExpr + from Products.PageTemplates.Expressions import SecureModuleImporter + + vptf = self._makeOne('seagull.pt') + engine = vptf.pt_getEngine() + self.assertEqual(engine.types['standard'], ZopePathExpr) + self.assertEqual(engine.types['path'], ZopePathExpr) + self.assertEqual(engine.types['exists'], ZopePathExpr) + self.assertEqual(engine.types['nocall'], ZopePathExpr) + self.assertEqual(engine.types['string'], StringExpr) + self.assertEqual(engine.types['python'], PythonExpr) + self.assertEqual(engine.types['not'], NotExpr) + self.assertEqual(engine.types['defer'], DeferExpr) + self.assertEqual(engine.types['lazy'], LazyExpr) + self.assertEqual(engine.types['provider'], TALESProviderExpression) + self.assertEqual(engine.base_names['modules'], SecureModuleImporter) + + def test_pt_getContext_no_kw_no_physicalRoot(self): + from Products.Five.browser.pagetemplatefile import ViewMapper + from Products.PageTemplates.Expressions import SecureModuleImporter + from AccessControl.SecurityManagement import newSecurityManager + newSecurityManager(None, DummyUser('a_user')) + context = DummyContext() + request = DummyRequest() + view = self._makeView(context, request) + vptf = self._makeOne('seagull.pt') + namespace = vptf.pt_getContext(view, request) + self.failUnless(namespace['context'] is context) + self.failUnless(namespace['request'] is request) + views = namespace['views'] + self.failUnless(isinstance(views, ViewMapper)) + self.assertEqual(views.ob, context) + self.assertEqual(views.request, request) + self.failUnless(namespace['here'] is context) + self.failUnless(namespace['container'] is context) + self.failUnless(namespace['root'] is None) + modules = namespace['modules'] + self.failUnless(modules is SecureModuleImporter) + self.assertEqual(namespace['traverse_subpath'], []) + self.assertEqual(namespace['user'].getId(), 'a_user') + + def test_pt_getContext_w_physicalRoot(self): + from Products.Five.browser.pagetemplatefile import ViewMapper + from Products.PageTemplates.Expressions import SecureModuleImporter + from AccessControl.SecurityManagement import newSecurityManager + newSecurityManager(None, DummyUser('a_user')) + context = DummyContext() + root = DummyContext() + context.getPhysicalRoot = lambda: root + request = DummyRequest() + view = self._makeView(context, request) + vptf = self._makeOne('seagull.pt') + namespace = vptf.pt_getContext(view, request) + self.failUnless(namespace['root'] is root) + + def test_pt_getContext_w_ignored_kw(self): + from Products.Five.browser.pagetemplatefile import ViewMapper + from Products.PageTemplates.Expressions import SecureModuleImporter + from AccessControl.SecurityManagement import newSecurityManager + newSecurityManager(None, DummyUser('a_user')) + context = DummyContext() + request = DummyRequest() + view = self._makeView(context, request) + vptf = self._makeOne('seagull.pt') + namespace = vptf.pt_getContext(view, request, foo='bar') + self.failIf('foo' in namespace) + self.failIf('foo' in namespace['options']) + + def test_pt_getContext_w_args_kw(self): + from Products.Five.browser.pagetemplatefile import ViewMapper + from Products.PageTemplates.Expressions import SecureModuleImporter + from AccessControl.SecurityManagement import newSecurityManager + newSecurityManager(None, DummyUser('a_user')) + context = DummyContext() + request = DummyRequest() + view = self._makeView(context, request) + vptf = self._makeOne('seagull.pt') + namespace = vptf.pt_getContext(view, request, args=('bar', 'baz')) + self.assertEqual(namespace['args'], ('bar', 'baz')) + + def test_pt_getContext_w_options_kw(self): + from Products.Five.browser.pagetemplatefile import ViewMapper + from Products.PageTemplates.Expressions import SecureModuleImporter + from AccessControl.SecurityManagement import newSecurityManager + newSecurityManager(None, DummyUser('a_user')) + context = DummyContext() + request = DummyRequest() + view = self._makeView(context, request) + vptf = self._makeOne('seagull.pt') + namespace = vptf.pt_getContext(view, request, options={'bar': 'baz'}) + self.assertEqual(namespace['options'], {'bar': 'baz'}) + + def test___call___no_previous_content_type(self): + context = DummyContext() + request = DummyRequest() + response = request.response = DummyResponse() + view = self._makeView(context, request) + vptf = self._makeOne('pages/dirpage1.pt') + body = vptf(view) + self.assertEqual(body, DIRPAGE1) + self.assertEqual(response._headers['Content-Type'], 'text/html') + + def test___call___w_previous_content_type(self): + context = DummyContext() + request = DummyRequest() + response = request.response = DummyResponse( + {'Content-Type': 'text/xhtml'}) + view = self._makeView(context, request) + vptf = self._makeOne('pages/dirpage1.pt') + body = vptf(view) + self.assertEqual(response._headers['Content-Type'], 'text/xhtml') + + def test___get___(self): + from Products.Five.browser.pagetemplatefile import BoundPageTemplate + template = self._makeOne('pages/dirpage1.pt') + class Foo: + def __init__(self, context, request): + self.context = context + self.request = request + bar = template + context = DummyContext() + request = DummyRequest() + foo = Foo(context, request) + bound = foo.bar + self.failUnless(isinstance(bound, BoundPageTemplate)) + self.failUnless(bound.im_func is template) + self.failUnless(bound.im_self is foo) + +class ViewMapperTests(unittest.TestCase): + + def setUp(self): + from zope.component.testing import setUp + setUp() + + def tearDown(self): + from zope.component.testing import tearDown + tearDown() + + def _getTargetClass(self): + from Products.Five.browser.pagetemplatefile import ViewMapper + return ViewMapper + + def _makeOne(self, ob=None, request=None): + if ob is None: + ob = DummyContext() + if request is None: + request = DummyRequest() + return self._getTargetClass()(ob, request) + + def test___getitem___miss(self): + from zope.component import ComponentLookupError + mapper = self._makeOne() + self.assertRaises(ComponentLookupError, mapper.__getitem__, 'nonesuch') + + def test___getitem___hit(self): + from zope.interface import Interface + from zope.component import provideAdapter + def _adapt(context, request): + return self + provideAdapter(_adapt, (None, None), Interface, name='test') + mapper = self._makeOne() + self.failUnless(mapper['test'] is self) + +_marker = object() + +class BoundPageTemplateTests(unittest.TestCase): + + def _getTargetClass(self): + from Products.Five.browser.pagetemplatefile import BoundPageTemplate + return BoundPageTemplate + + def _makeOne(self, pt=_marker, ob=_marker): + if pt is _marker: + pt = DummyTemplate() + if ob is _marker: + ob = DummyContext() + return self._getTargetClass()(pt, ob) + + def test___init__(self): + pt = DummyTemplate({'foo': 'bar'}) + ob = DummyContext() + bpt = self._makeOne(pt, ob) + self.failUnless(bpt.im_func is pt) + self.failUnless(bpt.im_self is ob) + self.failUnless(bpt.__parent__ is ob) + self.assertEqual(bpt.macros['foo'], 'bar') + self.assertEqual(bpt.filename, 'dummy.pt') + + def test___setattr___raises(self): + bpt = self._makeOne() + try: + bpt.foo = 'bar' + except AttributeError: + pass + else: + self.fail('Attribute assigned') + + def test___call___w_real_im_self_no_args_no_kw(self): + pt = DummyTemplate() + ob = DummyContext() + bpt = self._makeOne(pt, ob) + rendered = bpt() + self.assertEqual(rendered, '<h1>Dummy</h1>') + self.assertEqual(pt._called_with, (ob, (), {})) + + def test___call___w_real_im_self_w_args_w_kw(self): + pt = DummyTemplate() + ob = DummyContext() + bpt = self._makeOne(pt, ob) + rendered = bpt('abc', foo='bar') + self.assertEqual(rendered, '<h1>Dummy</h1>') + self.assertEqual(pt._called_with, (ob, ('abc',), {'foo': 'bar'})) + + def test___call___wo_real_im_self_w_args_w_kw(self): + pt = DummyTemplate() + bpt = self._makeOne(pt, None) + rendered = bpt('abc', 'def', foo='bar') + self.assertEqual(rendered, '<h1>Dummy</h1>') + self.assertEqual(pt._called_with, ('abc', ('def',), {'foo': 'bar'})) + +DIRPAGE1 = """\ +<html> +<p>This is page 1</p> +</html> +""" + +class DummyContext: + pass + +class DummyRequest: + debug = object() + +class DummyResponse: + def __init__(self, headers=None): + if headers is None: + headers = {} + self._headers = headers + + def getHeader(self, name): + return self._headers.get(name) + + def setHeader(self, name, value): + self._headers[name] = value + +class DummyTemplate: + filename = 'dummy.pt' + def __init__(self, macros=None): + if macros is None: + macros = {} + self.macros = macros + def __call__(self, im_self, *args, **kw): + self._called_with = (im_self, args, kw) + return '<h1>Dummy</h1>' + +class DummyView: + def __init__(self, context, request): + self.context = context + self.request = request + +class DummyUser: + def __init__(self, name): + self._name = name + def getId(self): + return self._name + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(ViewPageTemplateFileTests), + unittest.makeSuite(ViewMapperTests), + unittest.makeSuite(BoundPageTemplateTests), + )) _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins