From: Armin Ronacher Date: Wed, 13 Aug 2008 16:24:17 +0000 (+0200) Subject: Removed a few stdlib dependencies. This is the first step for IronPython support... X-Git-Tag: 2.1~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9a0078d5730e98330788add74cc7fe66b7122ecb;p=thirdparty%2Fjinja.git Removed a few stdlib dependencies. This is the first step for IronPython support, the second one being a new lexer. --HG-- branch : trunk --- diff --git a/jinja2/compiler.py b/jinja2/compiler.py index f1e182b2..fab6ea0c 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -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 diff --git a/jinja2/constants.py b/jinja2/constants.py index a0b4a636..c471e79e 100644 --- a/jinja2/constants.py +++ b/jinja2/constants.py @@ -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 +} diff --git a/jinja2/debug.py b/jinja2/debug.py index 92090548..f503c218 100644 --- a/jinja2/debug.py +++ b/jinja2/debug.py @@ -11,7 +11,7 @@ :license: BSD. """ import sys -from types import CodeType +from jinja2.utils import CodeType def translate_exception(exc_info): diff --git a/jinja2/environment.py b/jinja2/environment.py index 7aa75757..7faad268 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -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.""" diff --git a/jinja2/filters.py b/jinja2/filters.py index 9cd3e50b..da5f99d7 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -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)) diff --git a/jinja2/lexer.py b/jinja2/lexer.py index 0597b7a8..9702205d 100644 --- a/jinja2/lexer.py +++ b/jinja2/lexer.py @@ -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) diff --git a/jinja2/nodes.py b/jinja2/nodes.py index effa6d4a..034becf6 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -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 diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 496b484a..2c3aeb09 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -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): diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py index 20de3695..e9ab1d91 100644 --- a/jinja2/sandbox.py +++ b/jinja2/sandbox.py @@ -13,11 +13,11 @@ :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 diff --git a/jinja2/utils.py b/jinja2/utils.py index b1c20b69..1ed65366 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -10,14 +10,11 @@ """ 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 » About").unescape() u'Main \xbb About' """ + 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))