From 07bc684ccdbf4bfabbf81de1ecdfa531e65f923a Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 31 Mar 2008 14:18:49 +0200 Subject: [PATCH] a clean restart --HG-- branch : trunk rename : jinja/__init__.py => jinja2/__init__.py rename : jinja/_debugger.c => jinja2/_debugger.c rename : jinja/_native.py => jinja2/_native.py rename : jinja/_speedups.c => jinja2/_speedups.c rename : jinja/constants.py => jinja2/constants.py rename : jinja/contrib/__init__.py => jinja2/contrib/__init__.py rename : jinja/contrib/_djangosupport.py => jinja2/contrib/_djangosupport.py rename : jinja/contrib/djangosupport.py => jinja2/contrib/djangosupport.py rename : jinja/datastructure.py => jinja2/datastructure.py rename : jinja/defaults.py => jinja2/defaults.py rename : jinja/environment.py => jinja2/environment.py rename : jinja/exceptions.py => jinja2/exceptions.py rename : jinja/filters.py => jinja2/filters.py rename : jinja/lexer.py => jinja2/lexer.py rename : jinja/loaders.py => jinja2/loaders.py rename : jinja/nodes.py => jinja2/nodes.py rename : jinja/parser.py => jinja2/parser.py rename : jinja/tests.py => jinja2/tests.py rename : jinja/translators/__init__.py => jinja2/translators/__init__.py rename : jinja/translators/python.py => jinja2/translators/python.py rename : jinja/utils.py => jinja2/utils.py --- jinja/datastructure.py | 708 ----------- jinja/debugger.py | 213 ---- jinja/environment.py | 404 ------- jinja/exceptions.py | 91 -- jinja/loaders.py | 825 ------------- jinja/nodes.py | 792 ------------- jinja/parser.py | 1187 ------------------- jinja/plugin.py | 166 --- jinja/utils.py | 645 ---------- {jinja => jinja2}/__init__.py | 24 +- {jinja => jinja2}/_debugger.c | 0 {jinja => jinja2}/_native.py | 0 {jinja => jinja2}/_speedups.c | 0 {jinja => jinja2}/constants.py | 0 {jinja => jinja2}/contrib/__init__.py | 0 {jinja => jinja2}/contrib/_djangosupport.py | 0 {jinja => jinja2}/contrib/djangosupport.py | 0 jinja2/datastructure.py | 137 +++ {jinja => jinja2}/defaults.py | 2 +- jinja2/environment.py | 115 ++ jinja2/exceptions.py | 42 + {jinja => jinja2}/filters.py | 4 - {jinja => jinja2}/lexer.py | 39 +- jinja2/loaders.py | 10 + jinja2/nodes.py | 494 ++++++++ jinja2/parser.py | 39 + {jinja => jinja2}/tests.py | 0 {jinja => jinja2}/translators/__init__.py | 0 {jinja => jinja2}/translators/python.py | 0 jinja2/utils.py | 10 + 30 files changed, 863 insertions(+), 5084 deletions(-) delete mode 100644 jinja/datastructure.py delete mode 100644 jinja/debugger.py delete mode 100644 jinja/environment.py delete mode 100644 jinja/exceptions.py delete mode 100644 jinja/loaders.py delete mode 100644 jinja/nodes.py delete mode 100644 jinja/parser.py delete mode 100644 jinja/plugin.py delete mode 100644 jinja/utils.py rename {jinja => jinja2}/__init__.py (71%) rename {jinja => jinja2}/_debugger.c (100%) rename {jinja => jinja2}/_native.py (100%) rename {jinja => jinja2}/_speedups.c (100%) rename {jinja => jinja2}/constants.py (100%) rename {jinja => jinja2}/contrib/__init__.py (100%) rename {jinja => jinja2}/contrib/_djangosupport.py (100%) rename {jinja => jinja2}/contrib/djangosupport.py (100%) create mode 100644 jinja2/datastructure.py rename {jinja => jinja2}/defaults.py (86%) create mode 100644 jinja2/environment.py create mode 100644 jinja2/exceptions.py rename {jinja => jinja2}/filters.py (99%) rename {jinja => jinja2}/lexer.py (94%) create mode 100644 jinja2/loaders.py create mode 100644 jinja2/nodes.py create mode 100644 jinja2/parser.py rename {jinja => jinja2}/tests.py (100%) rename {jinja => jinja2}/translators/__init__.py (100%) rename {jinja => jinja2}/translators/python.py (100%) create mode 100644 jinja2/utils.py diff --git a/jinja/datastructure.py b/jinja/datastructure.py deleted file mode 100644 index eb213d4..0000000 --- a/jinja/datastructure.py +++ /dev/null @@ -1,708 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja.datastructure - ~~~~~~~~~~~~~~~~~~~ - - Module that helds several data types used in the template engine. - - :copyright: 2007 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from jinja.exceptions import TemplateSyntaxError, TemplateRuntimeError - -_missing = object() - - -def contextcallable(f): - """ - Mark a function context callable. - """ - f.jinja_context_callable = True - return f - - -def unsafe(f): - """ - Mark function as unsafe. - """ - f.jinja_unsafe_call = True - return f - - -def make_undefined(implementation): - """ - Creates an undefined singleton based on a given implementation. - It performs some tests that make sure the undefined type implements - everything it should. - """ - self = object.__new__(implementation) - self.__reduce__() - return self - - -class AbstractUndefinedType(object): - """ - Base class for any undefined type. - """ - __slots__ = () - - def __init__(self): - raise TypeError('cannot create %r instances' % - self.__class__.__name__) - - def __setattr__(self, name, value): - raise AttributeError('%r object has no attribute %r' % ( - self.__class__.__name__, - name - )) - - def __eq__(self, other): - return self is other - - def __ne__(self, other): - return self is not other - - def __copy__(self): - return self - __deepcopy__ = __copy__ - - def __repr__(self): - return 'Undefined' - - def __reduce__(self): - raise TypeError('undefined objects have to provide a __reduce__') - - -class SilentUndefinedType(AbstractUndefinedType): - """ - An object that does not exist. - """ - __slots__ = () - - def __add__(self, other): - """Any operator returns the operand.""" - return other - __sub__ = __mul__ = __div__ = __rsub__ = __rmul__ = __div__ = __mod__ =\ - __radd__ = __rmod__ = __add__ - - def __getitem__(self, arg): - """Getting any item returns `Undefined`""" - return self - - def __iter__(self): - """Iterating over `Undefined` returns an empty iterator.""" - if False: - yield None - - def __getattr__(self, arg): - """Getting any attribute returns `Undefined`""" - return self - - def __nonzero__(self): - """`Undefined` is considered boolean `False`""" - return False - - def __len__(self): - """`Undefined` is an empty sequence""" - return 0 - - def __str__(self): - """The string representation is empty.""" - return '' - - def __unicode__(self): - """The unicode representation is empty.""" - return u'' - - def __int__(self): - """Converting `Undefined` to an integer ends up in ``0``""" - return 0 - - def __float__(self): - """Converting `Undefined` to an float ends up in ``0.0``""" - return 0.0 - - def __call__(self, *args, **kwargs): - """Calling `Undefined` returns `Undefined`""" - return self - - def __reduce__(self): - """Helper for pickle.""" - return 'SilentUndefined' - - -class ComplainingUndefinedType(AbstractUndefinedType): - """ - An object that does not exist. - """ - __slots__ = () - - def __len__(self): - """Getting the length raises error.""" - raise TemplateRuntimeError('Operated on undefined object') - - def __iter__(self): - """Iterating over `Undefined` raises an error.""" - raise TemplateRuntimeError('Iterated over undefined object') - - def __nonzero__(self): - """`Undefined` is considered boolean `False`""" - return False - - def __str__(self): - """The string representation raises an error.""" - raise TemplateRuntimeError('Undefined object rendered') - - def __unicode__(self): - """The unicode representation raises an error.""" - self.__str__() - - def __call__(self, *args, **kwargs): - """Calling `Undefined` returns `Undefined`""" - raise TemplateRuntimeError('Undefined object called') - - def __reduce__(self): - """Helper for pickle.""" - return 'ComplainingUndefined' - - -#: the singleton instances for the undefined objects -SilentUndefined = make_undefined(SilentUndefinedType) -ComplainingUndefined = make_undefined(ComplainingUndefinedType) - -#: jinja 1.0 compatibility -Undefined = SilentUndefined -UndefinedType = SilentUndefinedType - - -class FakeTranslator(object): - """ - Default null translator. - """ - - def gettext(self, s): - """ - Translate a singular string. - """ - return s - - def ngettext(self, s, p, n): - """ - Translate a plural string. - """ - if n == 1: - return s - return p - - -class Deferred(object): - """ - Object marking an deferred value. Deferred objects are - objects that are called first access in the context. - """ - - def __init__(self, factory): - self.factory = factory - - def __call__(self, context, name): - return self.factory(context.environment, context, name) - - -class Markup(unicode): - """ - Compatibility for Pylons and probably some other frameworks. - - It's only used in Jinja environments with `auto_escape` set - to true. - """ - - def __html__(self): - return unicode(self) - - -class TemplateData(Markup): - """ - Subclass of unicode to mark objects that are coming from the - template. The autoescape filter can use that. - """ - - -# import these here because those modules import Deferred and Undefined -# from this module. -try: - # try to use the c implementation of the base context if available - from jinja._speedups import BaseContext -except ImportError: - # if there is no c implementation we go with a native python one - from jinja._native import BaseContext - - -class Context(BaseContext): - """ - Dict like object containing the variables for the template. - """ - - def __init__(self, *args, **kwargs): - environment = args[0] - if not kwargs and len(args) == 2 and isinstance(args[1], dict): - base = args[1] - else: - base = dict(*args[1:], **kwargs) - super(Context, self).__init__(environment.undefined_singleton, - environment.globals, base) - self._translate_func = None - self.cache = {} - self.environment = environment - - def to_dict(self): - """ - Convert the context into a dict. This skips the globals. - """ - result = {} - for layer in self.stack[1:]: - for key, value in layer.iteritems(): - if key.startswith('::'): - continue - result[key] = value - return result - - def set_nonlocal(self, name, value): - """ - Set a value in an outer scope. - """ - for layer in self.stack[:0:-1]: - if name in layer: - layer[name] = value - return - self.initial[name] = value - - def translate_func(self): - """ - The translation function for this context. It takes - 4 parameters. The singular string, the optional plural one, - The name of the variable in the replacements dict and the - replacements dict. This is only used by the i18n system - internally the simplified version (just one argument) is - available in the template for the user too. - """ - if self._translate_func is not None: - return self._translate_func - translator = self.environment.get_translator(self) - gettext = translator.gettext - ngettext = translator.ngettext - def translate(s, p=None, n=None, r=None): - if p is None: - s = gettext(s) - else: - s = ngettext(s, p, r[n]) - # apply replacement substitution only if replacements - # are given. This is the case for {% trans %}...{% endtrans %} - # but for the "_()" syntax and a trans tag without a body. - if r is not None: - return s % r - return s - translate.__doc__ = Context.translate_func.__doc__ - self._translate_func = translate - return translate - translate_func = property(translate_func, doc=translate_func.__doc__) - - def __repr__(self): - """ - String representation of the context. - """ - return 'Context(%r)' % self.to_dict() - - def __pretty__(self, p, cycle): - if cycle: - return p.text('Context({...})') - p.begin_group(9, 'Context({') - for idx, (key, value) in enumerate(self.to_dict().iteritems()): - if idx: - p.text(',') - p.breakable() - p.pretty(key) - p.text(': ') - p.pretty(value) - p.end_group(9, '})') - - -class LoopContext(object): - """ - Simple class that provides special loop variables. - Used by `Environment.iterate`. - """ - - jinja_allowed_attributes = ['index', 'index0', 'length', 'parent', - 'even', 'odd', 'revindex0', 'revindex', - 'first', 'last'] - - def __init__(self, seq, parent, loop_function): - self.loop_function = loop_function - self.parent = parent - self._stack = [] - if loop_function is None: - self.push(seq) - - def push(self, seq): - """ - Push a sequence to the loop stack. This is used by the - recursive for loop. - """ - # iteration over None is catched, but we don't catch iteration - # over undefined because that behavior is handled in the - # undefined singleton - if seq is None: - seq = () - length = 0 - else: - try: - length = len(seq) - except (AttributeError, TypeError): - seq = list(seq) - length = len(seq) - self._stack.append({ - 'index': -1, - 'seq': seq, - 'length': length - }) - return self - - def pop(self): - """Remove the last layer from the loop stack.""" - return self._stack.pop() - - iterated = property(lambda s: s._stack[-1]['index'] > -1) - index0 = property(lambda s: s._stack[-1]['index']) - index = property(lambda s: s._stack[-1]['index'] + 1) - revindex0 = property(lambda s: s._stack[-1]['length'] - - s._stack[-1]['index'] - 1) - revindex = property(lambda s: s._stack[-1]['length'] - - s._stack[-1]['index']) - length = property(lambda s: s._stack[-1]['length']) - even = property(lambda s: s._stack[-1]['index'] % 2 == 1) - odd = property(lambda s: s._stack[-1]['index'] % 2 == 0) - first = property(lambda s: s._stack[-1]['index'] == 0) - last = property(lambda s: s._stack[-1]['index'] == - s._stack[-1]['length'] - 1) - - def __iter__(self): - s = self._stack[-1] - for idx, item in enumerate(s['seq']): - s['index'] = idx - yield item - - def __len__(self): - return self._stack[-1]['length'] - - def __call__(self, seq): - if self.loop_function is not None: - return self.loop_function(seq) - raise TemplateRuntimeError('In order to make loops callable you have ' - 'to define them with the "recursive" ' - 'modifier.') - - def __repr__(self): - if self._stack: - return '' % ( - self.index, - self.length, - self.loop_function is not None and ' recursive' or '' - ) - return '' - - -class CycleContext(object): - """ - Helper class used for cycling. - """ - - def __init__(self, seq=None): - self.pos = -1 - # bind the correct helper function based on the constructor signature - if seq is not None: - self.seq = seq - self.length = len(seq) - self.cycle = self.cycle_static - else: - self.cycle = self.cycle_dynamic - - def cycle_static(self): - """Helper function for static cycling.""" - self.pos = (self.pos + 1) % self.length - return self.seq[self.pos] - - def cycle_dynamic(self, seq): - """Helper function for dynamic cycling.""" - self.pos = pos = (self.pos + 1) % len(seq) - return seq[pos] - - -class SuperBlock(object): - """ - Helper class for ``{{ super() }}``. - """ - jinja_allowed_attributes = ['name'] - - def __init__(self, name, blocks, level, context): - self.name = name - self.context = context - if name in blocks: - self.stack = blocks[name] - self.level = level - else: - self.stack = None - - def __call__(self, offset=1): - if self.stack is not None: - level = self.level + (offset - 1) - if level < len(self.stack): - return self.stack[level](self.context) - raise TemplateRuntimeError('no super block for %r' % self.name) - - def __repr__(self): - return '' % self.name - - -class StateTest(object): - """ - Wrapper class for basic lambdas in order to simplify - debugging in the parser. It also provides static helper - functions that replace some lambda expressions - """ - - def __init__(self, func, msg): - self.func = func - self.msg = msg - - def __call__(self, token): - return self.func(token) - - def expect_token(*types, **kw): - """Scans until one of the given tokens is found.""" - msg = kw.pop('msg', None) - if kw: - raise TypeError('unexpected keyword argument %r' % iter(kw).next()) - if len(types) == 1: - if msg is None: - msg = "expected '%s'" % types[0] - return StateTest(lambda t: t.type == types[0], msg) - if msg is None: - msg = 'expected one of %s' % ', '.join(["'%s'" % type - for type in types]) - return StateTest(lambda t: t.type in types, msg) - expect_token = staticmethod(expect_token) - - -class Token(object): - """ - Token class. - """ - __slots__ = ('lineno', 'type', 'value') - - def __init__(self, lineno, type, value): - self.lineno = lineno - self.type = intern(str(type)) - self.value = value - - def __str__(self): - from jinja.lexer import keywords, reverse_operators - if self.type in keywords: - return self.type - elif self.type in reverse_operators: - return reverse_operators[self.type] - return self.value - - def __repr__(self): - return 'Token(%r, %r, %r)' % ( - self.lineno, - self.type, - self.value - ) - - -class TokenStreamIterator(object): - """ - The iterator for tokenstreams. Iterate over the stream - until the eof token is reached. - """ - - def __init__(self, stream): - self._stream = stream - - def __iter__(self): - return self - - def next(self): - token = self._stream.current - if token.type == 'eof': - self._stream.close() - raise StopIteration() - self._stream.next() - return token - - -class TokenStream(object): - """ - A token stream wraps a generator and supports pushing tokens back. - It also provides some functions to expect tokens and similar stuff. - - Important note: Do never push more than one token back to the - stream. Although the stream object won't stop you - from doing so, the behavior is undefined. Multiple - pushed tokens are only used internally! - """ - - def __init__(self, generator, filename): - self._next = generator.next - self._pushed = [] - self.current = Token(1, 'initial', '') - self.filename = filename - self.next() - - def __iter__(self): - return TokenStreamIterator(self) - - def bound(self): - """Return True if the token stream is bound to a parser.""" - return self.parser is not None - bound = property(bound, doc=bound.__doc__) - - def lineno(self): - """The current line number.""" - return self.current.lineno - lineno = property(lineno, doc=lineno.__doc__) - - def __nonzero__(self): - """Are we at the end of the tokenstream?""" - return bool(self._pushed) or self.current.type != 'eof' - - eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__) - - def look(self): - """See what's the next token.""" - if self._pushed: - return self._pushed[-1] - old_token = self.current - self.next() - new_token = self.current - self.current = old_token - self.push(new_token) - return new_token - - def push(self, token): - """Push a token back to the stream.""" - self._pushed.append(token) - - def skip(self, n): - """Got n tokens ahead.""" - for x in xrange(n): - self.next() - - def shift(self, token): - """ - Push one token into the stream. - """ - old_current = self.current - self.next() - self.push(self.current) - self.push(old_current) - self.push(token) - self.next() - - def next(self): - """Go one token ahead.""" - if self._pushed: - self.current = self._pushed.pop() - elif self.current.type != 'eof': - try: - self.current = self._next() - except StopIteration: - self.close() - - def read_whitespace(self): - """Read all the whitespace, up to the next tag.""" - lineno = self.current.lineno - buf = [] - while self.current.type == 'data' and not \ - self.current.value.strip(): - buf.append(self.current.value) - self.next() - if buf: - return Token(lineno, 'data', u''.join(buf)) - - def close(self): - """Close the stream.""" - self.current = Token(self.current.lineno, 'eof', '') - self._next = None - - def expect(self, token_type, token_value=_missing): - """Expect a given token type and return it""" - if self.current.type != token_type: - raise TemplateSyntaxError("expected token %r, got %r" % - (token_type, self.current.type), - self.current.lineno, - self.filename) - elif token_value is not _missing and \ - self.current.value != token_value: - raise TemplateSyntaxError("expected %r, got %r" % - (token_value, self.current.value), - self.current.lineno, - self.filename) - try: - return self.current - finally: - self.next() - - -class TemplateStream(object): - """ - Wraps a genererator for outputing template streams. - """ - - def __init__(self, gen): - self._gen = gen - self._next = gen.next - self.buffered = False - - def disable_buffering(self): - """ - Disable the output buffering. - """ - self._next = self._gen.next - self.buffered = False - - def enable_buffering(self, size=5): - """ - Enable buffering. Buffer `size` items before - yielding them. - """ - if size <= 1: - raise ValueError('buffer size too small') - self.buffered = True - - def buffering_next(): - buf = [] - c_size = 0 - push = buf.append - next = self._gen.next - - try: - while True: - item = next() - if item: - push(item) - c_size += 1 - if c_size >= size: - raise StopIteration() - except StopIteration: - if not c_size: - raise - return u''.join(buf) - - self._next = buffering_next - - def __iter__(self): - return self - - def next(self): - return self._next() diff --git a/jinja/debugger.py b/jinja/debugger.py deleted file mode 100644 index 932fb1c..0000000 --- a/jinja/debugger.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jinja.debugger - ~~~~~~~~~~~~~~ - - This module implements helper function Jinja uses to give the users a - possibility to develop Jinja templates like they would debug python code. - It seamlessly integreates into the python traceback system, in fact it - just modifies the trackback stack so that the line numbers are correct - and the frame information are bound to the context and not the frame of - the template evaluation loop. - - To achive this it raises the exception it cought before in an isolated - namespace at a given line. The locals namespace is set to the current - template context. - - The traceback generated by raising that exception is then either returned - or linked with the former traceback if the `jinja._debugger` module is - available. Because it's not possible to modify traceback objects from the - python space this module is needed for this process. - - If it's not available it just ignores the other frames. Because this can - lead to actually harder to debug code there is a setting on the jinja - environment to disable the debugging system. - - The isolated namespace which is used to raise the exception also contains - a `__loader__` name that helds a reference to a PEP 302 compatible loader. - Because there are currently some traceback systems (such as the paste - evalexception debugger) that do not provide the frame globals when - retrieving the source from the linecache module, Jinja injects the source - to the linecache module itself and changes the filename to a URL style - "virtual filename" so that Jinja doesn't acidentally override other files - in the linecache. - - :copyright: 2007 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import sys -from random import randrange - -# if we have extended debugger support we should really use it -try: - from jinja._debugger import * - has_extended_debugger = True -except ImportError: - has_extended_debugger = False - -# we need the RUNTIME_EXCEPTION_OFFSET to skip the not used frames -from jinja.utils import reversed, RUNTIME_EXCEPTION_OFFSET - - -def fake_template_exception(exc_type, exc_value, tb, filename, lineno, - source, context_or_env, tb_back=None): - """ - Raise an exception "in a template". Return a traceback - object. This is used for runtime debugging, not compile time. - """ - # some traceback systems allow to skip frames - __traceback_hide__ = True - - # create the namespace which will be the local namespace - # of the new frame then. Some debuggers show local variables - # so we better inject the context and not the evaluation loop context. - from jinja.datastructure import Context - if isinstance(context_or_env, Context): - env = context_or_env.environment - namespace = context_or_env.to_dict() - else: - env = context_or_env - namespace = {} - - # no unicode for filenames - if isinstance(filename, unicode): - filename = filename.encode('utf-8') - - # generate an jinja unique filename used so that linecache - # gets data that doesn't interfere with other modules - if filename is None: - vfilename = 'jinja://~%d' % randrange(0, 10000) - filename = '' - else: - vfilename = 'jinja://%s' % filename - - # now create the used loaded and update the linecache - loader = TracebackLoader(env, source, filename) - loader.update_linecache(vfilename) - globals = { - '__name__': vfilename, - '__file__': vfilename, - '__loader__': loader - } - - # use the simple debugger to reraise the exception in the - # line where the error originally occoured - globals['__exception_to_raise__'] = (exc_type, exc_value) - offset = '\n' * (lineno - 1) - code = compile(offset + 'raise __exception_to_raise__[0], ' - '__exception_to_raise__[1]', - vfilename or '