Mercurial > p > roundup > code
annotate roundup/web/router.py @ 4943:7920d700e580 routing
Add support for extensions to provide custom pages to Roundup
and update CHANGES.txt
1. Added registerHandler() extension point to instance.Tracker
to register URL handlers for specific routes
2. Added processing of extension routes to client.cgi
3. Added example plugins/extensions/custompage.py
| author | anatoly techtonik <techtonik@gmail.com> |
|---|---|
| date | Tue, 25 Nov 2014 17:29:38 +0300 |
| parents | 997fa47c92d5 |
| children | d8e0af01543b |
| rev | line source |
|---|---|
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
1 #!/usr/bin/env python |
|
4943
7920d700e580
Add support for extensions to provide custom pages to Roundup
anatoly techtonik <techtonik@gmail.com>
parents:
4925
diff
changeset
|
2 # This Router component was written by techtonik@gmail.com and it's been |
|
7920d700e580
Add support for extensions to provide custom pages to Roundup
anatoly techtonik <techtonik@gmail.com>
parents:
4925
diff
changeset
|
3 # placed in the Public Domain. Copy and modify to your heart's content. |
|
7920d700e580
Add support for extensions to provide custom pages to Roundup
anatoly techtonik <techtonik@gmail.com>
parents:
4925
diff
changeset
|
4 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
5 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
6 The purpose of router is to make Roundup URL scheme configurable |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
7 and allow extensions add their own handlers and URLs to tracker. |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
8 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
9 |
|
4924
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
10 DEBUG = False |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
11 |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
12 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
13 import re |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
14 |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
15 # --- Example URL mapping |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
16 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
17 class NamedObject(object): |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
18 """Object that outputs its name when printed""" |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
19 def __init__(self, name): |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
20 self.name = name |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
21 def __repr__(self): |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
22 return self.name |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
23 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
24 ExampleHandler = NamedObject('ExampleHandler') |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
25 ExampleFileHandler = NamedObject('ExampleFileHandler') |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
26 |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
27 |
|
4918
35dc9191394d
routing: Use Router in cgi.client.main
anatoly techtonik <techtonik@gmail.com>
parents:
4910
diff
changeset
|
28 EXAMPLE_URL_MAP = ( |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
29 'static/(.*)', ExampleFileHandler, |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
30 'index', ExampleHandler |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
31 ) |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
32 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
33 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
34 # --- Regexp based router |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
35 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
36 class Router(object): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
37 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
38 def __init__(self, urlmap=[]): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
39 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
40 `urlmap` is a list (pattern, handler, pattern, ...) |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
41 leading slash in pattern is stripped |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
42 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
43 self.urlmap = urlmap |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
44 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
45 def get_handler(self, urlpath): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
46 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
47 `urlpath` is a part of url /that/looks?like=this |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
48 (leading slash is optional, will be stripped anyway) |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
49 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
50 returns tuple (handler, arguments) or (None, ()) |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
51 """ |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
52 # strip leading slashes before matching |
|
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
53 path = urlpath.lstrip('/') |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
54 for i in range(0, len(self.urlmap), 2): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
55 pattern, handler = self.urlmap[i], self.urlmap[i+1] |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
56 pattern = pattern.lstrip('/') |
|
4924
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
57 if DEBUG: |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
58 print('router: matching %s' % pattern) |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
59 match = re.match(pattern, path) |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
60 if match: |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
61 return handler, match.groups() |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
62 return (None, ()) |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
63 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
64 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
65 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
66 # [ ] len(urlmap) should be even to avoid errors |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
67 # (find a way to explain this to users) |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
68 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
69 if __name__ == '__main__': |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
70 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
71 import unittest |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
72 class test_Router(unittest.TestCase): |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
73 def test_example_routes(self): |
|
4918
35dc9191394d
routing: Use Router in cgi.client.main
anatoly techtonik <techtonik@gmail.com>
parents:
4910
diff
changeset
|
74 router = Router(EXAMPLE_URL_MAP) |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
75 self.assertEquals(router.get_handler(''), (None, ())) |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
76 handler, params = router.get_handler('/index') |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
77 self.assertEquals(handler, ExampleHandler) |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
78 self.assertEquals(params, tuple()) |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
79 |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
80 def test_route_param(self): |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
81 def echo_handler(args): |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
82 return args |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
83 router = Router(('/files/(.*)', echo_handler)) |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
84 self.assertEquals(router.get_handler(''), (None, ())) |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
85 self.assertEquals(router.get_handler('/files'), (None, ())) |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
86 handler, params = router.get_handler('/files/filename') |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
87 self.assertEquals(handler, echo_handler) |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
88 self.assertEquals(params, ('filename',)) |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
89 |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
90 unittest.main() |
