Removed a few stdlib dependencies. This is the first step for IronPython support...
authorArmin Ronacher <armin.ronacher@active-4.com>
Wed, 13 Aug 2008 16:24:17 +0000 (18:24 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Wed, 13 Aug 2008 16:24:17 +0000 (18:24 +0200)
--HG--
branch : trunk

jinja2/compiler.py
jinja2/constants.py
jinja2/debug.py
jinja2/environment.py
jinja2/filters.py
jinja2/lexer.py
jinja2/nodes.py
jinja2/runtime.py
jinja2/sandbox.py
jinja2/utils.py

index f1e182b2fff54f6e5566931a7c32a24ab8cfceaf..fab6ea0c09f7dd76c0b1a74e89fbcfec7e537f3f 100644 (file)
@@ -8,14 +8,12 @@
     :copyright: Copyright 2008 by Armin Ronacher.
     :license: BSD.
 """
-from copy import copy
-from keyword import iskeyword
 from cStringIO import StringIO
 from itertools import chain
 from jinja2 import nodes
 from jinja2.visitor import NodeVisitor, NodeTransformer
 from jinja2.exceptions import TemplateAssertionError
-from jinja2.utils import Markup, concat, escape
+from jinja2.utils import Markup, concat, escape, is_python_keyword
 
 
 operators = {
@@ -164,8 +162,10 @@ class Frame(object):
 
     def copy(self):
         """Create a copy of the current one."""
-        rv = copy(self)
-        rv.identifiers = copy(self.identifiers)
+        rv = object.__new__(self.__class__)
+        rv.__dict__.update(self.__dict__)
+        rv.identifiers = object.__new__(self.identifiers.__class__)
+        rv.identifiers.__dict__.update(self.identifiers.__dict__)
         return rv
 
     def inspect(self, nodes, hard_scope=False):
@@ -186,10 +186,12 @@ class Frame(object):
         standalone thing as it shares the resources with the frame it
         was created of, but it's not a rootlevel frame any longer.
         """
-        rv = copy(self)
+        rv = self.copy()
         rv.rootlevel = False
         return rv
 
+    __copy__ = copy
+
 
 class VisitorExit(RuntimeError):
     """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
@@ -449,7 +451,7 @@ class CodeGenerator(NodeVisitor):
         # we have to make sure that no invalid call is created.
         kwarg_workaround = False
         for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
-            if iskeyword(kwarg):
+            if is_python_keyword(kwarg):
                 kwarg_workaround = True
                 break
 
index a0b4a6366b14ab950f61e01e86b3b65dee45f0db..c471e79eeff313f98d439c7066cddf10e65f6d39 100644 (file)
@@ -30,3 +30,261 @@ sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
 tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
 ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
 viverra volutpat vulputate'''
+
+
+#: a dict of all html entities + apos
+HTML_ENTITIES = {
+    'AElig': 198,
+    'Aacute': 193,
+    'Acirc': 194,
+    'Agrave': 192,
+    'Alpha': 913,
+    'Aring': 197,
+    'Atilde': 195,
+    'Auml': 196,
+    'Beta': 914,
+    'Ccedil': 199,
+    'Chi': 935,
+    'Dagger': 8225,
+    'Delta': 916,
+    'ETH': 208,
+    'Eacute': 201,
+    'Ecirc': 202,
+    'Egrave': 200,
+    'Epsilon': 917,
+    'Eta': 919,
+    'Euml': 203,
+    'Gamma': 915,
+    'Iacute': 205,
+    'Icirc': 206,
+    'Igrave': 204,
+    'Iota': 921,
+    'Iuml': 207,
+    'Kappa': 922,
+    'Lambda': 923,
+    'Mu': 924,
+    'Ntilde': 209,
+    'Nu': 925,
+    'OElig': 338,
+    'Oacute': 211,
+    'Ocirc': 212,
+    'Ograve': 210,
+    'Omega': 937,
+    'Omicron': 927,
+    'Oslash': 216,
+    'Otilde': 213,
+    'Ouml': 214,
+    'Phi': 934,
+    'Pi': 928,
+    'Prime': 8243,
+    'Psi': 936,
+    'Rho': 929,
+    'Scaron': 352,
+    'Sigma': 931,
+    'THORN': 222,
+    'Tau': 932,
+    'Theta': 920,
+    'Uacute': 218,
+    'Ucirc': 219,
+    'Ugrave': 217,
+    'Upsilon': 933,
+    'Uuml': 220,
+    'Xi': 926,
+    'Yacute': 221,
+    'Yuml': 376,
+    'Zeta': 918,
+    'aacute': 225,
+    'acirc': 226,
+    'acute': 180,
+    'aelig': 230,
+    'agrave': 224,
+    'alefsym': 8501,
+    'alpha': 945,
+    'amp': 38,
+    'and': 8743,
+    'ang': 8736,
+    'apos': 39,
+    'aring': 229,
+    'asymp': 8776,
+    'atilde': 227,
+    'auml': 228,
+    'bdquo': 8222,
+    'beta': 946,
+    'brvbar': 166,
+    'bull': 8226,
+    'cap': 8745,
+    'ccedil': 231,
+    'cedil': 184,
+    'cent': 162,
+    'chi': 967,
+    'circ': 710,
+    'clubs': 9827,
+    'cong': 8773,
+    'copy': 169,
+    'crarr': 8629,
+    'cup': 8746,
+    'curren': 164,
+    'dArr': 8659,
+    'dagger': 8224,
+    'darr': 8595,
+    'deg': 176,
+    'delta': 948,
+    'diams': 9830,
+    'divide': 247,
+    'eacute': 233,
+    'ecirc': 234,
+    'egrave': 232,
+    'empty': 8709,
+    'emsp': 8195,
+    'ensp': 8194,
+    'epsilon': 949,
+    'equiv': 8801,
+    'eta': 951,
+    'eth': 240,
+    'euml': 235,
+    'euro': 8364,
+    'exist': 8707,
+    'fnof': 402,
+    'forall': 8704,
+    'frac12': 189,
+    'frac14': 188,
+    'frac34': 190,
+    'frasl': 8260,
+    'gamma': 947,
+    'ge': 8805,
+    'gt': 62,
+    'hArr': 8660,
+    'harr': 8596,
+    'hearts': 9829,
+    'hellip': 8230,
+    'iacute': 237,
+    'icirc': 238,
+    'iexcl': 161,
+    'igrave': 236,
+    'image': 8465,
+    'infin': 8734,
+    'int': 8747,
+    'iota': 953,
+    'iquest': 191,
+    'isin': 8712,
+    'iuml': 239,
+    'kappa': 954,
+    'lArr': 8656,
+    'lambda': 955,
+    'lang': 9001,
+    'laquo': 171,
+    'larr': 8592,
+    'lceil': 8968,
+    'ldquo': 8220,
+    'le': 8804,
+    'lfloor': 8970,
+    'lowast': 8727,
+    'loz': 9674,
+    'lrm': 8206,
+    'lsaquo': 8249,
+    'lsquo': 8216,
+    'lt': 60,
+    'macr': 175,
+    'mdash': 8212,
+    'micro': 181,
+    'middot': 183,
+    'minus': 8722,
+    'mu': 956,
+    'nabla': 8711,
+    'nbsp': 160,
+    'ndash': 8211,
+    'ne': 8800,
+    'ni': 8715,
+    'not': 172,
+    'notin': 8713,
+    'nsub': 8836,
+    'ntilde': 241,
+    'nu': 957,
+    'oacute': 243,
+    'ocirc': 244,
+    'oelig': 339,
+    'ograve': 242,
+    'oline': 8254,
+    'omega': 969,
+    'omicron': 959,
+    'oplus': 8853,
+    'or': 8744,
+    'ordf': 170,
+    'ordm': 186,
+    'oslash': 248,
+    'otilde': 245,
+    'otimes': 8855,
+    'ouml': 246,
+    'para': 182,
+    'part': 8706,
+    'permil': 8240,
+    'perp': 8869,
+    'phi': 966,
+    'pi': 960,
+    'piv': 982,
+    'plusmn': 177,
+    'pound': 163,
+    'prime': 8242,
+    'prod': 8719,
+    'prop': 8733,
+    'psi': 968,
+    'quot': 34,
+    'rArr': 8658,
+    'radic': 8730,
+    'rang': 9002,
+    'raquo': 187,
+    'rarr': 8594,
+    'rceil': 8969,
+    'rdquo': 8221,
+    'real': 8476,
+    'reg': 174,
+    'rfloor': 8971,
+    'rho': 961,
+    'rlm': 8207,
+    'rsaquo': 8250,
+    'rsquo': 8217,
+    'sbquo': 8218,
+    'scaron': 353,
+    'sdot': 8901,
+    'sect': 167,
+    'shy': 173,
+    'sigma': 963,
+    'sigmaf': 962,
+    'sim': 8764,
+    'spades': 9824,
+    'sub': 8834,
+    'sube': 8838,
+    'sum': 8721,
+    'sup': 8835,
+    'sup1': 185,
+    'sup2': 178,
+    'sup3': 179,
+    'supe': 8839,
+    'szlig': 223,
+    'tau': 964,
+    'there4': 8756,
+    'theta': 952,
+    'thetasym': 977,
+    'thinsp': 8201,
+    'thorn': 254,
+    'tilde': 732,
+    'times': 215,
+    'trade': 8482,
+    'uArr': 8657,
+    'uacute': 250,
+    'uarr': 8593,
+    'ucirc': 251,
+    'ugrave': 249,
+    'uml': 168,
+    'upsih': 978,
+    'upsilon': 965,
+    'uuml': 252,
+    'weierp': 8472,
+    'xi': 958,
+    'yacute': 253,
+    'yen': 165,
+    'yuml': 255,
+    'zeta': 950,
+    'zwj': 8205,
+    'zwnj': 8204
+}
index 9209054848d2f1cba4673b825f5adb75e3717777..f503c218a4ae25ff064f3a154aa9b2bb0bbb45bf 100644 (file)
@@ -11,7 +11,7 @@
     :license: BSD.
 """
 import sys
-from types import CodeType
+from jinja2.utils import CodeType
 
 
 def translate_exception(exc_info):
index 7aa757579be3d38edb9b06fd22c43ccb58708ac8..7faad2683b62ff6dad91c08f00fc470ef9700ef9 100644 (file)
@@ -10,7 +10,7 @@
 """
 import sys
 from jinja2.defaults import *
-from jinja2.lexer import Lexer, TokenStream
+from jinja2.lexer import get_lexer, TokenStream
 from jinja2.parser import Parser
 from jinja2.optimizer import optimize
 from jinja2.compiler import generate
@@ -281,10 +281,7 @@ class Environment(object):
 
         return _environment_sanity_check(rv)
 
-    @property
-    def lexer(self):
-        """Return a fresh lexer for the environment."""
-        return Lexer(self)
+    lexer = property(get_lexer, doc="The lexer for this environment.")
 
     def getitem(self, obj, argument):
         """Get an item or attribute of an object but prefer the item."""
index 9cd3e50b52bf5e12627a4fbb7a6ac45fddf04cc9..da5f99d7a22dea4117109c86af1c0589ea638263 100644 (file)
@@ -10,7 +10,6 @@
 """
 import re
 import math
-import textwrap
 from random import choice
 from operator import itemgetter
 from itertools import imap, groupby
@@ -365,6 +364,7 @@ def do_wordwrap(s, width=79, break_long_words=True):
     parameter.  If you set the second parameter to `false` Jinja will not
     split words apart if they are longer than `width`.
     """
+    import textwrap
     return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
                                    replace_whitespace=False,
                                    break_long_words=break_long_words))
index 0597b7a80b46f63c4067f6190c27fd1b3367e2ed..9702205df7309374a5279437d9563846bb76c324 100644 (file)
@@ -15,7 +15,6 @@
     :license: BSD, see LICENSE for more details.
 """
 import re
-import unicodedata
 from operator import itemgetter
 from collections import deque
 from jinja2.exceptions import TemplateSyntaxError
@@ -27,9 +26,9 @@ from jinja2.utils import LRUCache
 _lexer_cache = LRUCache(50)
 
 # static regular expressions
-whitespace_re = re.compile(r'\s+(?um)')
+whitespace_re = re.compile(r'\s+', re.U)
 string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
-                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?ms)')
+                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
 integer_re = re.compile(r'\d+')
 name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
 float_re = re.compile(r'\d+\.\d+')
