Commit 58f5e30e authored by Tres Seaver's avatar Tres Seaver

Remove all use of ``zope.app.pagetemplate`` by cloning / simplifying code.

o Added tests for previously-untested clients.
parent 97dce912
......@@ -18,10 +18,10 @@ Zope2 depends on the following zope.app packages directly:
- [_] 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
......
......@@ -11,6 +11,9 @@ Trunk (unreleased)
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``).
......
......@@ -22,15 +22,17 @@ import os
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.browser import IBrowserRequest
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
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,9 +407,24 @@ class ViewMixinForAttributes(BrowserView,
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''):
......
......@@ -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 @@ _engine = createTrustedZopeEngine()
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 @@ class ViewPageTemplateFile(viewpagetemplatefile.ViewPageTemplateFile):
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
......@@ -176,9 +176,6 @@ ViewPageTemplateFile's take arbitrary keyword arguments:
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>
......
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),
))
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),
))
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