: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 = {
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):
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."""
# 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
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
+}
:license: BSD.
"""
import sys
-from types import CodeType
+from jinja2.utils import CodeType
def translate_exception(exc_info):
"""
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
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."""
"""
import re
import math
-import textwrap
from random import choice
from operator import itemgetter
from itertools import imap, groupby
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))
:license: BSD, see LICENSE for more details.
"""
import re
-import unicodedata
from operator import itemgetter
from collections import deque
from jinja2.exceptions import TemplateSyntaxError
_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+')
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):
Multiple environments can share the same lexer.
"""
- __metaclass__ = LexerMeta
-
def __init__(self, environment):
# shortcuts
c = lambda x: re.compile(x, re.M | re.S)
: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
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
: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
'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):
: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
"""
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
_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'})()
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
'@' 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')
>>> Markup("Main » <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))