[z3-checkins] r56075 - in z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance: . tests

ianb at codespeak.net ianb at codespeak.net
Wed Jun 25 20:28:38 CEST 2008


Author: ianb
Date: Wed Jun 25 20:28:35 2008
New Revision: 56075

Added:
   z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/classmatch.py   (contents, props changed)
   z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/test_classmatch.txt   (contents, props changed)
Log:
add matchers for request/response matching

Added: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/classmatch.py
==============================================================================
--- (empty file)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/classmatch.py	Wed Jun 25 20:28:35 2008
@@ -0,0 +1,194 @@
+import fnmatch
+import re
+
+__all__ = ['compile_matcher', 'compile_header_matcher', 'MatchSyntaxError']
+
+_prefix_re = re.compile(r'^([a-z_-]+):', re.I)
+
+def compile_matcher(s, default=None):
+    match = _prefix_re.search(s)
+    if not match:
+        if default is None:
+            ## FIXME: show possible names?
+            raise MatchSyntaxError(
+                "You must provide a match type (like type:) in the pattern %r"
+                % s)
+        type = default
+        pattern = s
+    else:
+        type = match.group(1).lower()
+        pattern = s[match.end():]
+    if type not in _matches:
+        ## FIXME: show possible names?
+        raise MatchSyntaxError(
+            "The type %r is not valid"
+            % (type + ':'))
+    return _matches[type](pattern)
+
+def compile_header_matcher(s, default='exact'):
+    if ':' not in s:
+        raise MatchSyntaxError(
+            "A header match must be like 'Header: pattern'; you have no header in %r"
+            % s)
+    header, pattern = s.split(':', 1)
+    header = header.strip()
+    pattern = pattern.lstrip()
+    pattern = compile_matcher(pattern, default)
+    if '*' in header:
+        return HeaderWildcardMatcher(header, pattern)
+    else:
+        return HeaderMatcher(header, pattern)
+
+class MatchSyntaxError(Exception):
+    """
+    Raised if you have an invalid expression in a matcher
+    """
+
+_matches = {}
+
+def _add_matcher(cls):
+    _matches[cls.name] = cls
+
+class Matcher(object):
+
+    name = None
+
+    def __init__(self, pattern):
+        self.pattern = pattern
+
+    def __call__(self, s):
+        raise NotImplementedError
+
+    def __unicode__(self):
+        return '%s:%s' % (self.name, self.pattern)
+
+    def __str__(self):
+        return str(unicode(self))
+    
+    def __repr__(self):
+        return '<%s %s>' % (self.__class__.__name__, str(self))
+
+class WildcardMatcher(Matcher):
+
+    name = 'wildcard'
+
+    def __init__(self, pattern):
+        super(WildcardMatcher, self).__init__(pattern)
+        self.compiled = re.compile(fnmatch.translate(pattern))
+
+    def __call__(self, s):
+        return bool(self.compiled.match(s))
+
+_add_matcher(WildcardMatcher)
+
+class WildcardInsensitiveMatcher(Matcher):
+
+    name = 'wildcard-insensitive'
+
+    def __init__(self, pattern):
+        super(WildcardInsensitiveMatcher, self).__init__(pattern)
+        self.compiled = re.compile(fnmatch.translate(pattern), re.I)
+
+    def __call__(self, s):
+        return bool(self.compiled.match(s))
+
+_add_matcher(WildcardInsensitiveMatcher)
+
+class RegexMatcher(Matcher):
+
+    name = 'regex'
+
+    def __init__(self, pattern):
+        super(RegexMatcher, self).__init__(pattern)
+        try:
+            self.compiled = re.compile(pattern)
+        except re.error, e:
+            raise MatchSyntaxError(
+                "Invalid regular expression %r: %s"
+                % (pattern, e))
+    
+    def __call__(self, s):
+        return bool(self.compiled.match(s))
+            
+_add_matcher(RegexMatcher)
+
+class PathMatcher(Matcher):
+
+    name = 'path'
+
+    def __init__(self, pattern):
+        if not pattern.endswith('/'):
+            pattern += '/'
+        super(PathMatcher, self).__init__(pattern)
+
+    def __call__(self, s):
+        return (s == self.pattern[:-1]
+                or s.startswith(self.pattern))
+
+_add_matcher(PathMatcher)
+
+class ExactMatcher(Matcher):
+
+    name = 'exact'
+
+    def __call__(self, s):
+        return s == self.pattern
+
+_add_matcher(ExactMatcher)
+        
+class ExactInsensitiveMatcher(Matcher):
+    
+    name = 'exact-insensitive'
+
+    def __call__(self, s):
+        return s.lower() == self.pattern.lower()
+
+_add_matcher(ExactInsensitiveMatcher)
+
+class ContainsMatcher(Matcher):
+
+    name = 'contains'
+
+    def __call__(self, s):
+        return self.pattern in s
+
+_add_matcher(ContainsMatcher)
+
+class ContainsInsensitiveMatcher(Matcher):
+
+    name = 'contains-insensitive'
+
+    def __call__(self, s):
+        return self.pattern.lower() in s.lower()
+
+_add_matcher(ContainsInsensitiveMatcher)
+
+class HeaderMatcher(object):
+
+    def __init__(self, header, pattern):
+        self.header = header
+        self.pattern = pattern
+
+    def __call__(self, headers):
+        return self.pattern(headers.get(self.header, ''))
+
+    def __unicode__(self):
+        return u'%s: %s' % (self.header, self.pattern)
+
+class HeaderWildcardMatcher(object):
+
+    def __init__(self, header, pattern):
+        self.header = header
+        self.header_re = re.compile(fnmatch.translate(header), re.I)
+        self.pattern = pattern
+
+    def __call__(self, headers):
+        matches = self.header_re.match
+        for key in headers:
+            if matches(key):
+                if self.pattern(headers[key]):
+                    return True
+        return False
+
+    def __unicode__(self):
+        return u'%s: %s' % (self.header, self.pattern)