@@ -246,26 +245,22 @@ class TokenStream(object):
             self.next()
 
 
-class LexerMeta(type):
-    """Metaclass for the lexer that caches instances for
-    the same configuration in a weak value dictionary.
-    """
-
-    def __call__(cls, environment):
-        key = (environment.block_start_string,
-               environment.block_end_string,
-               environment.variable_start_string,
-               environment.variable_end_string,
-               environment.comment_start_string,
-               environment.comment_end_string,
-               environment.line_statement_prefix,
-               environment.trim_blocks,
-               environment.newline_sequence)
-        lexer = _lexer_cache.get(key)
-        if lexer is None:
-            lexer = type.__call__(cls, environment)
-            _lexer_cache[key] = lexer
-        return lexer
+def get_lexer(environment):
+    """Return a lexer which is probably cached."""
+    key = (environment.block_start_string,
+           environment.block_end_string,
+           environment.variable_start_string,
+           environment.variable_end_string,
+           environment.comment_start_string,
+           environment.comment_end_string,
+           environment.line_statement_prefix,
+           environment.trim_blocks,
+           environment.newline_sequence)
+    lexer = _lexer_cache.get(key)
+    if lexer is None:
+        lexer = Lexer(environment)
+        _lexer_cache[key] = lexer
+    return lexer
 
 
 class Lexer(object):
