changeset 715:b5bd8c109209 trunk

Enable pickling of `Template` and `Code` objects.
author cmlenz
date Tue, 08 Apr 2008 22:34:01 +0000
parents fc6d9d2a3527
children eee7483041dd
files genshi/template/base.py genshi/template/eval.py genshi/template/markup.py genshi/template/tests/eval.py genshi/template/tests/markup.py genshi/tests/core.py
diffstat 6 files changed, 78 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/genshi/template/base.py
+++ b/genshi/template/base.py
@@ -363,10 +363,7 @@
         self.loader = loader
         self.lookup = lookup
         self.allow_exec = allow_exec
-
-        self.filters = [self._flatten, self._eval, self._exec]
-        if loader:
-            self.filters.append(self._include)
+        self._init_filters()
 
         if isinstance(source, basestring):
             source = StringIO(source)
@@ -377,9 +374,23 @@
         except ParseError, e:
             raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset)
 
+    def __getstate__(self):
+        state = self.__dict__.copy()
+        state['filters'] = []
+        return state
+
+    def __setstate__(self, state):
+        self.__dict__ = state
+        self._init_filters()
+
     def __repr__(self):
         return '<%s "%s">' % (self.__class__.__name__, self.filename)
 
+    def _init_filters(self):
+        self.filters = [self._flatten, self._eval, self._exec]
+        if self.loader:
+            self.filters.append(self._include)
+
     def _parse(self, source, encoding):
         """Parse the template.
         
--- a/genshi/template/eval.py
+++ b/genshi/template/eval.py
@@ -75,6 +75,21 @@
             lookup = {'lenient': LenientLookup, 'strict': StrictLookup}[lookup]
         self._globals = lookup.globals
 
+    def __getstate__(self):
+        state = {'source': self.source, 'ast': self.ast,
+                 'lookup': self._globals.im_self}
+        c = self.code
+        state['code'] = (c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code,
+                         c.co_consts, c.co_names, c.co_varnames, c.co_filename,
+                         c.co_name, c.co_firstlineno, c.co_lnotab, (), ())
+        return state
+
+    def __setstate__(self, state):
+        self.source = state['source']
+        self.ast = state['ast']
+        self.code = new.code(0, *state['code'])
+        self._globals = state['lookup'].globals
+
     def __eq__(self, other):
         return (type(other) == type(self)) and (self.code == other.code)
 
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -60,16 +60,13 @@
     serializer = 'xml'
     _number_conv = Markup
 
-    def __init__(self, source, filepath=None, filename=None, loader=None,
-                 encoding=None, lookup='strict', allow_exec=True):
-        Template.__init__(self, source, filepath=filepath, filename=filename,
-                          loader=loader, encoding=encoding, lookup=lookup,
-                          allow_exec=allow_exec)
+    def _init_filters(self):
+        Template._init_filters(self)
         # Make sure the include filter comes after the match filter
-        if loader:
+        if self.loader:
             self.filters.remove(self._include)
         self.filters += [self._match]
-        if loader:
+        if self.loader:
             self.filters.append(self._include)
 
     def _parse(self, source, encoding):
--- a/genshi/template/tests/eval.py
+++ b/genshi/template/tests/eval.py
@@ -12,6 +12,8 @@
 # history and logs, available at http://genshi.edgewall.org/log/.
 
 import doctest
+import pickle
+from StringIO import StringIO
 import sys
 import unittest
 
@@ -32,6 +34,14 @@
         self.assertEqual(hash(expr), hash(Expression('x,y')))
         self.assertNotEqual(hash(expr), hash(Expression('y, x')))
 
+    def test_pickle(self):
+        expr = Expression('1 < 2')
+        buf = StringIO()
+        pickle.dump(expr, buf, 2)
+        buf.seek(0)
+        unpickled = pickle.load(buf)
+        assert unpickled.evaluate({}) is True
+
     def test_name_lookup(self):
         self.assertEqual('bar', Expression('foo').evaluate({'foo': 'bar'}))
         self.assertEqual(id, Expression('id').evaluate({}))
@@ -443,6 +453,16 @@
 
 class SuiteTestCase(unittest.TestCase):
 
+    def test_pickle(self):
+        suite = Suite('foo = 42')
+        buf = StringIO()
+        pickle.dump(suite, buf, 2)
+        buf.seek(0)
+        unpickled = pickle.load(buf)
+        data = {}
+        unpickled.execute(data)
+        self.assertEqual(42, data['foo'])
+
     def test_internal_shadowing(self):
         # The context itself is stored in the global execution scope of a suite
         # It used to get stored under the name 'data', which meant the
--- a/genshi/template/tests/markup.py
+++ b/genshi/template/tests/markup.py
@@ -13,6 +13,7 @@
 
 import doctest
 import os
+import pickle
 import shutil
 from StringIO import StringIO
 import sys
@@ -39,6 +40,15 @@
         tmpl = MarkupTemplate(stream)
         self.assertEqual('<root> 42 42</root>', str(tmpl.generate(var=42)))
 
+    def test_pickle(self):
+        stream = XML('<root>$var</root>')
+        tmpl = MarkupTemplate(stream)
+        buf = StringIO()
+        pickle.dump(tmpl, buf, 2)
+        buf.seek(0)
+        unpickled = pickle.load(buf)
+        self.assertEqual('<root>42</root>', str(unpickled.generate(var=42)))
+
     def test_interpolate_mixed3(self):
         tmpl = MarkupTemplate('<root> ${var} $var</root>')
         self.assertEqual('<root> 42 42</root>', str(tmpl.generate(var=42)))
--- a/genshi/tests/core.py
+++ b/genshi/tests/core.py
@@ -21,7 +21,7 @@
 import unittest
 
 from genshi import core
-from genshi.core import Markup, Namespace, QName, escape, unescape
+from genshi.core import Markup, Attrs, Namespace, QName, escape, unescape
 from genshi.input import XML, ParseError
 
 
@@ -164,6 +164,18 @@
         self.assertEquals("<Markup u'foo'>", repr(pickle.load(buf)))
 
 
+class AttrsTestCase(unittest.TestCase):
+
+    def test_pickle(self):
+        attrs = Attrs([("attr1", "foo"), ("attr2", "bar")])
+        buf = StringIO()
+        pickle.dump(attrs, buf, 2)
+        buf.seek(0)
+        unpickled = pickle.load(buf)
+        self.assertEquals("Attrs([('attr1', 'foo'), ('attr2', 'bar')])",
+                          repr(unpickled))
+
+
 class NamespaceTestCase(unittest.TestCase):
 
     def test_pickle(self):
@@ -206,6 +218,7 @@
     suite.addTest(unittest.makeSuite(StreamTestCase, 'test'))
     suite.addTest(unittest.makeSuite(MarkupTestCase, 'test'))
     suite.addTest(unittest.makeSuite(NamespaceTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(AttrsTestCase, 'test'))
     suite.addTest(unittest.makeSuite(QNameTestCase, 'test'))
     suite.addTest(doctest.DocTestSuite(core))
     return suite
Copyright (C) 2012-2017 Edgewall Software