[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