- improved security system regarding function calls.
-- added `lipsum` function
+- added `lipsum` function to generate random text.
- strings without unicode characters are processed as binary strings now
to workaround problems with `datetime.strftime` which only accepts
- silent variable name failure is now toggleable
+- fixed issue with old-style classes not implementing `__getitem__`
+ (thanks to Axel Böhm for discovering that bug)
+
+- added a bunch of new docstrings to the Jinja classes. Makes fun now to
+ use pydoc :-)
+
Version 1.0
-----------
return '\n\n'.join(result)
+def generate_environment_doc():
+ from jinja.environment import Environment
+ return '%s\n\n%s' % (
+ inspect.getdoc(Environment),
+ inspect.getdoc(Environment.__init__)
+ )
+
e = Environment()
PYGMENTS_FORMATTER = HtmlFormatter(style='pastie', cssclass='syntax')
LIST_OF_FILTERS = generate_list_of_filters()
LIST_OF_TESTS = generate_list_of_tests()
LIST_OF_LOADERS = generate_list_of_loaders()
+ENVIRONMENT_DOC = generate_environment_doc()
FULL_TEMPLATE = e.from_string('''\
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
writer = DocumentationWriter(link_style)
data = data.replace('[[list_of_filters]]', LIST_OF_FILTERS)\
.replace('[[list_of_tests]]', LIST_OF_TESTS)\
- .replace('[[list_of_loaders]]', LIST_OF_LOADERS)
+ .replace('[[list_of_loaders]]', LIST_OF_LOADERS)\
+ .replace('[[environment_doc]]', ENVIRONMENT_DOC)
parts = publish_parts(
data,
writer=writer,
The Environment
===============
-The core component of Jinja is the `Environment`. It contains important shared
-variables like configuration, filters, tests, globals and other stuff.
-
-Here the possible initialization parameters:
-
-========================= ==================================================
-`block_start_string` * the string marking the begin of a block. this
- defaults to ``'{%'``.
-`block_end_string` * the string marking the end of a block. defaults
- to ``'%}'``.
-`variable_start_string` * the string marking the begin of a print
- statement. defaults to ``'{{'``.
-`comment_start_string` * the string marking the begin of a
- comment. defaults to ``'{#'``.
-`comment_end_string` * the string marking the end of a comment.
- defaults to ``'#}'``.
-`trim_blocks` * If this is set to ``True`` the first newline
- after a block is removed (block, not
- variable tag!). Defaults to ``False``.
-`auto_escape` If this is set to ``True`` Jinja will
- automatically escape all variables using xml
- escaping methods. If you don't want to escape a
- string you have to wrap it in a ``Markup``
- object from the ``jinja.datastructure`` module.
- If `auto_escape` is ``True`` there will be also
- a ``Markup`` object in the template namespace
- to define partial html fragments. Note that we do
- not recomment this feature, see also the comment
- below.
-`default_filters` list of tuples in the form (``filter_name``,
- ``arguments``) where ``filter_name`` is the
- name of a registered filter and ``arguments``
- a tuple with the filter arguments. The filters
- specified here will always be applied when
- printing data to the template.
- *new in jinja 1.1*
-`template_charset` The charset of the templates. Defaults
- to ``'utf-8'``.
-`charset` Charset of all string input data. Defaults
- to ``'utf-8'``.
-`namespace` Global namespace for all templates.
-`loader` Specify a template loader.
-`filters` dict of filters or the default filters if not
- defined.
-`tests` dict of tests of the default tests if not defined.
-`context_class` the context class this template should use. See
- the `context documentation`_ for more details.
-`silent` set this to `False` if you want to receive errors
- for missing template variables or attributes.
- Defaults to `False`. *new in Jinja 1.1*
-`friendly_traceback` Set this to `False` to disable the developer
- friendly traceback rewriting. Whenever an
- runtime or syntax error occours jinja will try
- to make a developer friendly traceback that shows
- the error in the template line. This however can
- be annoying when debugging broken functions which
- are called from the template. *new in Jinja 1.1*
-========================= ==================================================
-
-All of these variables except those marked with a star (*) are modifiable after
-environment initialization.
+[[environment_doc]]
The environment provides the following useful functions and properties in
addition to the initialization values:
.. _installation: installation.txt
.. _context documentation: contextenv.txt
-.. _translators: translators.txt
.. _loader: loaders.txt
+.. _translators: translators.txt
.. _filter development: filters.txt
.. _test development: tests.txt
__slots__ = ()
def __init__(self):
- try:
- Undefined
- except NameError:
- pass
- else:
- raise TypeError('cannot create %r instances' %
- self.__class__.__name__)
+ raise TypeError('cannot create %r instances' %
+ self.__class__.__name__)
- __sub__ = __mul__ = __div__ = __rsub__ = __rmul__ = __div__ = __radd__ = \
- __add__ = lambda self, other: other
+ 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 __repr__(self):
+ """The representation is ``'Undefined'``"""
+ return 'Undefined'
+
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 __eq__(self, other):
+ """`Undefined` is not equal to anything else."""
return False
def __ne__(self, other):
+ """`Undefined` is not equal to anything else."""
return True
def __call__(self, *args, **kwargs):
+ """Calling `Undefined` returns `Undefined`"""
+ return self
+
+ def __copy__(self):
+ """Return a copy."""
return self
+ def __deepcopy__(self, memo):
+ """Return a deepcopy."""
+ return self
-Undefined = UndefinedType()
+ def __reduce__(self):
+ """Helper for pickle."""
+ return 'Undefined'
+
+
+#: the singleton instance of UndefinedType
+Undefined = object.__new__(UndefinedType)
class FakeTranslator(object):
"""
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
self.cache = {}
def pop(self):
- """Pop the last layer from the stack and return it."""
+ """
+ Pop the last layer from the stack and return it.
+ """
rv = self._stack.pop()
self.current = self._stack[-1]
return rv
def push(self, data=None):
- """Push a new dict or empty layer to the stack and return that layer"""
+ """
+ Push a new dict or empty layer to the stack and return that layer
+ """
data = data or {}
self._stack.append(data)
self.current = self._stack[-1]
return data
def to_dict(self):
- """Convert the context into a dict. This skips the globals."""
+ """
+ Convert the context into a dict. This skips the globals.
+ """
result = {}
for layer in self._stack[1:]:
for key, value in layer.iteritems():
return result
def __getitem__(self, name):
+ """
+ Resolve one item. Restrict the access to internal variables
+ such as ``'::cycle1'``. Resolve deferreds.
+ """
if not name.startswith('::'):
# because the stack is usually quite small we better use [::-1]
# which is faster than reversed() somehow.
raise TemplateRuntimeError('%r is not defined' % name)
def __setitem__(self, name, value):
+ """
+ Set a variable in the outermost layer.
+ """
self.current[name] = value
def __delitem__(self, name):
+ """
+ Delete an variable in the outermost layer.
+ """
if name in self.current:
del self.current[name]
def __contains__(self, name):
+ """
+ Check if the context contains a given variable.
+ """
for layer in self._stack:
if name in layer:
return True
return False
def __repr__(self):
- tmp = {}
- for d in self._stack:
- for key, value in d.iteritems():
- tmp[key] = value
- return 'Context(%s)' % repr(tmp)
+ """
+ String representation of the context.
+ """
+ return 'Context(%r)' % self.to_dict()
class LoopContext(object):
"""
def __init__(self, generator):
- self._generator = generator
+ self._next = generator.next
self._pushed = []
self.last = (1, 'initial', '')
def __iter__(self):
+ """Return self in order to mark this is iterator."""
return self
def __nonzero__(self):
if self._pushed:
rv = self._pushed.pop()
else:
- rv = self._generator.next()
+ rv = self._next()
self.last = rv
return rv
watch_changes
+__all__ = ['DEFAULT_FILTERS', 'DEFAULT_TESTS', 'DEFAULT_NAMESPACE']
+
+
DEFAULT_NAMESPACE = {
'range': safe_range,
'debug': debug_context,
from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
+__all__ = ['Environment']
+
+
class Environment(object):
"""
The jinja environment.
+
+ The core component of Jinja is the `Environment`. It contains
+ important shared variables like configuration, filters, tests,
+ globals and others.
"""
def __init__(self,
context_class=Context,
silent=True,
friendly_traceback=True):
+ """
+ Here the possible initialization parameters:
+
+ ========================= ============================================
+ `block_start_string` * the string marking the begin of a block.
+ this defaults to ``'{%'``.
+ `block_end_string` * the string marking the end of a block.
+ defaults to ``'%}'``.
+ `variable_start_string` * the string marking the begin of a print
+ statement. defaults to ``'{{'``.
+ `comment_start_string` * the string marking the begin of a
+ comment. defaults to ``'{#'``.
+ `comment_end_string` * the string marking the end of a comment.
+ defaults to ``'#}'``.
+ `trim_blocks` * If this is set to ``True`` the first newline
+ after a block is removed (block, not
+ variable tag!). Defaults to ``False``.
+ `auto_escape` If this is set to ``True`` Jinja will
+ automatically escape all variables using xml
+ escaping methods. If you don't want to
+ escape a string you have to wrap it in a
+ ``Markup`` object from the
+ ``jinja.datastructure`` module. If
+ `auto_escape` is ``True`` there will be also
+ a ``Markup`` object in the template
+ namespace to define partial html fragments.
+ Note that we do not recomment this feature.
+ `default_filters` list of tuples in the form (``filter_name``,
+ ``arguments``) where ``filter_name`` is the
+ name of a registered filter and
+ ``arguments`` a tuple with the filter
+ arguments. The filters specified here will
+ always be applied when printing data to the
+ template. *new in Jinja 1.1*
+ `template_charset` The charset of the templates. Defaults
+ to ``'utf-8'``.
+ `charset` Charset of all string input data. Defaults
+ to ``'utf-8'``.
+ `namespace` Global namespace for all templates.
+ `loader` Specify a template loader.
+ `filters` dict of filters or the default filters if
+ not defined.
+ `tests` dict of tests of the default tests if not
+ defined.
+ `context_class` the context class this template should use.
+ See the `Context` documentation for more
+ details.
+ `silent` set this to `False` if you want to receive
+ errors for missing template variables or
+ attributes. Defaults to `False`. *new in
+ Jinja 1.1*
+ `friendly_traceback` Set this to `False` to disable the developer
+ friendly traceback rewriting. Whenever an
+ runtime or syntax error occours jinja will
+ try to make a developer friendly traceback
+ that shows the error in the template line.
+ This however can be annoying when debugging
+ broken functions that are called from the
+ template. *new in Jinja 1.1*
+ ========================= ============================================
+
+ All of these variables except those marked with a star (*) are
+ modifiable after environment initialization.
+ """
# lexer / parser information
self.block_start_string = block_start_string
Get or set the template loader.
"""
self._loader = LoaderWrapper(self, value)
- loader = property(lambda s: s._loader, loader, loader.__doc__)
+ loader = property(lambda s: s._loader, loader, doc=loader.__doc__)
def parse(self, source, filename=None):
- """Function that creates a new parser and parses the source."""
+ """
+ Parse the sourcecode and return the abstract syntax tree. This tree
+ of nodes is used by the `translators`_ to convert the template into
+ executable source- or bytecode.
+
+ .. _translators: translators.txt
+ """
parser = Parser(self, source, filename)
return parser.parse()
def from_string(self, source):
- """Load a template from a string."""
+ """
+ Load and parse a template source and translate it into eval-able
+ Python code. This code is wrapped within a `Template` class that
+ allows you to render it.
+ """
from jinja.parser import Parser
from jinja.translators.python import PythonTranslator
try:
return rv
def get_template(self, filename):
- """Load a template from a filename. Only works
- if a proper loader is set."""
+ """
+ Load a template from a loader. If the template does not exist, you
+ will get a `TemplateNotFound` exception.
+ """
return self._loader.load(filename)
def to_unicode(self, value):
try:
return iter(seq).next()
except StopIteration:
- return Undefined
+ if env.silent:
+ return Undefined
+ raise TemplateRuntimeError('%r is empty' % seq)
return wrapped
def wrapped(env, context, seq):
try:
return iter(_reversed(seq)).next()
- except (TypeError, StopIteration):
- return Undefined
+ except StopIteration:
+ if env.silent:
+ return Undefined
+ raise TemplateRuntimeError('%r is empty' % seq)
return wrapped
def wrapped(env, context, seq):
try:
return choice(seq)
- except:
- return Undefined
+ except IndexError:
+ if env.silent:
+ return Undefined
+ raise TemplateRuntimeError('%r is empty' % seq)
return wrapped
jinja.lexer
~~~~~~~~~~~
+ This module implements a Jinja / Python combination lexer. The
+ `Lexer` class provided by this module is used to do some preprocessing
+ for Jinja.
+
+ On the one hand it filters out invalid operators like the bitshift
+ operators we don't allow in templates. On the other hand it separates
+ template code and python code in expressions.
+
+ Because of some limitations in the compiler package which are just
+ natural but annoying for Jinja, the lexer also "escapes" non names that
+ are not keywords. The Jinja parser then removes those escaping marks
+ again.
+
+ This is required in order to make "class" and some other python keywords
+ we don't use valid identifiers.
+
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from sets import Set as set
+__all__ = ['Lexer', 'Failure', 'keywords']
+
+
# static regular expressions
whitespace_re = re.compile(r'\s+(?m)')
name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
Additionally non keywords are escaped.
"""
- def filter():
+ def generate():
for lineno, token, value in self.tokeniter(source):
if token == 'name' and value not in keywords:
value += '_'
yield lineno, token, value
- return TokenStream(filter())
+ return TokenStream(generate())
def tokeniter(self, source):
"""
parser uses the `tokenize` function with returns a `TokenStream` with
some escaped tokens.
"""
- source = type(source)('\n').join(source.splitlines())
+ source = '\n'.join(source.splitlines())
pos = 0
lineno = 1
stack = ['root']
jinja.nodes
~~~~~~~~~~~
- Additional nodes for Jinja. Look like nodes from the ast.
+ This module implements additional nodes derived from the ast base node.
+
+ It also provides some node tree helper functions like `in_lineno` and
+ `get_nodes` used by the parser and translator in order to normalize
+ python and jinja nodes.
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
Implements the template parser.
+ The Jinja template parser is not a real parser but a combination of the
+ python compiler package and some postprocessing. The tokens yielded by
+ the lexer are used to separate template data and expressions. The
+ expression tokens are then converted into strings again and processed
+ by the python parser.
+
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from sets import Set as set
+__all__ = ['Parser']
+
+
# callback functions for the subparse method
end_of_block = lambda p, t, d: t == 'block_end'
end_of_variable = lambda p, t, d: t == 'variable_end'
end_of_trans = lambda p, t, d: t == 'name' and d == 'endtrans'
-string_inc_re = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+')
-
-
-def inc_string(s):
- """
- Increment a string
- """
- m = string_inc_re.search(s)
- if m:
- next = str(int(m.group(1)) + 1)
- start, end = m.span(1)
- s = s[:max(end - len(next), start)] + next + s[end:]
- else:
- name, ext = s.rsplit('.', 1)
- return '%s2.%s' % (name, ext)
- return s
-
-
class Parser(object):
"""
The template parser class.
self.filename = filename
self.tokenstream = environment.lexer.tokenize(source)
+ #: if this template has a parent template it's stored here
+ #: after parsing
self.extends = None
+ #: set for blocks in order to keep them unique
self.blocks = set()
self.directives = {
jinja.translators
~~~~~~~~~~~~~~~~~
- The submodules of this module provide translators for the jinja ast
- which basically just is the python ast with a few more nodes.
+ The submodules of this module provide translators for the jinja ast.
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
This module translates a jinja ast into python code.
+ This translator tries hard to keep Jinja sandboxed. All security
+ relevant calls are wrapped by methods defined in the environment.
+ This affects:
+
+ - method calls
+ - attribute access
+ - name resolution
+
+ It also adds debug symbols used by the traceback toolkit implemented
+ in `jinja.utils`.
+
:copyright: 2007 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
self.environment = environment
self.node = node
+ #: mapping to constants in the environment that are not
+ #: converted to context lookups. this should improve
+ #: performance and avoid accidentally screwing things up
+ #: by rebinding essential constants.
self.constants = {
'true': 'True',
'false': 'False',
'undefined': 'Undefined'
}
+ #: bind the nodes to the callback functions. There are
+ #: some missing! A few are specified in the `unhandled`
+ #: mapping in order to disallow their usage, some of them
+ #: will not appear in the jinja parser output because
+ #: they are filtered out.
self.handlers = {
# jinja nodes
nodes.Template: self.handle_template,
nodes.Block: self.handle_block,
nodes.Include: self.handle_include,
nodes.Trans: self.handle_trans,
- # used python nodes
+ # python nodes
ast.Name: self.handle_name,
ast.AssName: self.handle_name,
ast.Compare: self.handle_compare,
ast.Sliceobj: self.handle_sliceobj
}
+ #: mapping of unsupported syntax elements.
+ #: the value represents the feature name that appears
+ #: in the exception.
self.unsupported = {
ast.ListComp: 'list comprehensions',
ast.From: 'imports',
ast.Import: 'imports',
}
+
+ #: because of python2.3 compatibility add generator
+ #: expressions only to the list of unused features
+ #: if it exists.
if hasattr(ast, 'GenExpr'):
self.unsupported.update({
ast.GenExpr: 'generator expressions'
})
- self.require_translations = False
-
# -- public methods
def process(environment, node):
+ """
+ The only public method. Creates a translator instance,
+ translates the code and returns it in form of an
+ `Template` instance.
+ """
translator = PythonTranslator(environment, node)
filename = node.filename or '<template>'
source = translator.translate()
- return Template(environment,
- compile(source, filename, 'exec'))
+ return Template(environment, compile(source, filename, 'exec'))
process = staticmethod(process)
# -- private helper methods
def indent(self, text):
"""
- Indent the current text.
+ Indent the current text. This does only indent the
+ first line.
"""
return (' ' * (self.indention * 4)) + text
def handle_node(self, node):
"""
- Handle one node
+ Handle one node. Resolves the correct callback functions defined
+ in the callback mapping. This also raises the `TemplateSyntaxError`
+ for unsupported syntax elements such as list comprehensions.
"""
if node.__class__ in self.handlers:
out = self.handlers[node.__class__](node)
return out
def reset(self):
+ """
+ Reset translation variables such as indention, cycle id
+ or the require_translations flag.
+ """
self.indention = 0
self.last_cycle_id = 0
+ self.require_translations = False
def translate(self):
+ """
+ Translate the node defined in the constructor after
+ resetting the translator.
+ """
self.reset()
return self.handle_node(self.node)
str(name),
_to_tuple(tmp)
))
+
+ # blocks must always be defined. even if it's empty. some
+ # features depend on it
lines.append('\nblocks = {\n%s\n}' % ',\n'.join(dict_lines))
# now get the real source lines and map the debugging symbols
# simple loops
else:
- write('context[\'loop\'] = loop = LoopContext(%s, context[\'loop\'], None)' %
- self.handle_node(node.seq))
+ write('context[\'loop\'] = loop = LoopContext(%s, '
+ 'context[\'loop\'], None)' % self.handle_node(node.seq))
write('for %s in loop:' %
self.handle_node(node.item)
)
self.indention += 1
write('yield None')
self.indention -= 2
- write('context[\'loop\'] = LoopContext(None, context[\'loop\'], buffereater(forloop))')
+ write('context[\'loop\'] = LoopContext(None, context[\'loop\'], '
+ 'buffereater(forloop))')
write('for item in forloop(%s):' % self.handle_node(node.seq))
self.indention += 1
write('yield item')
self.indention -= 1
if hardcoded:
- write('yield finish_var(context.current[%r].cycle(), context)' % name)
+ write('yield finish_var(context.current[%r].cycle(), '
+ 'context)' % name)
else:
- write('yield finish_var(context.current[%r].cycle(%s), context)' % (
+ write('yield finish_var(context.current[%r].cycle(%s), '
+ 'context)' % (
name,
self.handle_node(node.seq)
))
write('argcount = len(args)')
tmp = []
for idx, (name, n) in enumerate(node.arguments):
- tmp.append('\'%s\': (argcount > %d and (args[%d],) or (%s,))[0]' % (
+ tmp.append('\'%s\': (argcount > %d and (args[%d],) '
+ 'or (%s,))[0]' % (
name,
idx,
idx,
self.indention += 1
write('yield False')
self.indention -= 2
- buf.append(self.indent('context[%r] = buffereater(macro)' % node.name))
+ buf.append(self.indent('context[%r] = buffereater(macro)' %
+ node.name))
return '\n'.join(buf)
self.indention += 1
write('yield None')
self.indention -= 2
- write('yield %s' % self.filter('u\'\'.join(filtered())', node.filters))
+ write('yield %s' % self.filter('u\'\'.join(filtered())',
+ node.filters))
return '\n'.join(buf)
def handle_block(self, node, level=0):
else:
args.append(self.handle_node(arg))
if not (args or kwargs or star_args or dstar_args):
- return 'call_function_simple(%s, context)' % self.handle_node(node.node)
+ return 'call_function_simple(%s, context)' % \
+ self.handle_node(node.node)
return 'call_function(%s, context, %s, {%s}, %s, %s)' % (
self.handle_node(node.node),
_to_tuple(args),
from jinja.datastructure import Context, TemplateData
from jinja.exceptions import SecurityException, TemplateNotFound
+#: the python2.4 version of deque is missing the remove method
+#: because a for loop with a lookup for the missing value written
+#: in python is slower we just use deque if we have python2.5 or higher
if sys.version_info >= (2, 5):
from collections import deque
else:
def collect_translations(ast):
"""
- Collect all translatable strings for the given ast.
+ Collect all translatable strings for the given ast. The
+ return value is a list of tuples in the form ``(lineno, singular,
+ plural)``. If a translation doesn't require a plural form the
+ third item is `None`.
"""
todo = [ast]
result = []
self._append = self._queue.append
def copy(self):
+ """
+ Return an shallow copy of the instance.
+ """
rv = CacheDict(self.capacity)
rv._mapping.update(self._mapping)
rv._queue = self._queue[:]
return rv
def get(self, key, default=None):
+ """
+ Return an item from the cache dict or `default`
+ """
if key in self:
return self[key]
return default
def setdefault(self, key, default=None):
+ """
+ Set `default` if the key is not in the cache otherwise
+ leave unchanged. Return the value of this key.
+ """
if key in self:
return self[key]
self[key] = default
return default
def clear(self):
+ """
+ Clear the cache dict.
+ """
self._mapping.clear()
try:
self._queue.clear()
del self._queue[:]
def __contains__(self, key):
+ """
+ Check if a key exists in this cache dict.
+ """
return key in self._mapping
def __len__(self):
+ """
+ Return the current size of the cache dict.
+ """
return len(self._mapping)
def __repr__(self):
)
def __getitem__(self, key):
+ """
+ Get an item from the cache dict. Moves the item up so that
+ it has the highest priority then.
+
+ Raise an `KeyError` if it does not exist.
+ """
rv = self._mapping[key]
if self._queue[-1] != key:
self._remove(key)
return rv
def __setitem__(self, key, value):
+ """
+ Sets the value for an item. Moves the item up so that it
+ has the highest priority then.
+ """
if key in self._mapping:
self._remove(key)
elif len(self._mapping) == self.capacity:
self._mapping[key] = value
def __delitem__(self, key):
+ """
+ Remove an item from the cache dict.
+ Raise an `KeyError` if it does not exist.
+ """
del self._mapping[key]
self._remove(key)
def __iter__(self):
+ """
+ Iterate over all values in the cache dict, ordered by
+ the most recent usage.
+ """
try:
return reversed(self._queue)
except NameError:
return iter(self._queue[::-1])
def __reversed__(self):
+ """
+ Iterate over the values in the cache dict, oldest items
+ coming first.
+ """
return iter(self._queue)
__copy__ = copy
+
+ def __deepcopy__(self):
+ """
+ Return a deep copy of the cache dict.
+ """
+ from copy import deepcopy
+ rv = CacheDict(self.capacity)
+ rv._mapping = deepcopy(self._mapping)
+ rv._queue = deepcopy(self._queue)
+ return rv