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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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()

Roundup Issue Tracker: http://roundup-tracker.org/