[Secure-testing-commits] r14622 - lib/python

Florian Weimer fw at alioth.debian.org
Fri May 7 18:50:02 UTC 2010


Author: fw
Date: 2010-05-07 18:50:02 +0000 (Fri, 07 May 2010)
New Revision: 14622

Added:
   lib/python/regexpcase.py
Log:
lib/python/regexpcase.py: dispatching on regular expressions


Added: lib/python/regexpcase.py
===================================================================
--- lib/python/regexpcase.py	                        (rev 0)
+++ lib/python/regexpcase.py	2010-05-07 18:50:02 UTC (rev 14622)
@@ -0,0 +1,127 @@
+# regexpcase.py -- Python module for regexp-based dispatching
+# Copyright (C) 2009, 2010 Florian Weimer <fw at deneb.enyo.de>
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import re
+
+class RegexpCase(object):
+    def __init__(self, rules, prefix=None, suffix=None, default=None):
+        offset = 0
+        probes = []
+        maycall = default is None or callable(default)
+
+        # We use a single regular expression and use special probe
+        # captures to figure out which one has actually matched.
+        # Hopefully, the regular expression engine will make this run
+        # fast.
+        for (regexp, action) in rules:
+            compiled = re.compile(regexp)
+            probes.append((offset, offset + 1, offset + compiled.groups + 1,
+                           action))
+            offset += compiled.groups + 1
+            if action is not None:
+                maycall = maycall and callable(action)
+        self.probes = tuple(probes)
+        self.maycall = maycall
+
+        if not self.probes:
+            raise ValueError("empty rule list")
+        if prefix is None:
+            prefix = "^(?:("
+        else:
+            if re.compile(prefix).groups > 0:
+                raise ValueError("prefix must not contain captures")
+            prefix = "^(?:" + prefix + ")(?:("
+
+        if suffix is None:
+            suffix = "))$"
+        else:
+            if re.compile(suffix).groups > 0:
+                raise ValueError("suffix must not contain captures")
+            suffix = "))(?:" + suffix + ")$"
+
+        self.regexp = re.compile(
+            prefix + ')|('.join(regexp for (regexp, action) in rules)
+            + suffix)
+
+        self.default = default
+
+    def match(self, key):
+        match = self.regexp.match(key)
+        if match is None:
+            return (None, self.default)
+        groups = match.groups()
+        for (probe, i, j, action) in self.probes:
+            if groups[probe] is not None:
+                return (groups[i:j], action)
+        raise AssertionError("pattern and offset list incongruent")
+
+    def __getitem__(self, key):
+        return self.match(key)[1]
+
+    def __call__(self, key, *args):
+        if not self.maycall:
+            raise TypeError, "not all actions are callable"
+        (groups, action) = self.match(key)
+        if action is None:
+            return None
+        if groups is None:
+            groups = key
+        return action(groups, *args)
+
+def rule(regexp):
+    """Add a regular expression to the function, for the rule list"""
+    return lambda f: (regexp, f)
+
+if __name__ == "__main__":
+    import unittest
+
+    class TestRegexpCase(unittest.TestCase):
+        def testempty(self):
+            self.assertRaises(ValueError, RegexpCase, ())
+            self.assertRaises(ValueError, RegexpCase, (), prefix="foo")
+            self.assertRaises(ValueError, RegexpCase, (), suffix="foo")
+            self.assertRaises(ValueError, RegexpCase, (), default="foo")
+            self.assertRaises(ValueError, RegexpCase, (("two", 2)),
+                                                       prefix="(f)oo")
+            self.assertRaises(ValueError, RegexpCase, (("two", 2)),
+                                                       suffix="(f)oo")
+
+        def teststrings(self):
+            rc = RegexpCase((("two", 2),
+                             ("three", 3),
+                             ("five", 5)))
+            self.assertEqual(2, rc["two"])
+            self.assertEqual(3, rc["three"])
+            self.assertEqual(5, rc["five"])
+            self.assertEqual(None, rc["seven"])
+            self.assertEquals((None, None), rc.match("seven"))
+            self.assertRaises(TypeError, rc.__call__, ())
+
+        def testcallstrings(self):
+            rc = RegexpCase((("(two)", lambda groups, x: (groups, x)),
+                             ("three", lambda groups, x: (groups, x)),
+                             ("f(i)v(e)", lambda groups, x : (groups, x))))
+            self.assertEqual((("two",), -2), rc("two", -2))
+            self.assertEqual(((), -3), rc("three", -3))
+            self.assertEqual((tuple("ie"), -5), rc("five", -5))
+            self.assertEqual(None, rc("seven", -1))
+        def testcallstringsdefault(self):
+            rc = RegexpCase([("f(i)v(e)", lambda groups, x : (groups, x))],
+                            default=lambda key, x: (key, x))
+            self.assertEqual(("seven", -1), rc("seven", -1))
+
+    unittest.main()




More information about the Secure-testing-commits mailing list