Actual version on http://pypi.python.org/pypi/decoroute
1
2 easy_install decoroute
Example:
1
2 import decoroute
3
4 app = decoroute.App()
5
6 def render_response(status = '200 OK', **kw):
7
8 return status, [('Content-Type', 'text/plain')], [str(kw)]
9
10 def redirect_to(env, endpoint, **kw):
11 return '302 FOUND', [('Content-Type', 'text/plain'), \
12 ('Location', '%s://%s%s' % (env['wsgi.url_scheme'], env['HTTP_HOST'], \
13 env['decoroute.app'].url_for(endpoint, **kw)))], ['']
14
15 @app.expose('/node', id = '1')
16 @app.expose('/node/<id:\d+>')
17 def node(env, id):
18 return render_response(id = id)
19
20 @app.expose('/url_for')
21 def url_for(env):
22 return render_response( \
23 url = env['decoroute.app'].url_for(node, id = 666))
24
25 @app.expose('/302')
26 def found(env):
27 return redirect_to(env, not_found)
28
29 @app.expose('/404')
30 def not_found(env):
31 raise decoroute.NotFound()
32
33 @app.not_found()
34 def not_found_handler(env):
35 return render_response('404 NF', url = env['PATH_INFO'])
36
37 from wsgiref.simple_server import make_server
38
39 make_server('', 5555, app).serve_forever()
Complete source < 100 lines of code:
1
2
3
4
5
6
7
8
9
10
11
12 __author__ = "Vsevolod Balashov"
13 __email__ = "vsevolod at balashov dot name"
14
15 import re
16 from sets import ImmutableSet
17 import wsgistraw
18
19 __all__ = ['NotFound', 'App']
20
21 class NotFound(Exception):
22 pass
23
24 _pattern_re = re.compile(r'''
25 ([^<]+) # static rule data
26 (?:<
27 ([a-zA-Z][a-zA-Z0-9_]*) # variable name
28 \:
29 ([^>]+) # regexp constraint
30 >)?
31 ''', re.VERBOSE)
32
33 def pattern2regexp(pattern, f, s = lambda x: re.escape(x)):
34 def parser():
35 for t in _pattern_re.findall(pattern):
36 yield s(t[0])
37 if t[1] != '':
38 yield f(t[1], t[2])
39 return parser()
40
41 make_url_for = lambda p: ''.join(pattern2regexp(p, lambda v, r: '%%(%s)s' % v, lambda s: s))
42 make_variables = lambda p: filter(lambda x: x, pattern2regexp(p, lambda v, r: v, lambda s: None))
43 make_pattern = lambda p: r''.join(pattern2regexp(p, lambda v, r: r'(?P<%s>%s)' % (v, r)))
44 make_selector_fragment = lambda p: r''.join(pattern2regexp(p, lambda v, r: r'(?:%s)' % r))
45 make_selector = lambda i: re.compile(r'(^%s$)' % r'$)|(^'.join(map(make_selector_fragment, i)))
46
47 class UrlMap(object):
48 def __init__(self):
49 self._endpoints = {}
50 self._patterns = {}
51 self._pattern_selector = make_selector(self._patterns.iterkeys())
52
53 def add(self, pattern, endpoint, **kw):
54 if self._patterns.has_key(pattern):
55 raise Exception('duplicate pattern', pattern)
56 self._endpoints[(endpoint, ImmutableSet(make_variables(pattern)))] = make_url_for(pattern)
57 self._patterns[pattern] = (re.compile(make_pattern(pattern)), endpoint, kw)
58 self._pattern_selector = make_selector(self._patterns.iterkeys())
59
60 def route(self, url):
61 try:
62 p = self._patterns.values()[re.match(self._pattern_selector, url).lastindex - 1]
63 d = re.match(p[0], url).groupdict().copy()
64 d.update(p[2])
65 return p[1], d
66 except:
67 raise NotFound('route not found', url)
68
69 def url_for(self, endpoint, **kw):
70 return self._endpoints[(endpoint, ImmutableSet(kw.keys()))] % kw
71
72 class App(object):
73 def __init__(self, key = 'decoroute.app'):
74 self._key = key
75 self._map = UrlMap()
76 self._not_found = lambda e: ('404 NOT FOUND', [("Content-Type", "text/plain")], [''])
77
78 @wsgistraw.app
79 def __call__(self, env):
80 try:
81 env[self._key] = self
82 endpoint, kw = self._map.route(env['PATH_INFO'])
83 return endpoint(env, **kw)
84 except NotFound:
85 return self._not_found(env)
86
87 def expose(self, pattern, **kw):
88 def decorate(f):
89 self._map.add(pattern, f, **kw)
90 return f
91 return decorate
92
93 def not_found(self):
94 def decorate(f):
95 self._not_found = f
96 return f
97 return decorate
98
99 def url_for(self, endpoint, **kw):
100 return self._map.url_for(endpoint, **kw)