Rev 8: Include error reporting for not being able to find a command. in http://bzr.arbash-meinel.com/branches/bzr/other/moin_graphviz

John Arbash Meinel john at arbash-meinel.com
Thu Apr 5 20:48:01 BST 2007


At http://bzr.arbash-meinel.com/branches/bzr/other/moin_graphviz

------------------------------------------------------------
revno: 8
revision-id: john at arbash-meinel.com-20070405194758-95ioshbgq02k5s3v
parent: john at arbash-meinel.com-20070402235747-7l90uoalf1vcifui
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: moin_graphviz
timestamp: Thu 2007-04-05 14:47:58 -0500
message:
  Include error reporting for not being able to find a command.
  Also add a function for testing escaping xml text.
  And use that to make sure we don't pass any html codes back for errors.
modified:
  graphviz.py                    graphviz.py-20070402222439-3qloodrjtlsuaz1o-1
  test_graphviz.py               test_graphviz.py-20070402223544-kez8ooyxqh1wx5w0-1
-------------- next part --------------
=== modified file 'graphviz.py'
--- a/graphviz.py	2007-04-02 23:57:47 +0000
+++ b/graphviz.py	2007-04-05 19:47:58 +0000
@@ -20,8 +20,10 @@
 @license: GNU GPL, see COPYING for details.
 """
 
+import errno
+from base64 import b64encode
+import re
 from subprocess import Popen, PIPE
-from base64 import b64encode
 
 
 Dependencies = []
@@ -31,6 +33,11 @@
     """Raised when we fail to run Dot"""
 
 
+class DotNotFound(Exception):
+    """Raised when the specified dot command cannot be found."""
+
+
+
 class Parser(object):
     """Parse the output through graphviz to generate output."""
 
@@ -58,7 +65,12 @@
         return [Parser._dot, "-Tpng"]
 
     def _spawn_dot(self):
-        return Popen(self._dot_command(), stdin=PIPE, stdout=PIPE)
+        try:
+            return Popen(self._dot_command(), stdin=PIPE, stdout=PIPE)
+        except OSError, e:
+            if e.errno in (errno.ENOENT,):
+                raise DotNotFound()
+            raise
 
     def _create_b64_png(self):
         myout = self._create_png()
@@ -66,8 +78,19 @@
 
     def format(self, formatter):
         """Send the text."""
+        try:
+            png_base64 = self._create_b64_png()
+        except DotNotFound:
+            # TODO: Even though this is configured locally, we should almost
+            #       definitely safely escape _dot_command() here.
+            self.request.write(formatter.rawHTML(
+                        '<b>Could not find dot processor:'
+                        ' <tt>"%s"</tt></b>'
+                        % (xml_escape(self._dot_command()[0]),)
+                        ))
+            return
         img_str = ('<img src="data:image/png;base64,%s"'
-                   ' alt="Graphviz Image" />') % (self._create_b64_png(),)
+                   ' alt="Graphviz Image" />') % (png_base64,)
         # TODO: jam 20070402 Are we supposed to be calling wikiutil.escape?
         #       The MoinMoin wiki seems to say that we should trap rawHTML in a
         #       try/except and fall back to escapedText or something else.
@@ -77,6 +100,28 @@
         self.request.write(formatter.rawHTML(img_str))
 
 
+_escape_re = re.compile("[&'\"<>]")
+_escape_map = {
+    "&":'&amp;',
+    "'":"&apos;", # FIXME: overkill
+    "\"":"&quot;",
+    "<":"&lt;",
+    ">":"&gt;",
+    }
+
+def _escape_replace(match):
+    """Replace the match object with its matching string.
+
+    Just a helper for xml_escape and re.sub()
+    """
+    return _escape_map[match.group()]
+
+
+def xml_escape(s):
+    """Take a string and make sure it is safe for display."""
+    return _escape_re.sub(_escape_replace, s)
+
+
 def test_suite():
     import unittest
     import test_graphviz

=== modified file 'test_graphviz.py'
--- a/test_graphviz.py	2007-04-02 23:52:58 +0000
+++ b/test_graphviz.py	2007-04-05 19:47:58 +0000
@@ -148,7 +148,7 @@
         try:
             graphviz.Parser._dot = '/this/command/doesnt/exist'
             p = self.get_parser()
-            self.assertRaises(OSError, p._spawn_dot)
+            self.assertRaises(graphviz.DotNotFound, p._spawn_dot)
         finally:
             graphviz.Parser._dot = orig_val
 
@@ -209,3 +209,59 @@
 
         self.assertEqual([('rawHTML', expected_img_txt)],
                          formatter._actions)
+
+    def test_format_handles_missing_dot(self):
+        request = self.get_request()
+        p = ParserWithCommand('', request)
+        p._command = ['/no/such/command', 'arg', 'arg2']
+        formatter = self.get_formatter()
+        p.format(formatter)
+
+        expected_text = ('<b>Could not find dot processor:'
+                          ' <tt>"/no/such/command"</tt></b>')
+        self.assertEqual(expected_text, request._output.getvalue())
+        self.assertEqual([('rawHTML', expected_text)], formatter._actions)
+
+    def test_format_escapes_missing_command(self):
+        request = self.get_request()
+        p = ParserWithCommand('', request)
+        p._command = ['/bo&gu<s>/command', 'args']
+        formatter = self.get_formatter()
+        p.format(formatter)
+
+        expected_text = ('<b>Could not find dot processor: <tt>'
+                         '"/bo&amp;gu&lt;s&gt;/command"'
+                         '</tt></b>')
+        self.assertEqual(expected_text, request._output.getvalue())
+        self.assertEqual([('rawHTML', expected_text)], formatter._actions)
+
+
+class TestXmlEscape(unittest.TestCase):
+    """Test the basic abilities of graphiz.escape"""
+
+    def test_simple(self):
+        self.assertEqual('foo', graphviz.xml_escape('foo'))
+
+    def test_apostrophe(self):
+        self.assertEqual('fo&apos;o', graphviz.xml_escape('fo\'o'))
+
+    def test_quote(self):
+        self.assertEqual('fo&quot;o', graphviz.xml_escape('fo"o'))
+
+    def test_less_than(self):
+        self.assertEqual('fo&lt;o', graphviz.xml_escape('fo<o'))
+
+    def test_greater_than(self):
+        self.assertEqual('fo&gt;o', graphviz.xml_escape('fo>o'))
+
+    def test_ampersand(self):
+        self.assertEqual('fo&amp;o', graphviz.xml_escape('fo&o'))
+
+    def test_html(self):
+        self.assertEqual('&lt;a href=&quot;foo&quot;&gt;bar&lt;/a&gt;',
+                         graphviz.xml_escape('<a href="foo">bar</a>'))
+
+    def test_alphabet(self):
+        """Most chars shouldn't be escaped."""
+        txt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+        self.assertEqual(txt, graphviz.xml_escape(txt))



More information about the bazaar-commits mailing list