"Fossies" - the Fresh Open Source Software Archive

Member "Zope-5.2.1/src/Products/PageTemplates/tests/testExpressions.py" (8 Jun 2021, 13176 Bytes) of package /linux/www/Zope-5.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "testExpressions.py": 5.2_vs_5.2.1.

    1 import unittest
    2 import warnings
    3 
    4 from AccessControl import safe_builtins
    5 from zExceptions import NotFound
    6 from zope.component.testing import PlacelessSetup
    7 from zope.location.interfaces import LocationError
    8 
    9 
   10 class EngineTestsBase(PlacelessSetup):
   11 
   12     def setUp(self):
   13         from zope.component import provideAdapter
   14         from zope.traversing.adapters import DefaultTraversable
   15         PlacelessSetup.setUp(self)
   16         provideAdapter(DefaultTraversable, (None,))
   17 
   18     def tearDown(self):
   19         PlacelessSetup.tearDown(self)
   20 
   21     def _makeEngine(self):
   22         # subclasses must override
   23         raise NotImplementedError
   24 
   25     def _makeContext(self, bindings=None):
   26 
   27         class Dummy:
   28             __allow_access_to_unprotected_subobjects__ = 1
   29 
   30             def __call__(self):
   31                 return 'dummy'
   32 
   33         class DummyDocumentTemplate:
   34             __allow_access_to_unprotected_subobjects__ = 1
   35             isDocTemp = True
   36 
   37             def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
   38                 return 'dummy'
   39 
   40             def absolute_url(self, relative=0):
   41                 url = 'dummy'
   42                 if not relative:
   43                     url = "http://server/" + url
   44                 return url
   45 
   46         _DEFAULT_BINDINGS = dict(
   47             one=1,
   48             d={'one': 1, 'b': 'b', '': 'blank', '_': 'under'},
   49             blank='',
   50             dummy=Dummy(),
   51             dummy2=DummyDocumentTemplate(),
   52             eightbit=b'\xe4\xfc\xf6',
   53             # ZopeContext needs 'context' and 'template' keys for unicode
   54             # conflict resolution
   55             context=Dummy(),
   56             template=DummyDocumentTemplate(),
   57         )
   58 
   59         if bindings is None:
   60             bindings = _DEFAULT_BINDINGS
   61         return self._makeEngine().getContext(bindings)
   62 
   63     def test_compile(self):
   64         # Test expression compilation
   65         e = self._makeEngine()
   66         for p in ('x', 'x/y', 'x/y/z'):
   67             e.compile(p)
   68         e.compile('path:a|b|c/d/e')
   69         e.compile('string:Fred')
   70         e.compile('string:A$B')
   71         e.compile('string:a ${x/y} b ${y/z} c')
   72         e.compile('python: 2 + 2')
   73         e.compile('python: 2 \n+\n 2\n')
   74 
   75     def test_evaluate_simple_path_binding(self):
   76         ec = self._makeContext()
   77         self.assertEqual(ec.evaluate('one'), 1)
   78 
   79     def test_evaluate_simple_path_dict_key_int_value(self):
   80         ec = self._makeContext()
   81         self.assertEqual(ec.evaluate('d/one'), 1)
   82 
   83     def test_evaluate_simple_path_dict_key_string_value(self):
   84         ec = self._makeContext()
   85         self.assertEqual(ec.evaluate('d/b'), 'b')
   86 
   87     def test_evaluate_with_render_simple_callable(self):
   88         ec = self._makeContext()
   89         self.assertEqual(ec.evaluate('dummy'), 'dummy')
   90 
   91     def test_evaluate_with_unimplemented_call(self):
   92         class Dummy:
   93             def __call__(self):
   94                 raise NotImplementedError()
   95 
   96         dummy = Dummy()
   97         ec = self._makeContext(bindings={'dummy': dummy})
   98         self.assertIs(ec.evaluate('dummy'), dummy)
   99 
  100     def test_evaluate_with_render_DTML_template(self):
  101         # http://www.zope.org/Collectors/Zope/2232
  102         # DTML templates could not be called from a Page Template
  103         # due to an ImportError
  104         ec = self._makeContext()
  105         self.assertEqual(ec.evaluate('dummy2'), 'dummy')
  106 
  107     def test_evaluate_alternative_first_missing(self):
  108         ec = self._makeContext()
  109         self.assertTrue(ec.evaluate('x | nothing') is None)
  110 
  111     def test_evaluate_dict_key_as_underscore(self):
  112         # Traversing to the name `_` will raise a DeprecationWarning
  113         # because it will go away in Zope 6.
  114         ec = self._makeContext()
  115         with warnings.catch_warnings():
  116             warnings.simplefilter('ignore')
  117             self.assertEqual(ec.evaluate('d/_'), 'under')
  118 
  119     def test_evaluate_dict_with_key_from_expansion(self):
  120         ec = self._makeContext()
  121         self.assertEqual(ec.evaluate('d/?blank'), 'blank')
  122 
  123     def test_hybrid_with_python_expression_int_value(self):
  124         ec = self._makeContext()
  125         self.assertEqual(ec.evaluate('x | python:1+1'), 2)
  126 
  127     def test_hybrid_with_python_expression_type_value_not_called(self):
  128         ec = self._makeContext()
  129         self.assertEqual(ec.evaluate('x | python:int'), int)
  130 
  131     def test_hybrid_with_string_expression(self):
  132         ec = self._makeContext()
  133         self.assertEqual(ec.evaluate('x | string:x'), 'x')
  134 
  135     def test_hybrid_with_string_expression_and_expansion(self):
  136         ec = self._makeContext()
  137         self.assertEqual(ec.evaluate('x | string:$one'), '1')
  138 
  139     def test_hybrid_with_compound_expression_int_value(self):
  140         ec = self._makeContext()
  141         self.assertTrue(ec.evaluate('x | not:exists:x'))
  142 
  143     def test_access_iterator_from_python_expression(self):
  144         ec = self._makeContext()
  145         ec.beginScope()
  146         ec.setRepeat('loop', "python:[1,2,3]")
  147         self.assertTrue(ec.evaluate("python:repeat['loop'].odd()"))
  148         ec.endScope()
  149 
  150     def test_defer_expression_returns_wrapper(self):
  151         from zope.tales.expressions import DeferWrapper
  152         ec = self._makeContext()
  153         defer = ec.evaluate('defer: b')
  154         self.assertIsInstance(defer, DeferWrapper)
  155 
  156     def test_lazy_expression_returns_wrapper(self):
  157         from zope.tales.expressions import LazyWrapper
  158         ec = self._makeContext()
  159         lazy = ec.evaluate('lazy: b')
  160         self.assertIsInstance(lazy, LazyWrapper)
  161 
  162     def test_empty_path_expression_explicit(self):
  163         ec = self._makeContext()
  164         self.assertEqual(ec.evaluate('path:'), None)
  165 
  166     def test_empty_path_expression_explicit_with_trailing_whitespace(self):
  167         ec = self._makeContext()
  168         self.assertEqual(ec.evaluate('path:  '), None)
  169 
  170     def test_empty_path_expression_implicit(self):
  171         ec = self._makeContext()
  172         self.assertEqual(ec.evaluate(''), None)
  173 
  174     def test_empty_path_expression_implicit_with_trailing_whitespace(self):
  175         ec = self._makeContext()
  176         self.assertEqual(ec.evaluate('  \n'), None)
  177 
  178     def test_unicode(self):
  179         # All our string expressions are unicode now
  180         eng = self._makeEngine()
  181         ec = self._makeContext()
  182         # XXX: can't do ec.evaluate(u'string:x') directly because ZopeContext
  183         # only bothers compiling true strings, not unicode strings
  184         result = ec.evaluate(eng.compile('string:x'))
  185         self.assertEqual(result, 'x')
  186         self.assertIsInstance(result, str)
  187 
  188     def test_mixed(self):
  189         # 8-bit strings in unicode string expressions cause UnicodeDecodeErrors
  190         eng = self._makeEngine()
  191         ec = self._makeContext()
  192         expr = eng.compile('string:$eightbit')
  193         self.assertRaises(UnicodeDecodeError,
  194                           ec.evaluate, expr)
  195         # But registering an appropriate IUnicodeEncodingConflictResolver
  196         # should fix it
  197         from Products.PageTemplates.interfaces import \
  198             IUnicodeEncodingConflictResolver
  199         from Products.PageTemplates.unicodeconflictresolver import \
  200             StrictUnicodeEncodingConflictResolver
  201         from zope.component import provideUtility
  202         provideUtility(StrictUnicodeEncodingConflictResolver,
  203                        IUnicodeEncodingConflictResolver)
  204         self.assertEqual(ec.evaluate(expr), 'äüö')
  205 
  206     def test_builtin_in_path_expr(self):
  207         ec = self._makeContext()
  208         self.assertIs(ec.evaluate('True'), True)
  209         self.assertIs(ec.evaluate('False'), False)
  210         self.assertIs(ec.evaluate('nocall: test'), safe_builtins["test"])
  211 
  212 
  213 class UntrustedEngineTests(EngineTestsBase, unittest.TestCase):
  214 
  215     def _makeEngine(self):
  216         from Products.PageTemplates.Expressions import createZopeEngine
  217         return createZopeEngine()
  218 
  219     # XXX:  add tests that show security checks being enforced
  220 
  221     def test_open_in_path_expr(self):
  222         ec = self._makeContext()
  223         with self.assertRaises(KeyError):
  224             ec.evaluate("nocall:open")
  225 
  226     def test_list_in_path_expr(self):
  227         ec = self._makeContext()
  228         self.assertIs(ec.evaluate('nocall: list'), safe_builtins["list"])
  229 
  230     def test_underscore_traversal(self):
  231         # Prevent traversal to names starting with an underscore (_)
  232         ec = self._makeContext()
  233 
  234         with self.assertRaises(NotFound):
  235             ec.evaluate("context/__class__")
  236 
  237         with self.assertRaises((NotFound, LocationError)):
  238             ec.evaluate("nocall: random/_itertools/repeat")
  239 
  240         with self.assertRaises((NotFound, LocationError)):
  241             ec.evaluate("random/_itertools/repeat/foobar")
  242 
  243 
  244 class TrustedEngineTests(EngineTestsBase, unittest.TestCase):
  245 
  246     def _makeEngine(self):
  247         from Products.PageTemplates.Expressions import createTrustedZopeEngine
  248         return createTrustedZopeEngine()
  249 
  250     # XXX:  add tests that show security checks *not* being enforced
  251 
  252     def test_open_in_path_expr(self):
  253         ec = self._makeContext()
  254         self.assertIs(ec.evaluate("nocall:open"), open)
  255 
  256     def test_list_in_path_expr(self):
  257         ec = self._makeContext()
  258         self.assertIs(ec.evaluate('nocall: list'), list)
  259 
  260 
  261 class UnicodeEncodingConflictResolverTests(PlacelessSetup, unittest.TestCase):
  262 
  263     def testDefaultResolver(self):
  264         from Products.PageTemplates.interfaces import \
  265             IUnicodeEncodingConflictResolver
  266         from Products.PageTemplates.unicodeconflictresolver import \
  267             DefaultUnicodeEncodingConflictResolver
  268         from zope.component import getUtility
  269         from zope.component import provideUtility
  270         provideUtility(DefaultUnicodeEncodingConflictResolver,
  271                        IUnicodeEncodingConflictResolver)
  272         resolver = getUtility(IUnicodeEncodingConflictResolver)
  273         self.assertRaises(UnicodeDecodeError,
  274                           resolver.resolve, None, b'\xe4\xfc\xf6', None)
  275 
  276     def testStrictResolver(self):
  277         from Products.PageTemplates.interfaces import \
  278             IUnicodeEncodingConflictResolver
  279         from Products.PageTemplates.unicodeconflictresolver import \
  280             StrictUnicodeEncodingConflictResolver
  281         from zope.component import getUtility
  282         from zope.component import provideUtility
  283         provideUtility(StrictUnicodeEncodingConflictResolver,
  284                        IUnicodeEncodingConflictResolver)
  285         resolver = getUtility(IUnicodeEncodingConflictResolver)
  286         text = '\xe4\xfc\xe4'
  287         self.assertEqual(resolver.resolve(None, text, None), text)
  288 
  289     def testIgnoringResolver(self):
  290         from Products.PageTemplates.interfaces import \
  291             IUnicodeEncodingConflictResolver
  292         from Products.PageTemplates.unicodeconflictresolver import \
  293             IgnoringUnicodeEncodingConflictResolver
  294         from zope.component import getUtility
  295         from zope.component import provideUtility
  296         provideUtility(IgnoringUnicodeEncodingConflictResolver,
  297                        IUnicodeEncodingConflictResolver)
  298         resolver = getUtility(IUnicodeEncodingConflictResolver)
  299         self.assertEqual(resolver.resolve(None, b'\xe4\xfc\xf6', None), '')
  300 
  301     def testReplacingResolver(self):
  302         from Products.PageTemplates.interfaces import \
  303             IUnicodeEncodingConflictResolver
  304         from Products.PageTemplates.unicodeconflictresolver import \
  305             ReplacingUnicodeEncodingConflictResolver
  306         from zope.component import getUtility
  307         from zope.component import provideUtility
  308         provideUtility(ReplacingUnicodeEncodingConflictResolver,
  309                        IUnicodeEncodingConflictResolver)
  310         resolver = getUtility(IUnicodeEncodingConflictResolver)
  311         self.assertEqual(resolver.resolve(None, b'\xe4\xfc\xf6', None),
  312                          '\ufffd\ufffd\ufffd')
  313 
  314 
  315 class ZopeContextTests(unittest.TestCase):
  316 
  317     def _getTargetClass(self):
  318         from Products.PageTemplates.Expressions import ZopeContext
  319         return ZopeContext
  320 
  321     def _makeOne(self, engine=None, contexts=None):
  322         if engine is None:
  323             engine = self._makeEngine()
  324         if contexts is None:
  325             contexts = {}
  326         return self._getTargetClass()(engine, contexts)
  327 
  328     def _makeEngine(self):
  329         class DummyEngine:
  330             pass
  331         return DummyEngine()
  332 
  333     def test_class_conforms_to_ITALExpressionEngine(self):
  334         from zope.interface.verify import verifyClass
  335         from zope.tal.interfaces import ITALExpressionEngine
  336         verifyClass(ITALExpressionEngine, self._getTargetClass())
  337 
  338     def test_instance_conforms_to_ITALExpressionEngine(self):
  339         from zope.interface.verify import verifyObject
  340         from zope.tal.interfaces import ITALExpressionEngine
  341         verifyObject(ITALExpressionEngine, self._makeOne())
  342 
  343     def test_createErrorInfo_returns_unrestricted_object(self):
  344         # See: https://bugs.launchpad.net/zope2/+bug/174705
  345         context = self._makeOne()
  346         info = context.createErrorInfo(AttributeError('nonesuch'), (12, 3))
  347         self.assertTrue(info.type is AttributeError)
  348         self.assertEqual(info.__allow_access_to_unprotected_subobjects__, 1)