Mercurial > p > roundup > code
annotate roundup/web/router.py @ 5002:ce06c665932a routing
Update router docstring to reference usage example
| author | anatoly techtonik <techtonik@gmail.com> |
|---|---|
| date | Fri, 11 Sep 2015 11:08:34 +0300 |
| parents | e9077def7678 |
| children |
| 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 |
|
5002
ce06c665932a
Update router docstring to reference usage example
anatoly techtonik <techtonik@gmail.com>
parents:
4999
diff
changeset
|
2 # The Router component is placed into public domain by |
|
ce06c665932a
Update router docstring to reference usage example
anatoly techtonik <techtonik@gmail.com>
parents:
4999
diff
changeset
|
3 # anatoly techtonik <techtonik@gmail.com> |
|
4943
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 """ |
|
5002
ce06c665932a
Update router docstring to reference usage example
anatoly techtonik <techtonik@gmail.com>
parents:
4999
diff
changeset
|
6 Router allows extensions to add handlers for their own URLs to |
|
ce06c665932a
Update router docstring to reference usage example
anatoly techtonik <techtonik@gmail.com>
parents:
4999
diff
changeset
|
7 tracker. To test how it works, copy |
|
ce06c665932a
Update router docstring to reference usage example
anatoly techtonik <techtonik@gmail.com>
parents:
4999
diff
changeset
|
8 plugins/extensions/custompage.py module to your tracker. |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
9 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
10 |
|
4924
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
11 DEBUG = False |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
12 |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
13 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
14 import re |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
15 |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
16 # --- Example URL mapping |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
17 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
18 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
|
19 """Object that outputs its name when printed""" |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
20 def __init__(self, name): |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
21 self.name = name |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
22 def __repr__(self): |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
23 return self.name |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
24 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
25 ExampleHandler = NamedObject('ExampleHandler') |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
26 ExampleFileHandler = NamedObject('ExampleFileHandler') |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
27 |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
28 |
|
4918
35dc9191394d
routing: Use Router in cgi.client.main
anatoly techtonik <techtonik@gmail.com>
parents:
4910
diff
changeset
|
29 EXAMPLE_URL_MAP = ( |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
30 'static/(.*)', ExampleFileHandler, |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
31 'index', ExampleHandler |
|
4906
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 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
34 # --- Helper functions |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
35 |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
36 def entry(prompt='> '): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
37 """Just get text for interactive mode""" |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
38 import sys |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
39 if sys.version_info[0] < 3: |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
40 return raw_input(prompt) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
41 else: |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
42 return input(prompt) |
|
4906
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
43 |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
44 # --- Regexp based router |
|
b860ede03056
routing: Add example URL map
anatoly techtonik <techtonik@gmail.com>
parents:
4905
diff
changeset
|
45 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
46 class Router(object): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
47 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
48 urlmap = [] |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
49 |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
50 def __init__(self, urlmap=[]): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
51 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
52 `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
|
53 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
|
54 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
55 self.urlmap = urlmap |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
56 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
57 def get_handler(self, urlpath): |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
58 """ |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
59 `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
|
60 (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
|
61 |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
62 returns tuple (handler, arguments) or (None, ()) |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
63 """ |
|
4909
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
64 # strip leading slashes before matching |
|
f31c93abedf6
routing: No leading slash in URL map patterns
anatoly techtonik <techtonik@gmail.com>
parents:
4907
diff
changeset
|
65 path = urlpath.lstrip('/') |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
66 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
|
67 pattern = pattern.lstrip('/') |
|
4924
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
68 if DEBUG: |
|
6ee1844019d5
routing: Add DEBUG flag for troubleshooting
anatoly techtonik <techtonik@gmail.com>
parents:
4918
diff
changeset
|
69 print('router: matching %s' % pattern) |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
70 match = re.match(pattern, path) |
|
4905
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
71 if match: |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
72 return handler, match.groups() |
|
6e313bdf6b69
routing: Add new roundup.web namespace with router component
anatoly techtonik <techtonik@gmail.com>
parents:
diff
changeset
|
73 return (None, ()) |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
74 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
75 def iter_urlmap(self): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
76 """ |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
77 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
|
78 """ |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
79 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
|
80 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
|
81 |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
82 def interactive(self): |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
83 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
|
84 url = entry('url: ') |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
85 while url != '': |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
86 if url == 'l': |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
87 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
|
88 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
|
89 print(self.urlmap[i:i+2]) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
90 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
|
91 url = entry('url: ') |
|
4907
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 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
94 # [ ] 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
|
95 # (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
|
96 |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
97 if __name__ == '__main__': |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
98 |
|
4999
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
99 import sys |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
100 if '-i' in sys.argv: |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
101 router = Router(EXAMPLE_URL_MAP) |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
102 router.interactive() |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
103 sys.exit() |
|
e9077def7678
router: Add interactive mode and iterator over urlmap
anatoly techtonik <techtonik@gmail.com>
parents:
4998
diff
changeset
|
104 |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
105 import unittest |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
106 class test_Router(unittest.TestCase): |
|
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
107 def test_example_routes(self): |
|
4918
35dc9191394d
routing: Use Router in cgi.client.main
anatoly techtonik <techtonik@gmail.com>
parents:
4910
diff
changeset
|
108 router = Router(EXAMPLE_URL_MAP) |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
109 self.assertEqual(router.get_handler(''), (None, ())) |
|
4910
e5f90a69f660
routing: Fix router test
anatoly techtonik <techtonik@gmail.com>
parents:
4909
diff
changeset
|
110 handler, params = router.get_handler('/index') |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
111 self.assertEqual(handler, ExampleHandler) |
|
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
112 self.assertEqual(params, tuple()) |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
113 |
|
4925
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
114 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
|
115 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
|
116 return args |
|
997fa47c92d5
routing: Strip leading slash from both path and pattern, add test
anatoly techtonik <techtonik@gmail.com>
parents:
4924
diff
changeset
|
117 router = Router(('/files/(.*)', echo_handler)) |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
118 self.assertEqual(router.get_handler(''), (None, ())) |
|
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
119 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
|
120 handler, params = router.get_handler('/files/filename') |
|
4998
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
121 self.assertEqual(handler, echo_handler) |
|
d8e0af01543b
Fix unittest warning about deprecated assertEquals
anatoly techtonik <techtonik@gmail.com>
parents:
4943
diff
changeset
|
122 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
|
123 |
|
4907
c37069a99cec
routing: Add self-test to router.py
anatoly techtonik <techtonik@gmail.com>
parents:
4906
diff
changeset
|
124 unittest.main() |
