text_type = str
string_types = (str,)
- _iterkeys = 'keys'
- _itervalues = 'values'
- _iteritems = 'items'
+ iterkeys = lambda d: iter(d.keys())
+ itervalues = lambda d: iter(d.values())
+ iteritems = lambda d: iter(d.items())
+ import pickle
from io import BytesIO, StringIO
NativeStringIO = StringIO
- ifilter = filter
- imap = map
- izip = zip
-
def reraise(tp, value, tb=None):
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
- Iterator = object
+ ifilter = filter
+ imap = map
+ izip = zip
+ intern = sys.intern
- class UnicodeMixin(object):
- __slots__ = ()
- def __str__(self):
- return self.__unicode__()
+ implements_iterator = lambda x: x
+ implements_to_string = lambda x: x
+ get_next = lambda x: x.__next__
else:
- text_type = unicode
unichr = unichr
+ text_type = unicode
+ range_type = xrange
string_types = (str, unicode)
- _iterkeys = 'iterkeys'
- _itervalues = 'itervalues'
- _iteritems = 'iteritems'
-
- from itertools import imap, izip, ifilter
- range_type = xrange
+ iterkeys = lambda d: d.iterkeys()
+ itervalues = lambda d: d.itervalues()
+ iteritems = lambda d: d.iteritems()
- from cStringIO import StringIO as BytesIO
- from StringIO import StringIO
+ import cPickle as pickle
+ from cStringIO import StringIO as BytesIO, StringIO
NativeStringIO = BytesIO
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
- class UnicodeMixin(object):
- __slots__ = ()
- def __str__(self):
- return self.__unicode__().encode('utf-8')
+ from itertools import imap, izip, ifilter
+ intern = intern
+
+ def implements_iterator(cls):
+ cls.next = cls.__next__
+ del cls.__next__
+ return cls
+
+ def implements_to_string(cls):
+ cls.__unicode__ = cls.__str__
+ cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
+ return cls
+
+ get_next = lambda x: x.next
- class Iterator(object):
- __slots__ = ()
- def next(self):
- return self.__next__()
try:
next = next
def with_metaclass(meta, *bases):
- """Create a base class with a metaclass."""
- return meta('NewBase', bases, {})
-
-def iterkeys(d, **kw):
- return iter(getattr(d, _iterkeys)(**kw))
+ class __metaclass__(meta):
+ __call__ = type.__call__
+ __init__ = type.__init__
+ def __new__(cls, name, this_bases, d):
+ if this_bases is None:
+ return type.__new__(cls, name, (), d)
+ return meta(name, bases, d)
+ return __metaclass__('<dummy_class>', None, {})
-def itervalues(d, **kw):
- return iter(getattr(d, _itervalues)(**kw))
-
-def iteritems(d, **kw):
- return iter(getattr(d, _iteritems)(**kw))
-
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
try:
from collections import Mapping as mapping_types
from jinja2.utils import import_string, LRUCache, Markup, missing, \
concat, consume, internalcode, _encode_filename
from jinja2._compat import imap, ifilter, string_types, iteritems, \
- text_type, reraise, Iterator, next, UnicodeMixin
+ text_type, reraise, implements_iterator, implements_to_string, \
+ get_next
from functools import reduce
return '<%s %s>' % (self.__class__.__name__, name)
-class TemplateModule(UnicodeMixin):
+@implements_to_string
+class TemplateModule(object):
"""Represents an imported template. All the exported names of the
template are available as attributes on this object. Additionally
converting it into an unicode- or bytestrings renders the contents.
def __html__(self):
return Markup(concat(self._body_stream))
- def __unicode__(self):
+ def __str__(self):
return concat(self._body_stream)
def __repr__(self):
return rv
-class TemplateStream(Iterator):
+@implements_iterator
+class TemplateStream(object):
"""A template stream works pretty much like an ordinary python generator
but it can buffer multiple items to reduce the number of total iterations.
Per default the output is unbuffered which means that for every unbuffered
def disable_buffering(self):
"""Disable the output buffering."""
- self._next = lambda: next(self._gen)
+ self._next = get_next(self._gen)
self.buffered = False
def enable_buffering(self, size=5):
c_size = 0
self.buffered = True
- self._next = lambda: next(generator(lambda: next(self._gen)))
+ self._next = get_next(generator(get_next(self._gen)))
def __iter__(self):
return self
:copyright: (c) 2010 by the Jinja Team.
:license: BSD, see LICENSE for more details.
"""
-from jinja2._compat import imap, text_type, PY2, UnicodeMixin
+from jinja2._compat import imap, text_type, PY2, implements_to_string
class TemplateError(Exception):
return message
-class TemplateNotFound(IOError, LookupError, TemplateError, UnicodeMixin):
+@implements_to_string
+class TemplateNotFound(IOError, LookupError, TemplateError):
"""Raised if a template does not exist."""
# looks weird, but removes the warning descriptor that just
self.name = name
self.templates = [name]
- def __unicode__(self):
+ def __str__(self):
return self.message
self.templates = list(names)
-class TemplateSyntaxError(UnicodeMixin, TemplateError):
+@implements_to_string
+class TemplateSyntaxError(TemplateError):
"""Raised to tell the user that there is a problem with the template."""
def __init__(self, message, lineno, name=None, filename=None):
# function translated the syntax error into a new traceback
self.translated = False
- def __unicode__(self):
+ def __str__(self):
# for translated errors we only return the message
if self.translated:
return self.message
from collections import deque
from jinja2.exceptions import TemplateSyntaxError
from jinja2.utils import LRUCache
-from jinja2._compat import next, iteritems, Iterator, text_type
+from jinja2._compat import next, iteritems, implements_iterator, text_type, \
+ intern
# cache for the lexers. Exists in order to be able to have multiple
float_re = re.compile(r'(?<!\.)\d+\.\d+')
newline_re = re.compile(r'(\r\n|\r|\n)')
-try:
- intern = intern # py2
-except NameError:
- import sys
- intern = sys.intern # py3
-
# internal the tokens and keep references to them
TOKEN_ADD = intern('add')
TOKEN_ASSIGN = intern('assign')
)
-class TokenStreamIterator(Iterator):
+@implements_iterator
+class TokenStreamIterator(object):
"""The iterator for tokenstreams. Iterate over the stream
until the eof token is reached.
"""
return token
-class TokenStream(Iterator):
+@implements_iterator
+class TokenStream(object):
"""A token stream is an iterable that yields :class:`Token`\s. The
parser however does not iterate over it but calls :meth:`next` to go
one token ahead. The current active token is stored as :attr:`current`.
internalcode, object_type_repr
from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
TemplateNotFound
-from jinja2._compat import next, imap, text_type, iteritems, Iterator, \
- string_types, PY2, UnicodeMixin
+from jinja2._compat import next, imap, text_type, iteritems, \
+ implements_iterator, implements_to_string, string_types
# these variables are exported to the template runtime
'TemplateNotFound']
#: the name of the function that is used to convert something into
-#: a string. 2to3 will adopt that automatically and the generated
-#: code can take advantage of it.
-try:
- to_string = unicode
-except NameError:
- to_string = str
+#: a string. We can just use the text type here.
+to_string = text_type
#: the identity function. Useful for certain things in the environment
identity = lambda x: x
)
-class LoopContextIterator(Iterator):
+@implements_iterator
+class LoopContextIterator(object):
"""The iterator for a loop context."""
__slots__ = ('context',)
)
-class Undefined(UnicodeMixin):
+@implements_to_string
+class Undefined(object):
"""The default undefined type. This undefined type can be printed and
iterated over, but every other access will raise an :exc:`UndefinedError`:
__float__ = __complex__ = __pow__ = __rpow__ = \
_fail_with_undefined_error
- def __unicode__(self):
+ def __str__(self):
return u''
def __len__(self):
return 'Undefined'
+@implements_to_string
class DebugUndefined(Undefined):
"""An undefined that returns the debug info when printed.
"""
__slots__ = ()
- def __unicode__(self):
+ def __str__(self):
if self._undefined_hint is None:
if self._undefined_obj is missing:
return u'{{ %s }}' % self._undefined_name
return u'{{ undefined value printed: %s }}' % self._undefined_hint
+@implements_to_string
class StrictUndefined(Undefined):
"""An undefined that barks on print and iteration as well as boolean
tests and all kinds of comparisons. In other words: you can do nothing
UndefinedError: 'foo' is undefined
"""
__slots__ = ()
- __iter__ = __unicode__ = __len__ = __nonzero__ = __eq__ = \
+ __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \
__ne__ = __bool__ = Undefined._fail_with_undefined_error
from jinja2.environment import Environment
from jinja2.exceptions import SecurityError
from jinja2._compat import string_types, function_type, method_type, \
- traceback_type, code_type, frame_type, generator_type
+ traceback_type, code_type, frame_type, generator_type, PY2
#: maximum number of items a range may produce
#: unsafe method attributes. function attributes are unsafe for methods too
UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
+#: unsafe generator attirbutes.
+UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code'])
+
+# On versions > python 2 the special attributes on functions are gone,
+# but they remain on methods and generators for whatever reason.
+if not PY2:
+ UNSAFE_FUNCTION_ATTRIBUTES = set()
import warnings
elif isinstance(obj, (code_type, traceback_type, frame_type)):
return True
elif isinstance(obj, generator_type):
- if attr == 'gi_frame':
+ if attr in UNSAFE_GENERATOR_ATTRIBUTES:
return True
return attr.startswith('__')
regex_type = type(number_re)
-try:
- test_callable = callable
-except NameError:
- def test_callable(x):
- return hasattr(x, '__call__')
+test_callable = callable
def test_odd(value):
from jinja2.testsuite import JinjaTestCase
from jinja2 import Markup, Environment
-from jinja2._compat import text_type, UnicodeMixin
+from jinja2._compat import text_type, implements_to_string
env = Environment()
assert tmpl.render() == "['Bar', 'blah', 'foo']"
def test_sort4(self):
- class Magic(UnicodeMixin):
+ @implements_to_string
+ class Magic(object):
def __init__(self, value):
self.value = value
- def __unicode__(self):
+ def __str__(self):
return text_type(self.value)
tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''')
assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234'
except ImportError:
from dummy_thread import allocate_lock
from collections import deque
-from jinja2._compat import text_type, string_types, Iterator, PY2
+from jinja2._compat import text_type, string_types, implements_iterator, PY2
_word_split_re = re.compile(r'(\s+)')
pass
-class Cycler(Iterator):
+@implements_iterator
+class Cycler(object):
"""A cycle helper for templates."""
def __init__(self, *items):