comparison wsgintegrate/match.py @ 0:ec815b7cb142

initial commit of wsgintegrate
author Jeff Hammel <jhammel@mozilla.com>
date Tue, 07 Jun 2011 08:03:09 -0700
parents
children 89047fd9ea8f
comparison
equal deleted inserted replaced
-1:000000000000 0:ec815b7cb142
1 """
2 utilities for matching requests
3 these are just a sample; you can add arbitrary match objects if desired
4 """
5
6 from webob import exc
7
8 class RequestMatch(object):
9 """abstract base class for matching requests"""
10
11 def __init__(self, app):
12 self.app = app
13
14 def __call__(self, environ, start_response):
15 """WSGI app"""
16 if not self.condition(environ):
17 raise exc.HTTPNotFound
18 return self.app(environ, start_response)
19
20 def condition(self, environ):
21 """match condition"""
22 return True
23
24 class ConditionMatch(RequestMatch):
25 """generic environment-based condition-checker"""
26 # XXX unused
27 def __init__(self, app, condition):
28 RequestMatch.__init__(self, app)
29 self.condition = condition
30
31 ### aspect-based checkers
32
33 class MatchPath(RequestMatch):
34 """match based on PATH INFO"""
35
36 def __init__(self, app, path):
37 RequestMatch.__init__(self, app)
38 self.path = path
39
40 def match(self, path):
41 if path.startswith(self.path):
42 # currently only matches with str.startswith;
43 # different types of matches could be considered
44 return self.path, path[len(self.path):]
45
46 def condition(self, environ): # XXX unused
47 return self.match(environ['PATH_INFO']) is not None
48
49 def __call__(self, environ, start_response):
50 match = self.match(environ['PATH_INFO'])
51 if match is None:
52 raise exc.HTTPNotFound
53 script_name, path_info = match
54
55 # fix up the environment for downstream applications
56 environ['SCRIPT_NAME'] = script_name
57 environ['PATH_INFO'] = path_info
58
59 return self.app(environ, start_response)
60
61 class MatchMethod(RequestMatch):
62 """match based on request method"""
63
64 def __init__(self, app, methods):
65 RequestMatch.__init__(self, app)
66 if isinstance(methods, basestring):
67 methods = methods.split()
68 self.methods = set(methods)
69
70 def condition(self, environ):
71 return environ['REQUEST_METHOD'] in self.methods
72
73 class MatchHost(RequestMatch):
74 """match based on the host and port"""
75
76 def __init__(self, app, host, port=None):
77 RequestMatch.__init__(self, app)
78 self.host = host
79 self.port = port
80 def condition(self, environ):
81 host = environ['HTTP_HOST']
82 port = None
83 if ':' in host:
84 host, port = host.rsplit(':', 1)
85 if self.port and port != self.port:
86 return False
87 return host == self.host
88
89 class MatchAuthorized(RequestMatch):
90 """match if a user is authorized or not"""
91 def __init__(self, app, users=None):
92 RequestMatch.__init__(self, app)
93 self.authorized_users = users
94 def condition(self, environ):
95 raise NotImplementedError # TODO
96
97 class MatchProtocol(RequestMatch):
98 """match a given protocol, i.e. http vs https://"""
99 def __init__(self, app, protocol):
100 self.protocol = protocol
101 RequestMatch.__init__(self, app)
102 def condition(self, environ):
103 raise NotImplementedError # TODO
104
105 class MatchQueryString(RequestMatch):
106 """
107 match a request based on if the right query string parameters are given
108 """
109 def __init__(self, app, *tags, **kw):
110 self.app = app
111 self.tags = tags
112 self.kw = kw
113 def condition(self, environ):
114 raise NotImplementedError # TODO
115
116 ### logical checkers (currently unused)
117
118 class AND(RequestMatch):
119 def __init__(self, app, condition1, condition2, *conditions):
120 RequestMatch.__init__(self, app)
121 self.conditions = [condition1, condition2]
122 self.conditions.extend(conditions)
123 def condition(self, environ):
124 for condition in self.conditions:
125 if isinstance(condition, RequestMatch):
126 if not condition.condition(environ):
127 return False
128 else:
129 if not condition():
130 return False
131 return True
132
133 class OR(RequestMatch):
134 def __init__(self, app, condition1, condition2, *conditions):
135 RequestMatch.__init__(self, app)
136 self.conditions = [condition1, condition2]
137 self.conditions.extend(conditions)
138 def condition(self, environ):
139 for condition in self.conditions:
140 if isinstance(condition, RequestMatch):
141 if condition.condition(environ):
142 return True
143 else:
144 if condition():
145 return
146 return False
147
148 # string accessible list of conditions
149 conditions = {'host': MatchHost,
150 'method': MatchMethod,
151 'path': MatchPath }
152
153 class WrapApp(object):
154 """match string-based conditions"""
155
156 def __init__(self, conditions=None):
157 self.conditions = conditions or globals()['conditions']
158
159 def __call__(self, app, *conditions, **kwargs):
160 """
161 wrap an app in conditions
162 conditions should be a key, value 2-tuple of string, args;
163 kwargs should be a dictionary of unordered conditions,
164 likewise of the form string, args.
165 use *conditions if order is important, otherwise kwargs
166 """
167
168 # determine the condition
169 conditions = list(conditions)
170 if kwargs:
171 conditions.extend(kwargs.items())
172
173 # wrap the application
174 for condition, args in conditions:
175 assert condition in self.conditions, 'Condition "%s" unknown' % condition
176 app = self.conditions[condition](app, args)
177 return app
178
179 # convenience invocation
180 wrap = WrapApp(conditions)
181