@@ -276,8 +271,6 @@ class Lexer(object):
     Multiple environments can share the same lexer.
     """
 
-    __metaclass__ = LexerMeta
-
     def __init__(self, environment):
         # shortcuts
         c = lambda x: re.compile(x, re.M | re.S)
index effa6d4adad454bff547d033dd9cbcb55e2ea587..034becf60c07e9ba18bbc2465569a8b72484470a 100644 (file)
@@ -13,7 +13,6 @@
     :license: BSD, see LICENSE for more details.
 """
 import operator
-from copy import copy
 from itertools import chain, izip
 from collections import deque
 from jinja2.utils import Markup
@@ -154,30 +153,6 @@ class Node(object):
             for result in child.find_all(node_type):
                 yield result
 
-    def copy(self):
-        """Return a deep copy of the node."""
-        result = object.__new__(self.__class__)
-        for field, value in self.iter_fields():
-            if isinstance(value, Node):
-                new_value = value.copy()
-            elif isinstance(value, list):
-                new_value = []
-                for item in value:
-                    if isinstance(item, Node):
-                        item = item.copy()
-                    else:
-                        item = copy(item)
-                    new_value.append(item)
-            else:
-                new_value = copy(value)
-            setattr(result, field, new_value)
-        for attr in self.attributes:
-            try:
-                setattr(result, attr, getattr(self, attr))
-            except AttributeError:
-                pass
-        return result
-
     def set_ctx(self, ctx):
         """Reset the context of a node and all child nodes.  Per default the
         parser will all generate nodes that have a 'load' context as it's the
index 496b484a66f1d9726f004e4b7b2072428438bf56..2c3aeb09df102e89968d6493a6d64cab1f4bc236 100644 (file)
@@ -9,7 +9,6 @@
     :license: BSD.
 """
 import sys
