Mercurial > p > roundup > code
annotate roundup/web/router.py @ 5000:082ee3ded101 routing
Improve docs for registerHandler
| author | anatoly techtonik <techtonik@gmail.com> |
|---|---|
| date | Fri, 11 Sep 2015 08:16:38 +0300 |
| parents | e9077def7678 |
| children | ce06c665932a |
| 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 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
33 # --- Helper functions |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
34 |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
35 def entry(prompt='> '): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
36 """Just get text for interactive mode""" |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
37 import sys |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
38 if sys.version_info[0] < 3: |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
39 return raw_input(prompt) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
40 else: |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
41 return input(prompt) |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
42 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
43 # --- Regexp based router |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
44 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
45 class Router(object): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
46 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
47 urlmap = [] |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
48 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
49 def __init__(self, urlmap=[]): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
50 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
51 `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
|
52 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
|
53 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
54 self.urlmap = urlmap |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
55 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
56 def get_handler(self, urlpath): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
57 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
58 `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
|
59 (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
|
60 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
61 returns tuple (handler, arguments) or (None, ()) |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
62 """ |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
63 # strip leading slashes before matching |
|
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
64 path = urlpath.lstrip('/') |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
65 for pattern, handler in self.iter_urlmap(): |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
66 pattern = pattern.lstrip('/') |
|
4924
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
67 if DEBUG: |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
68 print('router: matching %s' % pattern) |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
69 match = re.match(pattern, path) |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
70 if match: |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
71 return handler, match.groups() |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
72 return (None, ()) |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
73 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
74 def iter_urlmap(self): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
75 """ |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
76 iterate over self.urlmap returning (pattern, handler) pairs |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
77 """ |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
78 for i in range(0, len(self.urlmap), 2): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
79 yield self.urlmap[i], self.urlmap[i+1] |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
80 |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
81 def interactive(self): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
82 print('enter url to test, [l] to list rules, empty line exits') |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
83 url = entry('url: ') |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
84 while url != '': |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
85 if url == 'l': |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
86 for i in range(0, len(self.urlmap), 2): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
87 pattern, handler = self.urlmap[i], self.urlmap[i+1] |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
88 print(self.urlmap[i:i+2]) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
89 print('matched ' + str(self.get_handler(url))) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
90 url = entry('url: ') |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
91 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
92 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
93 # [ ] 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
|
94 # (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
|
95 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
96 if __name__ == '__main__': |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
97 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
98 import sys |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
99 if '-i' in sys.argv: |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
100 router = Router(EXAMPLE_URL_MAP) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
101 router.interactive() |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
102 sys.exit() |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
103 |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
104 import unittest |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
105 class test_Router(unittest.TestCase): |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
106 def test_example_routes(self): |
|
4918
35dc9191394d
routing: Use Router in cgi.client.main
anatoly techtonik <techtonik@gmail.com>
parents:
4910
diff
changeset
|
107 router = Router(EXAMPLE_URL_MAP) |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
108 self.assertEqual(router.get_handler(''), (None, ())) |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
109 handler, params = router.get_handler('/index') |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
110 self.assertEqual(handler, ExampleHandler) |
|
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
111 self.assertEqual(params, tuple()) |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
112 |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
113 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
|
114 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
|
115 return args |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
116 router = Router(('/files/(.*)', echo_handler)) |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
117 self.assertEqual(router.get_handler(''), (None, ())) |
|
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
118 self.assertEqual(router.get_handler('/files'), (None, ())) |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
119 handler, params = router.get_handler('/files/filename') |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
120 self.assertEqual(handler, echo_handler) |
|
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
121 self.assertEqual(params, ('filename',)) |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
122 |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
123 unittest.main() |
