Index: genshi/template/base.py
===================================================================
--- genshi/template/base.py	(revision 611)
+++ genshi/template/base.py	(working copy)
@@ -288,7 +288,7 @@
     """
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient'):
+                 encoding=None, lookup='lenient', disable_python=False):
         """Initialize a template from either a string, a file-like object, or
         an already parsed markup stream.
         
@@ -305,6 +305,7 @@
         :param encoding: the encoding of the `source`
         :param lookup: the variable lookup mechanism; either "lenient" (the
                        default), "strict", or a custom lookup class
+        :param disable_python: whether to allow python code blocks
         """
         self.basedir = basedir
         self.filename = filename
@@ -314,6 +315,7 @@
             self.filepath = filename
         self.loader = loader
         self.lookup = lookup
+        self.disable_python = disable_python
 
         if isinstance(source, basestring):
             source = StringIO(source)
Index: genshi/template/tests/markup.py
===================================================================
--- genshi/template/tests/markup.py	(revision 611)
+++ genshi/template/tests/markup.py	(working copy)
@@ -438,7 +438,37 @@
         finally:
             shutil.rmtree(dirname)
 
+    def test_disable_python_true(self): 
+        xml = ("""<?python
+          title = "A Genshi Template"
+          ?>
+          <html xmlns:py="http://genshi.edgewall.org/">
+            <head>
+              <title py:content="title">This is replaced.</title>
+            </head>
+        </html>""")
+        try:
+            tmpl = MarkupTemplate(xml, filename='test.html',
+                                  disable_python=True)
+            self.fail('Expected SyntaxError')
+        except TemplateSyntaxError, e:
+            self.assertEqual('test.html', e.filename)
 
+    def test_disable_python_false(self): 
+        xml = ("""<?python
+          title = "A Genshi Template"
+          ?>
+          <html xmlns:py="http://genshi.edgewall.org/">
+            <head>
+              <title py:content="title">This is replaced.</title>
+            </head>
+        </html>""")
+        try:
+            tmpl = MarkupTemplate(xml, filename='test.html',
+                                  disable_python=False)
+        except TemplateSyntaxError, e:
+            self.fail('Unexpected SyntaxError')
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(MarkupTemplate.__module__))
Index: genshi/template/plugin.py
===================================================================
--- genshi/template/plugin.py	(revision 611)
+++ genshi/template/plugin.py	(working copy)
@@ -63,11 +63,17 @@
             raise ConfigurationError('Unknown lookup errors mode "%s"' %
                                      lookup_errors)
 
+        disable_python = options.get('genshi.disable_python', False)
+        if not isinstance(disable_python, bool):
+            raise ConfigurationError('Invalid value for disable_python "%s"' %
+                                     disable_python)
+
         self.loader = TemplateLoader(filter(None, search_path),
                                      auto_reload=auto_reload,
                                      max_cache_size=max_cache_size,
                                      default_class=self.template_class,
-                                     variable_lookup=lookup_errors)
+                                     variable_lookup=lookup_errors,
+                                     disable_python=disable_python)
 
     def load_template(self, templatename, template_string=None):
         """Find a template specified in python 'dot' notation, or load one from
Index: genshi/template/markup.py
===================================================================
--- genshi/template/markup.py	(revision 611)
+++ genshi/template/markup.py	(working copy)
@@ -67,9 +67,10 @@
                   ('strip', StripDirective)]
 
     def __init__(self, source, basedir=None, filename=None, loader=None,
-                 encoding=None, lookup='lenient'):
+                 encoding=None, lookup='lenient', disable_python=False):
         Template.__init__(self, source, basedir=basedir, filename=filename,
-                          loader=loader, encoding=encoding, lookup=lookup)
+                          loader=loader, encoding=encoding, lookup=lookup,
+                          disable_python=disable_python)
         # Make sure the include filter comes after the match filter
         if loader:
             self.filters.remove(self._include)
@@ -185,6 +186,10 @@
                                               pos)]
 
             elif kind is PI and data[0] == 'python':
+                if self.disable_python:
+                    raise TemplateSyntaxError('Python code block are '
+                                              'disabled',
+                                              self.filepath, *pos[1:])
                 try:
                     # As Expat doesn't report whitespace between the PI target
                     # and the data, we have to jump through some hoops here to
Index: genshi/template/loader.py
===================================================================
--- genshi/template/loader.py	(revision 611)
+++ genshi/template/loader.py	(working copy)
@@ -73,7 +73,8 @@
     """
     def __init__(self, search_path=None, auto_reload=False,
                  default_encoding=None, max_cache_size=25, default_class=None,
-                 variable_lookup='lenient', callback=None):
+                 variable_lookup='lenient', disable_python=False,
+                 callback=None):
         """Create the template laoder.
         
         :param search_path: a list of absolute path names that should be
@@ -90,6 +91,7 @@
         :param variable_lookup: the variable lookup mechanism; either "lenient"
                                 (the default), "strict", or a custom lookup
                                 class
+        :param disable_python: whether to allow python code blocks
         :param callback: (optional) a callback function that is invoked after a
                          template was initialized by this loader; the function
                          is passed the template object as only argument. This
@@ -108,6 +110,7 @@
         self.default_encoding = default_encoding
         self.default_class = default_class or MarkupTemplate
         self.variable_lookup = variable_lookup
+        self.disable_python = disable_python
         if callback is not None and not callable(callback):
             raise TypeError('The "callback" parameter needs to be callable')
         self.callback = callback
@@ -200,7 +203,8 @@
                             dirname = ''
                         tmpl = cls(fileobj, basedir=dirname, filename=filename,
                                    loader=self, lookup=self.variable_lookup,
-                                   encoding=encoding)
+                                   encoding=encoding,
+                                   disable_python=self.disable_python)
                         if self.callback:
                             self.callback(tmpl)
                         self._cache[filename] = tmpl
