xref: /aosp_15_r20/external/libchrome/third_party/jinja2/bccache.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2*635a8641SAndroid Build Coastguard Worker"""
3*635a8641SAndroid Build Coastguard Worker    jinja2.bccache
4*635a8641SAndroid Build Coastguard Worker    ~~~~~~~~~~~~~~
5*635a8641SAndroid Build Coastguard Worker
6*635a8641SAndroid Build Coastguard Worker    This module implements the bytecode cache system Jinja is optionally
7*635a8641SAndroid Build Coastguard Worker    using.  This is useful if you have very complex template situations and
8*635a8641SAndroid Build Coastguard Worker    the compiliation of all those templates slow down your application too
9*635a8641SAndroid Build Coastguard Worker    much.
10*635a8641SAndroid Build Coastguard Worker
11*635a8641SAndroid Build Coastguard Worker    Situations where this is useful are often forking web applications that
12*635a8641SAndroid Build Coastguard Worker    are initialized on the first request.
13*635a8641SAndroid Build Coastguard Worker
14*635a8641SAndroid Build Coastguard Worker    :copyright: (c) 2017 by the Jinja Team.
15*635a8641SAndroid Build Coastguard Worker    :license: BSD.
16*635a8641SAndroid Build Coastguard Worker"""
17*635a8641SAndroid Build Coastguard Workerfrom os import path, listdir
18*635a8641SAndroid Build Coastguard Workerimport os
19*635a8641SAndroid Build Coastguard Workerimport sys
20*635a8641SAndroid Build Coastguard Workerimport stat
21*635a8641SAndroid Build Coastguard Workerimport errno
22*635a8641SAndroid Build Coastguard Workerimport marshal
23*635a8641SAndroid Build Coastguard Workerimport tempfile
24*635a8641SAndroid Build Coastguard Workerimport fnmatch
25*635a8641SAndroid Build Coastguard Workerfrom hashlib import sha1
26*635a8641SAndroid Build Coastguard Workerfrom jinja2.utils import open_if_exists
27*635a8641SAndroid Build Coastguard Workerfrom jinja2._compat import BytesIO, pickle, PY2, text_type
28*635a8641SAndroid Build Coastguard Worker
29*635a8641SAndroid Build Coastguard Worker
30*635a8641SAndroid Build Coastguard Worker# marshal works better on 3.x, one hack less required
31*635a8641SAndroid Build Coastguard Workerif not PY2:
32*635a8641SAndroid Build Coastguard Worker    marshal_dump = marshal.dump
33*635a8641SAndroid Build Coastguard Worker    marshal_load = marshal.load
34*635a8641SAndroid Build Coastguard Workerelse:
35*635a8641SAndroid Build Coastguard Worker
36*635a8641SAndroid Build Coastguard Worker    def marshal_dump(code, f):
37*635a8641SAndroid Build Coastguard Worker        if isinstance(f, file):
38*635a8641SAndroid Build Coastguard Worker            marshal.dump(code, f)
39*635a8641SAndroid Build Coastguard Worker        else:
40*635a8641SAndroid Build Coastguard Worker            f.write(marshal.dumps(code))
41*635a8641SAndroid Build Coastguard Worker
42*635a8641SAndroid Build Coastguard Worker    def marshal_load(f):
43*635a8641SAndroid Build Coastguard Worker        if isinstance(f, file):
44*635a8641SAndroid Build Coastguard Worker            return marshal.load(f)
45*635a8641SAndroid Build Coastguard Worker        return marshal.loads(f.read())
46*635a8641SAndroid Build Coastguard Worker
47*635a8641SAndroid Build Coastguard Worker
48*635a8641SAndroid Build Coastguard Workerbc_version = 3
49*635a8641SAndroid Build Coastguard Worker
50*635a8641SAndroid Build Coastguard Worker# magic version used to only change with new jinja versions.  With 2.6
51*635a8641SAndroid Build Coastguard Worker# we change this to also take Python version changes into account.  The
52*635a8641SAndroid Build Coastguard Worker# reason for this is that Python tends to segfault if fed earlier bytecode
53*635a8641SAndroid Build Coastguard Worker# versions because someone thought it would be a good idea to reuse opcodes
54*635a8641SAndroid Build Coastguard Worker# or make Python incompatible with earlier versions.
55*635a8641SAndroid Build Coastguard Workerbc_magic = 'j2'.encode('ascii') + \
56*635a8641SAndroid Build Coastguard Worker    pickle.dumps(bc_version, 2) + \
57*635a8641SAndroid Build Coastguard Worker    pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
58*635a8641SAndroid Build Coastguard Worker
59*635a8641SAndroid Build Coastguard Worker
60*635a8641SAndroid Build Coastguard Workerclass Bucket(object):
61*635a8641SAndroid Build Coastguard Worker    """Buckets are used to store the bytecode for one template.  It's created
62*635a8641SAndroid Build Coastguard Worker    and initialized by the bytecode cache and passed to the loading functions.
63*635a8641SAndroid Build Coastguard Worker
64*635a8641SAndroid Build Coastguard Worker    The buckets get an internal checksum from the cache assigned and use this
65*635a8641SAndroid Build Coastguard Worker    to automatically reject outdated cache material.  Individual bytecode
66*635a8641SAndroid Build Coastguard Worker    cache subclasses don't have to care about cache invalidation.
67*635a8641SAndroid Build Coastguard Worker    """
68*635a8641SAndroid Build Coastguard Worker
69*635a8641SAndroid Build Coastguard Worker    def __init__(self, environment, key, checksum):
70*635a8641SAndroid Build Coastguard Worker        self.environment = environment
71*635a8641SAndroid Build Coastguard Worker        self.key = key
72*635a8641SAndroid Build Coastguard Worker        self.checksum = checksum
73*635a8641SAndroid Build Coastguard Worker        self.reset()
74*635a8641SAndroid Build Coastguard Worker
75*635a8641SAndroid Build Coastguard Worker    def reset(self):
76*635a8641SAndroid Build Coastguard Worker        """Resets the bucket (unloads the bytecode)."""
77*635a8641SAndroid Build Coastguard Worker        self.code = None
78*635a8641SAndroid Build Coastguard Worker
79*635a8641SAndroid Build Coastguard Worker    def load_bytecode(self, f):
80*635a8641SAndroid Build Coastguard Worker        """Loads bytecode from a file or file like object."""
81*635a8641SAndroid Build Coastguard Worker        # make sure the magic header is correct
82*635a8641SAndroid Build Coastguard Worker        magic = f.read(len(bc_magic))
83*635a8641SAndroid Build Coastguard Worker        if magic != bc_magic:
84*635a8641SAndroid Build Coastguard Worker            self.reset()
85*635a8641SAndroid Build Coastguard Worker            return
86*635a8641SAndroid Build Coastguard Worker        # the source code of the file changed, we need to reload
87*635a8641SAndroid Build Coastguard Worker        checksum = pickle.load(f)
88*635a8641SAndroid Build Coastguard Worker        if self.checksum != checksum:
89*635a8641SAndroid Build Coastguard Worker            self.reset()
90*635a8641SAndroid Build Coastguard Worker            return
91*635a8641SAndroid Build Coastguard Worker        # if marshal_load fails then we need to reload
92*635a8641SAndroid Build Coastguard Worker        try:
93*635a8641SAndroid Build Coastguard Worker            self.code = marshal_load(f)
94*635a8641SAndroid Build Coastguard Worker        except (EOFError, ValueError, TypeError):
95*635a8641SAndroid Build Coastguard Worker            self.reset()
96*635a8641SAndroid Build Coastguard Worker            return
97*635a8641SAndroid Build Coastguard Worker
98*635a8641SAndroid Build Coastguard Worker    def write_bytecode(self, f):
99*635a8641SAndroid Build Coastguard Worker        """Dump the bytecode into the file or file like object passed."""
100*635a8641SAndroid Build Coastguard Worker        if self.code is None:
101*635a8641SAndroid Build Coastguard Worker            raise TypeError('can\'t write empty bucket')
102*635a8641SAndroid Build Coastguard Worker        f.write(bc_magic)
103*635a8641SAndroid Build Coastguard Worker        pickle.dump(self.checksum, f, 2)
104*635a8641SAndroid Build Coastguard Worker        marshal_dump(self.code, f)
105*635a8641SAndroid Build Coastguard Worker
106*635a8641SAndroid Build Coastguard Worker    def bytecode_from_string(self, string):
107*635a8641SAndroid Build Coastguard Worker        """Load bytecode from a string."""
108*635a8641SAndroid Build Coastguard Worker        self.load_bytecode(BytesIO(string))
109*635a8641SAndroid Build Coastguard Worker
110*635a8641SAndroid Build Coastguard Worker    def bytecode_to_string(self):
111*635a8641SAndroid Build Coastguard Worker        """Return the bytecode as string."""
112*635a8641SAndroid Build Coastguard Worker        out = BytesIO()
113*635a8641SAndroid Build Coastguard Worker        self.write_bytecode(out)
114*635a8641SAndroid Build Coastguard Worker        return out.getvalue()
115*635a8641SAndroid Build Coastguard Worker
116*635a8641SAndroid Build Coastguard Worker
117*635a8641SAndroid Build Coastguard Workerclass BytecodeCache(object):
118*635a8641SAndroid Build Coastguard Worker    """To implement your own bytecode cache you have to subclass this class
119*635a8641SAndroid Build Coastguard Worker    and override :meth:`load_bytecode` and :meth:`dump_bytecode`.  Both of
120*635a8641SAndroid Build Coastguard Worker    these methods are passed a :class:`~jinja2.bccache.Bucket`.
121*635a8641SAndroid Build Coastguard Worker
122*635a8641SAndroid Build Coastguard Worker    A very basic bytecode cache that saves the bytecode on the file system::
123*635a8641SAndroid Build Coastguard Worker
124*635a8641SAndroid Build Coastguard Worker        from os import path
125*635a8641SAndroid Build Coastguard Worker
126*635a8641SAndroid Build Coastguard Worker        class MyCache(BytecodeCache):
127*635a8641SAndroid Build Coastguard Worker
128*635a8641SAndroid Build Coastguard Worker            def __init__(self, directory):
129*635a8641SAndroid Build Coastguard Worker                self.directory = directory
130*635a8641SAndroid Build Coastguard Worker
131*635a8641SAndroid Build Coastguard Worker            def load_bytecode(self, bucket):
132*635a8641SAndroid Build Coastguard Worker                filename = path.join(self.directory, bucket.key)
133*635a8641SAndroid Build Coastguard Worker                if path.exists(filename):
134*635a8641SAndroid Build Coastguard Worker                    with open(filename, 'rb') as f:
135*635a8641SAndroid Build Coastguard Worker                        bucket.load_bytecode(f)
136*635a8641SAndroid Build Coastguard Worker
137*635a8641SAndroid Build Coastguard Worker            def dump_bytecode(self, bucket):
138*635a8641SAndroid Build Coastguard Worker                filename = path.join(self.directory, bucket.key)
139*635a8641SAndroid Build Coastguard Worker                with open(filename, 'wb') as f:
140*635a8641SAndroid Build Coastguard Worker                    bucket.write_bytecode(f)
141*635a8641SAndroid Build Coastguard Worker
142*635a8641SAndroid Build Coastguard Worker    A more advanced version of a filesystem based bytecode cache is part of
143*635a8641SAndroid Build Coastguard Worker    Jinja2.
144*635a8641SAndroid Build Coastguard Worker    """
145*635a8641SAndroid Build Coastguard Worker
146*635a8641SAndroid Build Coastguard Worker    def load_bytecode(self, bucket):
147*635a8641SAndroid Build Coastguard Worker        """Subclasses have to override this method to load bytecode into a
148*635a8641SAndroid Build Coastguard Worker        bucket.  If they are not able to find code in the cache for the
149*635a8641SAndroid Build Coastguard Worker        bucket, it must not do anything.
150*635a8641SAndroid Build Coastguard Worker        """
151*635a8641SAndroid Build Coastguard Worker        raise NotImplementedError()
152*635a8641SAndroid Build Coastguard Worker
153*635a8641SAndroid Build Coastguard Worker    def dump_bytecode(self, bucket):
154*635a8641SAndroid Build Coastguard Worker        """Subclasses have to override this method to write the bytecode
155*635a8641SAndroid Build Coastguard Worker        from a bucket back to the cache.  If it unable to do so it must not
156*635a8641SAndroid Build Coastguard Worker        fail silently but raise an exception.
157*635a8641SAndroid Build Coastguard Worker        """
158*635a8641SAndroid Build Coastguard Worker        raise NotImplementedError()
159*635a8641SAndroid Build Coastguard Worker
160*635a8641SAndroid Build Coastguard Worker    def clear(self):
161*635a8641SAndroid Build Coastguard Worker        """Clears the cache.  This method is not used by Jinja2 but should be
162*635a8641SAndroid Build Coastguard Worker        implemented to allow applications to clear the bytecode cache used
163*635a8641SAndroid Build Coastguard Worker        by a particular environment.
164*635a8641SAndroid Build Coastguard Worker        """
165*635a8641SAndroid Build Coastguard Worker
166*635a8641SAndroid Build Coastguard Worker    def get_cache_key(self, name, filename=None):
167*635a8641SAndroid Build Coastguard Worker        """Returns the unique hash key for this template name."""
168*635a8641SAndroid Build Coastguard Worker        hash = sha1(name.encode('utf-8'))
169*635a8641SAndroid Build Coastguard Worker        if filename is not None:
170*635a8641SAndroid Build Coastguard Worker            filename = '|' + filename
171*635a8641SAndroid Build Coastguard Worker            if isinstance(filename, text_type):
172*635a8641SAndroid Build Coastguard Worker                filename = filename.encode('utf-8')
173*635a8641SAndroid Build Coastguard Worker            hash.update(filename)
174*635a8641SAndroid Build Coastguard Worker        return hash.hexdigest()
175*635a8641SAndroid Build Coastguard Worker
176*635a8641SAndroid Build Coastguard Worker    def get_source_checksum(self, source):
177*635a8641SAndroid Build Coastguard Worker        """Returns a checksum for the source."""
178*635a8641SAndroid Build Coastguard Worker        return sha1(source.encode('utf-8')).hexdigest()
179*635a8641SAndroid Build Coastguard Worker
180*635a8641SAndroid Build Coastguard Worker    def get_bucket(self, environment, name, filename, source):
181*635a8641SAndroid Build Coastguard Worker        """Return a cache bucket for the given template.  All arguments are
182*635a8641SAndroid Build Coastguard Worker        mandatory but filename may be `None`.
183*635a8641SAndroid Build Coastguard Worker        """
184*635a8641SAndroid Build Coastguard Worker        key = self.get_cache_key(name, filename)
185*635a8641SAndroid Build Coastguard Worker        checksum = self.get_source_checksum(source)
186*635a8641SAndroid Build Coastguard Worker        bucket = Bucket(environment, key, checksum)
187*635a8641SAndroid Build Coastguard Worker        self.load_bytecode(bucket)
188*635a8641SAndroid Build Coastguard Worker        return bucket
189*635a8641SAndroid Build Coastguard Worker
190*635a8641SAndroid Build Coastguard Worker    def set_bucket(self, bucket):
191*635a8641SAndroid Build Coastguard Worker        """Put the bucket into the cache."""
192*635a8641SAndroid Build Coastguard Worker        self.dump_bytecode(bucket)
193*635a8641SAndroid Build Coastguard Worker
194*635a8641SAndroid Build Coastguard Worker
195*635a8641SAndroid Build Coastguard Workerclass FileSystemBytecodeCache(BytecodeCache):
196*635a8641SAndroid Build Coastguard Worker    """A bytecode cache that stores bytecode on the filesystem.  It accepts
197*635a8641SAndroid Build Coastguard Worker    two arguments: The directory where the cache items are stored and a
198*635a8641SAndroid Build Coastguard Worker    pattern string that is used to build the filename.
199*635a8641SAndroid Build Coastguard Worker
200*635a8641SAndroid Build Coastguard Worker    If no directory is specified a default cache directory is selected.  On
201*635a8641SAndroid Build Coastguard Worker    Windows the user's temp directory is used, on UNIX systems a directory
202*635a8641SAndroid Build Coastguard Worker    is created for the user in the system temp directory.
203*635a8641SAndroid Build Coastguard Worker
204*635a8641SAndroid Build Coastguard Worker    The pattern can be used to have multiple separate caches operate on the
205*635a8641SAndroid Build Coastguard Worker    same directory.  The default pattern is ``'__jinja2_%s.cache'``.  ``%s``
206*635a8641SAndroid Build Coastguard Worker    is replaced with the cache key.
207*635a8641SAndroid Build Coastguard Worker
208*635a8641SAndroid Build Coastguard Worker    >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
209*635a8641SAndroid Build Coastguard Worker
210*635a8641SAndroid Build Coastguard Worker    This bytecode cache supports clearing of the cache using the clear method.
211*635a8641SAndroid Build Coastguard Worker    """
212*635a8641SAndroid Build Coastguard Worker
213*635a8641SAndroid Build Coastguard Worker    def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
214*635a8641SAndroid Build Coastguard Worker        if directory is None:
215*635a8641SAndroid Build Coastguard Worker            directory = self._get_default_cache_dir()
216*635a8641SAndroid Build Coastguard Worker        self.directory = directory
217*635a8641SAndroid Build Coastguard Worker        self.pattern = pattern
218*635a8641SAndroid Build Coastguard Worker
219*635a8641SAndroid Build Coastguard Worker    def _get_default_cache_dir(self):
220*635a8641SAndroid Build Coastguard Worker        def _unsafe_dir():
221*635a8641SAndroid Build Coastguard Worker            raise RuntimeError('Cannot determine safe temp directory.  You '
222*635a8641SAndroid Build Coastguard Worker                               'need to explicitly provide one.')
223*635a8641SAndroid Build Coastguard Worker
224*635a8641SAndroid Build Coastguard Worker        tmpdir = tempfile.gettempdir()
225*635a8641SAndroid Build Coastguard Worker
226*635a8641SAndroid Build Coastguard Worker        # On windows the temporary directory is used specific unless
227*635a8641SAndroid Build Coastguard Worker        # explicitly forced otherwise.  We can just use that.
228*635a8641SAndroid Build Coastguard Worker        if os.name == 'nt':
229*635a8641SAndroid Build Coastguard Worker            return tmpdir
230*635a8641SAndroid Build Coastguard Worker        if not hasattr(os, 'getuid'):
231*635a8641SAndroid Build Coastguard Worker            _unsafe_dir()
232*635a8641SAndroid Build Coastguard Worker
233*635a8641SAndroid Build Coastguard Worker        dirname = '_jinja2-cache-%d' % os.getuid()
234*635a8641SAndroid Build Coastguard Worker        actual_dir = os.path.join(tmpdir, dirname)
235*635a8641SAndroid Build Coastguard Worker
236*635a8641SAndroid Build Coastguard Worker        try:
237*635a8641SAndroid Build Coastguard Worker            os.mkdir(actual_dir, stat.S_IRWXU)
238*635a8641SAndroid Build Coastguard Worker        except OSError as e:
239*635a8641SAndroid Build Coastguard Worker            if e.errno != errno.EEXIST:
240*635a8641SAndroid Build Coastguard Worker                raise
241*635a8641SAndroid Build Coastguard Worker        try:
242*635a8641SAndroid Build Coastguard Worker            os.chmod(actual_dir, stat.S_IRWXU)
243*635a8641SAndroid Build Coastguard Worker            actual_dir_stat = os.lstat(actual_dir)
244*635a8641SAndroid Build Coastguard Worker            if actual_dir_stat.st_uid != os.getuid() \
245*635a8641SAndroid Build Coastguard Worker               or not stat.S_ISDIR(actual_dir_stat.st_mode) \
246*635a8641SAndroid Build Coastguard Worker               or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU:
247*635a8641SAndroid Build Coastguard Worker                _unsafe_dir()
248*635a8641SAndroid Build Coastguard Worker        except OSError as e:
249*635a8641SAndroid Build Coastguard Worker            if e.errno != errno.EEXIST:
250*635a8641SAndroid Build Coastguard Worker                raise
251*635a8641SAndroid Build Coastguard Worker
252*635a8641SAndroid Build Coastguard Worker        actual_dir_stat = os.lstat(actual_dir)
253*635a8641SAndroid Build Coastguard Worker        if actual_dir_stat.st_uid != os.getuid() \
254*635a8641SAndroid Build Coastguard Worker           or not stat.S_ISDIR(actual_dir_stat.st_mode) \
255*635a8641SAndroid Build Coastguard Worker           or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU:
256*635a8641SAndroid Build Coastguard Worker            _unsafe_dir()
257*635a8641SAndroid Build Coastguard Worker
258*635a8641SAndroid Build Coastguard Worker        return actual_dir
259*635a8641SAndroid Build Coastguard Worker
260*635a8641SAndroid Build Coastguard Worker    def _get_cache_filename(self, bucket):
261*635a8641SAndroid Build Coastguard Worker        return path.join(self.directory, self.pattern % bucket.key)
262*635a8641SAndroid Build Coastguard Worker
263*635a8641SAndroid Build Coastguard Worker    def load_bytecode(self, bucket):
264*635a8641SAndroid Build Coastguard Worker        f = open_if_exists(self._get_cache_filename(bucket), 'rb')
265*635a8641SAndroid Build Coastguard Worker        if f is not None:
266*635a8641SAndroid Build Coastguard Worker            try:
267*635a8641SAndroid Build Coastguard Worker                bucket.load_bytecode(f)
268*635a8641SAndroid Build Coastguard Worker            finally:
269*635a8641SAndroid Build Coastguard Worker                f.close()
270*635a8641SAndroid Build Coastguard Worker
271*635a8641SAndroid Build Coastguard Worker    def dump_bytecode(self, bucket):
272*635a8641SAndroid Build Coastguard Worker        f = open(self._get_cache_filename(bucket), 'wb')
273*635a8641SAndroid Build Coastguard Worker        try:
274*635a8641SAndroid Build Coastguard Worker            bucket.write_bytecode(f)
275*635a8641SAndroid Build Coastguard Worker        finally:
276*635a8641SAndroid Build Coastguard Worker            f.close()
277*635a8641SAndroid Build Coastguard Worker
278*635a8641SAndroid Build Coastguard Worker    def clear(self):
279*635a8641SAndroid Build Coastguard Worker        # imported lazily here because google app-engine doesn't support
280*635a8641SAndroid Build Coastguard Worker        # write access on the file system and the function does not exist
281*635a8641SAndroid Build Coastguard Worker        # normally.
282*635a8641SAndroid Build Coastguard Worker        from os import remove
283*635a8641SAndroid Build Coastguard Worker        files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
284*635a8641SAndroid Build Coastguard Worker        for filename in files:
285*635a8641SAndroid Build Coastguard Worker            try:
286*635a8641SAndroid Build Coastguard Worker                remove(path.join(self.directory, filename))
287*635a8641SAndroid Build Coastguard Worker            except OSError:
288*635a8641SAndroid Build Coastguard Worker                pass
289*635a8641SAndroid Build Coastguard Worker
290*635a8641SAndroid Build Coastguard Worker
291*635a8641SAndroid Build Coastguard Workerclass MemcachedBytecodeCache(BytecodeCache):
292*635a8641SAndroid Build Coastguard Worker    """This class implements a bytecode cache that uses a memcache cache for
293*635a8641SAndroid Build Coastguard Worker    storing the information.  It does not enforce a specific memcache library
294*635a8641SAndroid Build Coastguard Worker    (tummy's memcache or cmemcache) but will accept any class that provides
295*635a8641SAndroid Build Coastguard Worker    the minimal interface required.
296*635a8641SAndroid Build Coastguard Worker
297*635a8641SAndroid Build Coastguard Worker    Libraries compatible with this class:
298*635a8641SAndroid Build Coastguard Worker
299*635a8641SAndroid Build Coastguard Worker    -   `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
300*635a8641SAndroid Build Coastguard Worker    -   `python-memcached <https://www.tummy.com/Community/software/python-memcached/>`_
301*635a8641SAndroid Build Coastguard Worker    -   `cmemcache <http://gijsbert.org/cmemcache/>`_
302*635a8641SAndroid Build Coastguard Worker
303*635a8641SAndroid Build Coastguard Worker    (Unfortunately the django cache interface is not compatible because it
304*635a8641SAndroid Build Coastguard Worker    does not support storing binary data, only unicode.  You can however pass
305*635a8641SAndroid Build Coastguard Worker    the underlying cache client to the bytecode cache which is available
306*635a8641SAndroid Build Coastguard Worker    as `django.core.cache.cache._client`.)
307*635a8641SAndroid Build Coastguard Worker
308*635a8641SAndroid Build Coastguard Worker    The minimal interface for the client passed to the constructor is this:
309*635a8641SAndroid Build Coastguard Worker
310*635a8641SAndroid Build Coastguard Worker    .. class:: MinimalClientInterface
311*635a8641SAndroid Build Coastguard Worker
312*635a8641SAndroid Build Coastguard Worker        .. method:: set(key, value[, timeout])
313*635a8641SAndroid Build Coastguard Worker
314*635a8641SAndroid Build Coastguard Worker            Stores the bytecode in the cache.  `value` is a string and
315*635a8641SAndroid Build Coastguard Worker            `timeout` the timeout of the key.  If timeout is not provided
316*635a8641SAndroid Build Coastguard Worker            a default timeout or no timeout should be assumed, if it's
317*635a8641SAndroid Build Coastguard Worker            provided it's an integer with the number of seconds the cache
318*635a8641SAndroid Build Coastguard Worker            item should exist.
319*635a8641SAndroid Build Coastguard Worker
320*635a8641SAndroid Build Coastguard Worker        .. method:: get(key)
321*635a8641SAndroid Build Coastguard Worker
322*635a8641SAndroid Build Coastguard Worker            Returns the value for the cache key.  If the item does not
323*635a8641SAndroid Build Coastguard Worker            exist in the cache the return value must be `None`.
324*635a8641SAndroid Build Coastguard Worker
325*635a8641SAndroid Build Coastguard Worker    The other arguments to the constructor are the prefix for all keys that
326*635a8641SAndroid Build Coastguard Worker    is added before the actual cache key and the timeout for the bytecode in
327*635a8641SAndroid Build Coastguard Worker    the cache system.  We recommend a high (or no) timeout.
328*635a8641SAndroid Build Coastguard Worker
329*635a8641SAndroid Build Coastguard Worker    This bytecode cache does not support clearing of used items in the cache.
330*635a8641SAndroid Build Coastguard Worker    The clear method is a no-operation function.
331*635a8641SAndroid Build Coastguard Worker
332*635a8641SAndroid Build Coastguard Worker    .. versionadded:: 2.7
333*635a8641SAndroid Build Coastguard Worker       Added support for ignoring memcache errors through the
334*635a8641SAndroid Build Coastguard Worker       `ignore_memcache_errors` parameter.
335*635a8641SAndroid Build Coastguard Worker    """
336*635a8641SAndroid Build Coastguard Worker
337*635a8641SAndroid Build Coastguard Worker    def __init__(self, client, prefix='jinja2/bytecode/', timeout=None,
338*635a8641SAndroid Build Coastguard Worker                 ignore_memcache_errors=True):
339*635a8641SAndroid Build Coastguard Worker        self.client = client
340*635a8641SAndroid Build Coastguard Worker        self.prefix = prefix
341*635a8641SAndroid Build Coastguard Worker        self.timeout = timeout
342*635a8641SAndroid Build Coastguard Worker        self.ignore_memcache_errors = ignore_memcache_errors
343*635a8641SAndroid Build Coastguard Worker
344*635a8641SAndroid Build Coastguard Worker    def load_bytecode(self, bucket):
345*635a8641SAndroid Build Coastguard Worker        try:
346*635a8641SAndroid Build Coastguard Worker            code = self.client.get(self.prefix + bucket.key)
347*635a8641SAndroid Build Coastguard Worker        except Exception:
348*635a8641SAndroid Build Coastguard Worker            if not self.ignore_memcache_errors:
349*635a8641SAndroid Build Coastguard Worker                raise
350*635a8641SAndroid Build Coastguard Worker            code = None
351*635a8641SAndroid Build Coastguard Worker        if code is not None:
352*635a8641SAndroid Build Coastguard Worker            bucket.bytecode_from_string(code)
353*635a8641SAndroid Build Coastguard Worker
354*635a8641SAndroid Build Coastguard Worker    def dump_bytecode(self, bucket):
355*635a8641SAndroid Build Coastguard Worker        args = (self.prefix + bucket.key, bucket.bytecode_to_string())
356*635a8641SAndroid Build Coastguard Worker        if self.timeout is not None:
357*635a8641SAndroid Build Coastguard Worker            args += (self.timeout,)
358*635a8641SAndroid Build Coastguard Worker        try:
359*635a8641SAndroid Build Coastguard Worker            self.client.set(*args)
360*635a8641SAndroid Build Coastguard Worker        except Exception:
361*635a8641SAndroid Build Coastguard Worker            if not self.ignore_memcache_errors:
362*635a8641SAndroid Build Coastguard Worker                raise
363