Added: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/test_classmatch.txt
==============================================================================
--- (empty file)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/test_classmatch.txt	Wed Jun 25 20:28:35 2008
@@ -0,0 +1,50 @@
+This tests different kinds of matchers.
+
+     >>> from deliverance.classmatch import compile_matcher, compile_header_matcher
+     >>> def match(pattern, *values):
+     ...     for value in values:
+     ...         result = compile_matcher(pattern)(value)
+     ...         print '%s: %s' % (value, result)
+     >>> match('exact:foo', 'foo', ' foo')
+     foo: True
+      foo: False
+     >>> match('exact-insensitive:foo', 'foo', 'fOo', ' FOO')
+     foo: True
+     fOo: True
+      FOO: False
+     >>> match('wildcard:foo*', 'foobar', 'barfoo', 'FOOBAR')
+     foobar: True
+     barfoo: False
+     FOOBAR: False
+     >>> match('wildcard-insensitive:*foo*', 'foobar', 'BARFOOBAR', 'fobar')
+     foobar: True
+     BARFOOBAR: True
+     fobar: False
+     >>> match('regex:^\\w+$', 'asdf', '1234', ' ', '')
+     asdf: True
+     1234: True
+      : False
+     : False
+     >>> match('path:/foo', '/foo', '/foo/', '/foo/something', '/foobar')
+     /foo: True
+     /foo/: True
+     /foo/something: True
+     /foobar: False
+     >>> match('contains:foo', 'somefoo thing')
+     somefoo thing: True
+
+And then some header matching:
+
+     >>> def mheader(pattern, headers):
+     ...     return compile_header_matcher(pattern)(headers)
+     >>> mheader('Something: foo', {'Something': 'foo'})
+     True
+     >>> mheader('Something: foo', {'Something': 'foobar'})
+     False
+     >>> mheader('Something: contains:foo', {'Something': 'foobar'})
+     True
+     >>> mheader('X-*: contains:evil', {'X-Other': 'nothing', 'X-Foo-Bar': 'some evil!'})
+     True
+     >>> mheader('X-*: contains:evil', {'X-Foo-Bar': 'okay'})
+     False
+     


More information about the z3-checkins mailing list