-from types import FunctionType, MethodType
 from itertools import chain, imap
 from jinja2.utils import Markup, partial, soft_unicode, escape, missing, concat
 from jinja2.exceptions import UndefinedError, TemplateRuntimeError
@@ -20,7 +19,13 @@ __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
            'TemplateRuntimeError', 'missing', 'concat', 'escape',
            'markup_join', 'unicode_join']
 
-_context_function_types = (FunctionType, MethodType)
+
+#: get the types we support for context functions.  We do not use types because
+#: IronPython doesn't provide that module out of the box.
+class _C(object):
+    meth = lambda: None
+_context_function_types = (type(lambda: None), type(_C.meth))
+del _C
 
 
 def markup_join(seq):
index 20de3695b7faef5a60381c03b2594defa6a7cc62..e9ab1d9165b270e5955abea65e33a1b000f8c88a 100644 (file)
     :license: BSD.
 """
 import operator
-from types import FunctionType, MethodType, TracebackType, CodeType, \
-     FrameType, GeneratorType
 from jinja2.runtime import Undefined
 from jinja2.environment import Environment
 from jinja2.exceptions import SecurityError
+from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
+     FrameType, GeneratorType
 
 
 #: maximum number of items a range may produce
index b1c20b699159d0fc377926577f381632b9f700f9..1ed653668bce549de384808b13a8f2fb5aa8edec 100644 (file)
 """
 import re
 import sys
