comparison examples/trac/trac/web/href.py @ 39:93b4dcbafd7b trunk

Copy Trac to main branch.
author cmlenz
date Mon, 03 Jul 2006 18:53:27 +0000
parents
children
comparison
equal deleted inserted replaced
38:ee669cb9cccc 39:93b4dcbafd7b
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2003-2005 Edgewall Software
4 # Copyright (C) 2003-2004 Jonas Borgström <jonas@edgewall.com>
5 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
6 # All rights reserved.
7 #
8 # This software is licensed as described in the file COPYING, which
9 # you should have received as part of this distribution. The terms
10 # are also available at http://trac.edgewall.com/license.html.
11 #
12 # This software consists of voluntary contributions made by many
13 # individuals. For the exact contribution history, see the revision
14 # history and logs, available at http://projects.edgewall.com/trac/.
15 #
16 # Author: Jonas Borgström <jonas@edgewall.com>
17 # Christopher Lenz <cmlenz@gmx.de>
18
19 from urllib import quote, urlencode
20 from trac.util.text import unicode_quote, unicode_urlencode
21
22
23 class Href(object):
24 """
25 Implements a callable that constructs URLs with the given base. The
26 function can be called with any number of positional and keyword
27 arguments which than are used to assemble the URL.
28
29 Positional arguments are appended as individual segments to
30 the path of the URL:
31
32 >>> href = Href('/trac')
33 >>> href('ticket', 540)
34 '/trac/ticket/540'
35 >>> href('ticket', 540, 'attachment', 'bugfix.patch')
36 '/trac/ticket/540/attachment/bugfix.patch'
37 >>> href('ticket', '540/attachment/bugfix.patch')
38 '/trac/ticket/540/attachment/bugfix.patch'
39
40 If a positional parameter evaluates to None, it will be skipped:
41
42 >>> href('ticket', 540, 'attachment', None)
43 '/trac/ticket/540/attachment'
44
45 The first path segment can also be specified by calling an attribute
46 of the instance, as follows:
47
48 >>> href.ticket(540)
49 '/trac/ticket/540'
50 >>> href.changeset(42, format='diff')
51 '/trac/changeset/42?format=diff'
52
53 Simply calling the Href object with no arguments will return the base URL:
54
55 >>> href()
56 '/trac'
57
58 Keyword arguments are added to the query string, unless the value is None:
59
60 >>> href = Href('/trac')
61 >>> href('timeline', format='rss')
62 '/trac/timeline?format=rss'
63 >>> href('timeline', format=None)
64 '/trac/timeline'
65 >>> href('search', q='foo bar')
66 '/trac/search?q=foo+bar'
67
68 Multiple values for one parameter are specified using a sequence (a list or
69 tuple) for the parameter:
70
71 >>> href('timeline', show=['ticket', 'wiki', 'changeset'])
72 '/trac/timeline?show=ticket&show=wiki&show=changeset'
73
74 Alternatively, query string parameters can be added by passing a dict or
75 list as last positional argument:
76
77 >>> href('timeline', {'from': '02/24/05', 'daysback': 30})
78 '/trac/timeline?daysback=30&from=02%2F24%2F05'
79
80 If the order of query string parameters should be preserved, you may also
81 pass a sequence of (name, value) tuples as last positional argument:
82
83 >>> href('query', (('group', 'component'), ('groupdesc', 1)))
84 '/trac/query?group=component&groupdesc=1'
85
86 >>> params = []
87 >>> params.append(('group', 'component'))
88 >>> params.append(('groupdesc', 1))
89 >>> href('query', params)
90 '/trac/query?group=component&groupdesc=1'
91
92 By specifying an absolute base, the function returned will also generate
93 absolute URLs:
94
95 >>> href = Href('http://projects.edgewall.com/trac')
96 >>> href('ticket', 540)
97 'http://projects.edgewall.com/trac/ticket/540'
98
99 >>> href = Href('https://projects.edgewall.com/trac')
100 >>> href('ticket', 540)
101 'https://projects.edgewall.com/trac/ticket/540'
102
103 In common usage, it may improve readability to use the function-calling
104 ability for the first component of the URL as mentioned earlier:
105
106 >>> href = Href('/trac')
107 >>> href.ticket(540)
108 '/trac/ticket/540'
109 >>> href.browser('/trunk/README.txt', format='txt')
110 '/trac/browser/trunk/README.txt?format=txt'
111 """
112
113 def __init__(self, base):
114 self.base = base
115 self._derived = {}
116
117 def __call__(self, *args, **kw):
118 href = self.base
119 if href and href[-1] == '/':
120 href = href[:-1]
121 params = []
122
123 def add_param(name, value):
124 if type(value) in (list, tuple):
125 for i in [i for i in value if i != None]:
126 params.append((name, i))
127 elif v != None:
128 params.append((name, value))
129
130 if args:
131 lastp = args[-1]
132 if lastp and type(lastp) is dict:
133 for k,v in lastp.items():
134 add_param(k, v)
135 args = args[:-1]
136 elif lastp and type(lastp) in (list, tuple):
137 for k,v in lastp:
138 add_param(k, v)
139 args = args[:-1]
140
141 # build the path
142 path = '/'.join([unicode_quote(unicode(arg).strip('/')) for arg in args
143 if arg != None])
144 if path:
145 href += '/' + path
146
147 # assemble the query string
148 for k,v in kw.items():
149 add_param(k, v)
150
151 if params:
152 href += '?' + unicode_urlencode(params)
153
154 return href
155
156 def __getattr__(self, name):
157 if not self._derived.has_key(name):
158 self._derived[name] = lambda *args, **kw: self(name, *args, **kw)
159 return self._derived[name]
160
161
162 if __name__ == '__main__':
163 import doctest, sys
164 doctest.testmod(sys.modules[__name__])
Copyright (C) 2012-2017 Edgewall Software