-import string
 try:
     from thread import allocate_lock
 except ImportError:
     from dummy_thread import allocate_lock
-from htmlentitydefs import name2codepoint
 from collections import deque
-from copy import deepcopy
 from itertools import imap
 
 
@@ -31,8 +28,8 @@ _punctuation_re = re.compile(
 _simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
 _striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
 _entity_re = re.compile(r'&([^;]+);')
-_entities = name2codepoint.copy()
-_entities['apos'] = 39
+_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+_digits = '0123456789'
 
 # special singleton representing missing values for the runtime
 missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
@@ -62,6 +59,40 @@ except TypeError, _error:
     del _test_gen_bug, _error
 
 
+# ironpython without stdlib doesn't have keyword
+try:
+    from keyword import iskeyword as is_python_keyword
+except ImportError:
+    _py_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9]*$')
+    def is_python_keyword(name):
+        if _py_identifier_re.search(name) is None:
+            return False
+        try:
+            exec name + " = 42"
+        except SyntaxError:
+            return False
+        return True
+
+
+# common types.  These do exist in the special types module too which however
+# does not exist in IronPython out of the box.
+class _C(object):
+    def method(self): pass
+def _func():
+    yield None
+FunctionType = type(_func)
+GeneratorType = type(_func())
+MethodType = type(_C.method)
+CodeType = type(_C.method.func_code)
+try:
+    raise TypeError()
+except TypeError:
+    _tb = sys.exc_info()[2]
+    TracebackType = type(_tb)
+    FrameType = type(_tb.tb_frame)
+del _C, _tb, _func
+
+
 def contextfunction(f):
     """This decorator can be used to mark a function or method context callable.
     A context callable is passed the active :class:`Context` as first argument when
@@ -179,7 +210,7 @@ def urlize(text, trim_url_limit=None, nofollow=False):
                 '@' not in middle and
                 not middle.startswith('http://') and
                 len(middle) > 0 and
-                middle[0] in string.letters + string.digits and (
+                middle[0] in _letters + _digits and (
                     middle.endswith('.org') or
                     middle.endswith('.net') or
                     middle.endswith('.com')
@@ -355,10 +386,11 @@ class Markup(unicode):
         >>> Markup("Main &raquo; <em>About</em>").unescape()
         u'Main \xbb <em>About</em>'
         """
+        from jinja2.constants import HTML_ENTITIES
         def handle_match(m):
             name = m.group(1)
-            if name in _entities:
-                return unichr(_entities[name])
+            if name in HTML_ENTITIES:
+                return unichr(HTML_ENTITIES[name])
             try:
                 if name[:2] in ('#x', '#X'):
                     return unichr(int(name[2:], 16))