##// END OF EJS Templates
hgweb: doctest of url creation from wildcard expansion
Mads Kiilerich -
r13402:f947d9a4 default
parent child Browse files
Show More
@@ -1,359 +1,370 b''
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import os, re, time, urlparse
9 import os, re, time, urlparse
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import ui, hg, util, templater
11 from mercurial import ui, hg, util, templater
12 from mercurial import error, encoding
12 from mercurial import error, encoding
13 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
13 from common import ErrorResponse, get_mtime, staticfile, paritygen, \
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from hgweb_mod import hgweb
15 from hgweb_mod import hgweb
16 from request import wsgirequest
16 from request import wsgirequest
17 import webutil
17 import webutil
18
18
19 def cleannames(items):
19 def cleannames(items):
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
21
21
22 def findrepos(paths):
22 def findrepos(paths):
23 repos = []
23 repos = []
24 for prefix, root in cleannames(paths):
24 for prefix, root in cleannames(paths):
25 roothead, roottail = os.path.split(root)
25 roothead, roottail = os.path.split(root)
26 # "foo = /bar/*" makes every subrepo of /bar/ to be
26 # "foo = /bar/*" makes every subrepo of /bar/ to be
27 # mounted as foo/subrepo
27 # mounted as foo/subrepo
28 # and "foo = /bar/**" also recurses into the subdirectories,
28 # and "foo = /bar/**" also recurses into the subdirectories,
29 # remember to use it without working dir.
29 # remember to use it without working dir.
30 try:
30 try:
31 recurse = {'*': False, '**': True}[roottail]
31 recurse = {'*': False, '**': True}[roottail]
32 except KeyError:
32 except KeyError:
33 repos.append((prefix, root))
33 repos.append((prefix, root))
34 continue
34 continue
35 roothead = os.path.normpath(os.path.abspath(roothead))
35 roothead = os.path.normpath(os.path.abspath(roothead))
36 for path in util.walkrepos(roothead, followsym=True, recurse=recurse):
36 paths = util.walkrepos(roothead, followsym=True, recurse=recurse)
37 path = os.path.normpath(path)
37 repos.extend(urlrepos(prefix, roothead, paths))
38 name = util.pconvert(path[len(roothead):]).strip('/')
39 if prefix:
40 name = prefix + '/' + name
41 repos.append((name, path))
42 return repos
38 return repos
43
39
40 def urlrepos(prefix, roothead, paths):
41 """yield url paths and filesystem paths from a list of repo paths
42
43 >>> list(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
44 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg/', '/opt')]
45 >>> list(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
46 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
47 """
48 for path in paths:
49 path = os.path.normpath(path)
50 name = util.pconvert(path[len(roothead):]).strip('/')
51 if prefix:
52 name = prefix + '/' + name
53 yield name, path
54
44 class hgwebdir(object):
55 class hgwebdir(object):
45 refreshinterval = 20
56 refreshinterval = 20
46
57
47 def __init__(self, conf, baseui=None):
58 def __init__(self, conf, baseui=None):
48 self.conf = conf
59 self.conf = conf
49 self.baseui = baseui
60 self.baseui = baseui
50 self.lastrefresh = 0
61 self.lastrefresh = 0
51 self.motd = None
62 self.motd = None
52 self.refresh()
63 self.refresh()
53
64
54 def refresh(self):
65 def refresh(self):
55 if self.lastrefresh + self.refreshinterval > time.time():
66 if self.lastrefresh + self.refreshinterval > time.time():
56 return
67 return
57
68
58 if self.baseui:
69 if self.baseui:
59 u = self.baseui.copy()
70 u = self.baseui.copy()
60 else:
71 else:
61 u = ui.ui()
72 u = ui.ui()
62 u.setconfig('ui', 'report_untrusted', 'off')
73 u.setconfig('ui', 'report_untrusted', 'off')
63 u.setconfig('ui', 'interactive', 'off')
74 u.setconfig('ui', 'interactive', 'off')
64
75
65 if not isinstance(self.conf, (dict, list, tuple)):
76 if not isinstance(self.conf, (dict, list, tuple)):
66 map = {'paths': 'hgweb-paths'}
77 map = {'paths': 'hgweb-paths'}
67 if not os.path.exists(self.conf):
78 if not os.path.exists(self.conf):
68 raise util.Abort(_('config file %s not found!') % self.conf)
79 raise util.Abort(_('config file %s not found!') % self.conf)
69 u.readconfig(self.conf, remap=map, trust=True)
80 u.readconfig(self.conf, remap=map, trust=True)
70 paths = u.configitems('hgweb-paths')
81 paths = u.configitems('hgweb-paths')
71 elif isinstance(self.conf, (list, tuple)):
82 elif isinstance(self.conf, (list, tuple)):
72 paths = self.conf
83 paths = self.conf
73 elif isinstance(self.conf, dict):
84 elif isinstance(self.conf, dict):
74 paths = self.conf.items()
85 paths = self.conf.items()
75
86
76 repos = findrepos(paths)
87 repos = findrepos(paths)
77 for prefix, root in u.configitems('collections'):
88 for prefix, root in u.configitems('collections'):
78 prefix = util.pconvert(prefix)
89 prefix = util.pconvert(prefix)
79 for path in util.walkrepos(root, followsym=True):
90 for path in util.walkrepos(root, followsym=True):
80 repo = os.path.normpath(path)
91 repo = os.path.normpath(path)
81 name = util.pconvert(repo)
92 name = util.pconvert(repo)
82 if name.startswith(prefix):
93 if name.startswith(prefix):
83 name = name[len(prefix):]
94 name = name[len(prefix):]
84 repos.append((name.lstrip('/'), repo))
95 repos.append((name.lstrip('/'), repo))
85
96
86 self.repos = repos
97 self.repos = repos
87 self.ui = u
98 self.ui = u
88 encoding.encoding = self.ui.config('web', 'encoding',
99 encoding.encoding = self.ui.config('web', 'encoding',
89 encoding.encoding)
100 encoding.encoding)
90 self.style = self.ui.config('web', 'style', 'paper')
101 self.style = self.ui.config('web', 'style', 'paper')
91 self.templatepath = self.ui.config('web', 'templates', None)
102 self.templatepath = self.ui.config('web', 'templates', None)
92 self.stripecount = self.ui.config('web', 'stripes', 1)
103 self.stripecount = self.ui.config('web', 'stripes', 1)
93 if self.stripecount:
104 if self.stripecount:
94 self.stripecount = int(self.stripecount)
105 self.stripecount = int(self.stripecount)
95 self._baseurl = self.ui.config('web', 'baseurl')
106 self._baseurl = self.ui.config('web', 'baseurl')
96 self.lastrefresh = time.time()
107 self.lastrefresh = time.time()
97
108
98 def run(self):
109 def run(self):
99 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
110 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
100 raise RuntimeError("This function is only intended to be "
111 raise RuntimeError("This function is only intended to be "
101 "called while running as a CGI script.")
112 "called while running as a CGI script.")
102 import mercurial.hgweb.wsgicgi as wsgicgi
113 import mercurial.hgweb.wsgicgi as wsgicgi
103 wsgicgi.launch(self)
114 wsgicgi.launch(self)
104
115
105 def __call__(self, env, respond):
116 def __call__(self, env, respond):
106 req = wsgirequest(env, respond)
117 req = wsgirequest(env, respond)
107 return self.run_wsgi(req)
118 return self.run_wsgi(req)
108
119
109 def read_allowed(self, ui, req):
120 def read_allowed(self, ui, req):
110 """Check allow_read and deny_read config options of a repo's ui object
121 """Check allow_read and deny_read config options of a repo's ui object
111 to determine user permissions. By default, with neither option set (or
122 to determine user permissions. By default, with neither option set (or
112 both empty), allow all users to read the repo. There are two ways a
123 both empty), allow all users to read the repo. There are two ways a
113 user can be denied read access: (1) deny_read is not empty, and the
124 user can be denied read access: (1) deny_read is not empty, and the
114 user is unauthenticated or deny_read contains user (or *), and (2)
125 user is unauthenticated or deny_read contains user (or *), and (2)
115 allow_read is not empty and the user is not in allow_read. Return True
126 allow_read is not empty and the user is not in allow_read. Return True
116 if user is allowed to read the repo, else return False."""
127 if user is allowed to read the repo, else return False."""
117
128
118 user = req.env.get('REMOTE_USER')
129 user = req.env.get('REMOTE_USER')
119
130
120 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
131 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
121 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
132 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
122 return False
133 return False
123
134
124 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
135 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
125 # by default, allow reading if no allow_read option has been set
136 # by default, allow reading if no allow_read option has been set
126 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
137 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
127 return True
138 return True
128
139
129 return False
140 return False
130
141
131 def run_wsgi(self, req):
142 def run_wsgi(self, req):
132 try:
143 try:
133 try:
144 try:
134 self.refresh()
145 self.refresh()
135
146
136 virtual = req.env.get("PATH_INFO", "").strip('/')
147 virtual = req.env.get("PATH_INFO", "").strip('/')
137 tmpl = self.templater(req)
148 tmpl = self.templater(req)
138 ctype = tmpl('mimetype', encoding=encoding.encoding)
149 ctype = tmpl('mimetype', encoding=encoding.encoding)
139 ctype = templater.stringify(ctype)
150 ctype = templater.stringify(ctype)
140
151
141 # a static file
152 # a static file
142 if virtual.startswith('static/') or 'static' in req.form:
153 if virtual.startswith('static/') or 'static' in req.form:
143 if virtual.startswith('static/'):
154 if virtual.startswith('static/'):
144 fname = virtual[7:]
155 fname = virtual[7:]
145 else:
156 else:
146 fname = req.form['static'][0]
157 fname = req.form['static'][0]
147 static = templater.templatepath('static')
158 static = templater.templatepath('static')
148 return (staticfile(static, fname, req),)
159 return (staticfile(static, fname, req),)
149
160
150 # top-level index
161 # top-level index
151 elif not virtual:
162 elif not virtual:
152 req.respond(HTTP_OK, ctype)
163 req.respond(HTTP_OK, ctype)
153 return self.makeindex(req, tmpl)
164 return self.makeindex(req, tmpl)
154
165
155 # nested indexes and hgwebs
166 # nested indexes and hgwebs
156
167
157 repos = dict(self.repos)
168 repos = dict(self.repos)
158 virtualrepo = virtual
169 virtualrepo = virtual
159 while virtualrepo:
170 while virtualrepo:
160 real = repos.get(virtualrepo)
171 real = repos.get(virtualrepo)
161 if real:
172 if real:
162 req.env['REPO_NAME'] = virtualrepo
173 req.env['REPO_NAME'] = virtualrepo
163 try:
174 try:
164 repo = hg.repository(self.ui, real)
175 repo = hg.repository(self.ui, real)
165 return hgweb(repo).run_wsgi(req)
176 return hgweb(repo).run_wsgi(req)
166 except IOError, inst:
177 except IOError, inst:
167 msg = inst.strerror
178 msg = inst.strerror
168 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
179 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
169 except error.RepoError, inst:
180 except error.RepoError, inst:
170 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
181 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
171
182
172 up = virtualrepo.rfind('/')
183 up = virtualrepo.rfind('/')
173 if up < 0:
184 if up < 0:
174 break
185 break
175 virtualrepo = virtualrepo[:up]
186 virtualrepo = virtualrepo[:up]
176
187
177 # browse subdirectories
188 # browse subdirectories
178 subdir = virtual + '/'
189 subdir = virtual + '/'
179 if [r for r in repos if r.startswith(subdir)]:
190 if [r for r in repos if r.startswith(subdir)]:
180 req.respond(HTTP_OK, ctype)
191 req.respond(HTTP_OK, ctype)
181 return self.makeindex(req, tmpl, subdir)
192 return self.makeindex(req, tmpl, subdir)
182
193
183 # prefixes not found
194 # prefixes not found
184 req.respond(HTTP_NOT_FOUND, ctype)
195 req.respond(HTTP_NOT_FOUND, ctype)
185 return tmpl("notfound", repo=virtual)
196 return tmpl("notfound", repo=virtual)
186
197
187 except ErrorResponse, err:
198 except ErrorResponse, err:
188 req.respond(err, ctype)
199 req.respond(err, ctype)
189 return tmpl('error', error=err.message or '')
200 return tmpl('error', error=err.message or '')
190 finally:
201 finally:
191 tmpl = None
202 tmpl = None
192
203
193 def makeindex(self, req, tmpl, subdir=""):
204 def makeindex(self, req, tmpl, subdir=""):
194
205
195 def archivelist(ui, nodeid, url):
206 def archivelist(ui, nodeid, url):
196 allowed = ui.configlist("web", "allow_archive", untrusted=True)
207 allowed = ui.configlist("web", "allow_archive", untrusted=True)
197 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
208 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
198 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
209 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
199 untrusted=True):
210 untrusted=True):
200 yield {"type" : i[0], "extension": i[1],
211 yield {"type" : i[0], "extension": i[1],
201 "node": nodeid, "url": url}
212 "node": nodeid, "url": url}
202
213
203 def rawentries(subdir="", **map):
214 def rawentries(subdir="", **map):
204
215
205 descend = self.ui.configbool('web', 'descend', True)
216 descend = self.ui.configbool('web', 'descend', True)
206 for name, path in self.repos:
217 for name, path in self.repos:
207
218
208 if not name.startswith(subdir):
219 if not name.startswith(subdir):
209 continue
220 continue
210 name = name[len(subdir):]
221 name = name[len(subdir):]
211 if not descend and '/' in name:
222 if not descend and '/' in name:
212 continue
223 continue
213
224
214 u = self.ui.copy()
225 u = self.ui.copy()
215 try:
226 try:
216 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
227 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
217 except Exception, e:
228 except Exception, e:
218 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
229 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
219 continue
230 continue
220 def get(section, name, default=None):
231 def get(section, name, default=None):
221 return u.config(section, name, default, untrusted=True)
232 return u.config(section, name, default, untrusted=True)
222
233
223 if u.configbool("web", "hidden", untrusted=True):
234 if u.configbool("web", "hidden", untrusted=True):
224 continue
235 continue
225
236
226 if not self.read_allowed(u, req):
237 if not self.read_allowed(u, req):
227 continue
238 continue
228
239
229 parts = [name]
240 parts = [name]
230 if 'PATH_INFO' in req.env:
241 if 'PATH_INFO' in req.env:
231 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
242 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
232 if req.env['SCRIPT_NAME']:
243 if req.env['SCRIPT_NAME']:
233 parts.insert(0, req.env['SCRIPT_NAME'])
244 parts.insert(0, req.env['SCRIPT_NAME'])
234 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
245 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
235
246
236 # update time with local timezone
247 # update time with local timezone
237 try:
248 try:
238 r = hg.repository(self.ui, path)
249 r = hg.repository(self.ui, path)
239 except error.RepoError:
250 except error.RepoError:
240 u.warn(_('error accessing repository at %s\n') % path)
251 u.warn(_('error accessing repository at %s\n') % path)
241 continue
252 continue
242 try:
253 try:
243 d = (get_mtime(r.spath), util.makedate()[1])
254 d = (get_mtime(r.spath), util.makedate()[1])
244 except OSError:
255 except OSError:
245 continue
256 continue
246
257
247 contact = get_contact(get)
258 contact = get_contact(get)
248 description = get("web", "description", "")
259 description = get("web", "description", "")
249 name = get("web", "name", name)
260 name = get("web", "name", name)
250 row = dict(contact=contact or "unknown",
261 row = dict(contact=contact or "unknown",
251 contact_sort=contact.upper() or "unknown",
262 contact_sort=contact.upper() or "unknown",
252 name=name,
263 name=name,
253 name_sort=name,
264 name_sort=name,
254 url=url,
265 url=url,
255 description=description or "unknown",
266 description=description or "unknown",
256 description_sort=description.upper() or "unknown",
267 description_sort=description.upper() or "unknown",
257 lastchange=d,
268 lastchange=d,
258 lastchange_sort=d[1]-d[0],
269 lastchange_sort=d[1]-d[0],
259 archives=archivelist(u, "tip", url))
270 archives=archivelist(u, "tip", url))
260 yield row
271 yield row
261
272
262 sortdefault = None, False
273 sortdefault = None, False
263 def entries(sortcolumn="", descending=False, subdir="", **map):
274 def entries(sortcolumn="", descending=False, subdir="", **map):
264 rows = rawentries(subdir=subdir, **map)
275 rows = rawentries(subdir=subdir, **map)
265
276
266 if sortcolumn and sortdefault != (sortcolumn, descending):
277 if sortcolumn and sortdefault != (sortcolumn, descending):
267 sortkey = '%s_sort' % sortcolumn
278 sortkey = '%s_sort' % sortcolumn
268 rows = sorted(rows, key=lambda x: x[sortkey],
279 rows = sorted(rows, key=lambda x: x[sortkey],
269 reverse=descending)
280 reverse=descending)
270 for row, parity in zip(rows, paritygen(self.stripecount)):
281 for row, parity in zip(rows, paritygen(self.stripecount)):
271 row['parity'] = parity
282 row['parity'] = parity
272 yield row
283 yield row
273
284
274 self.refresh()
285 self.refresh()
275 sortable = ["name", "description", "contact", "lastchange"]
286 sortable = ["name", "description", "contact", "lastchange"]
276 sortcolumn, descending = sortdefault
287 sortcolumn, descending = sortdefault
277 if 'sort' in req.form:
288 if 'sort' in req.form:
278 sortcolumn = req.form['sort'][0]
289 sortcolumn = req.form['sort'][0]
279 descending = sortcolumn.startswith('-')
290 descending = sortcolumn.startswith('-')
280 if descending:
291 if descending:
281 sortcolumn = sortcolumn[1:]
292 sortcolumn = sortcolumn[1:]
282 if sortcolumn not in sortable:
293 if sortcolumn not in sortable:
283 sortcolumn = ""
294 sortcolumn = ""
284
295
285 sort = [("sort_%s" % column,
296 sort = [("sort_%s" % column,
286 "%s%s" % ((not descending and column == sortcolumn)
297 "%s%s" % ((not descending and column == sortcolumn)
287 and "-" or "", column))
298 and "-" or "", column))
288 for column in sortable]
299 for column in sortable]
289
300
290 self.refresh()
301 self.refresh()
291 self.updatereqenv(req.env)
302 self.updatereqenv(req.env)
292
303
293 return tmpl("index", entries=entries, subdir=subdir,
304 return tmpl("index", entries=entries, subdir=subdir,
294 sortcolumn=sortcolumn, descending=descending,
305 sortcolumn=sortcolumn, descending=descending,
295 **dict(sort))
306 **dict(sort))
296
307
297 def templater(self, req):
308 def templater(self, req):
298
309
299 def header(**map):
310 def header(**map):
300 yield tmpl('header', encoding=encoding.encoding, **map)
311 yield tmpl('header', encoding=encoding.encoding, **map)
301
312
302 def footer(**map):
313 def footer(**map):
303 yield tmpl("footer", **map)
314 yield tmpl("footer", **map)
304
315
305 def motd(**map):
316 def motd(**map):
306 if self.motd is not None:
317 if self.motd is not None:
307 yield self.motd
318 yield self.motd
308 else:
319 else:
309 yield config('web', 'motd', '')
320 yield config('web', 'motd', '')
310
321
311 def config(section, name, default=None, untrusted=True):
322 def config(section, name, default=None, untrusted=True):
312 return self.ui.config(section, name, default, untrusted)
323 return self.ui.config(section, name, default, untrusted)
313
324
314 self.updatereqenv(req.env)
325 self.updatereqenv(req.env)
315
326
316 url = req.env.get('SCRIPT_NAME', '')
327 url = req.env.get('SCRIPT_NAME', '')
317 if not url.endswith('/'):
328 if not url.endswith('/'):
318 url += '/'
329 url += '/'
319
330
320 vars = {}
331 vars = {}
321 styles = (
332 styles = (
322 req.form.get('style', [None])[0],
333 req.form.get('style', [None])[0],
323 config('web', 'style'),
334 config('web', 'style'),
324 'paper'
335 'paper'
325 )
336 )
326 style, mapfile = templater.stylemap(styles, self.templatepath)
337 style, mapfile = templater.stylemap(styles, self.templatepath)
327 if style == styles[0]:
338 if style == styles[0]:
328 vars['style'] = style
339 vars['style'] = style
329
340
330 start = url[-1] == '?' and '&' or '?'
341 start = url[-1] == '?' and '&' or '?'
331 sessionvars = webutil.sessionvars(vars, start)
342 sessionvars = webutil.sessionvars(vars, start)
332 staticurl = config('web', 'staticurl') or url + 'static/'
343 staticurl = config('web', 'staticurl') or url + 'static/'
333 if not staticurl.endswith('/'):
344 if not staticurl.endswith('/'):
334 staticurl += '/'
345 staticurl += '/'
335
346
336 tmpl = templater.templater(mapfile,
347 tmpl = templater.templater(mapfile,
337 defaults={"header": header,
348 defaults={"header": header,
338 "footer": footer,
349 "footer": footer,
339 "motd": motd,
350 "motd": motd,
340 "url": url,
351 "url": url,
341 "staticurl": staticurl,
352 "staticurl": staticurl,
342 "sessionvars": sessionvars})
353 "sessionvars": sessionvars})
343 return tmpl
354 return tmpl
344
355
345 def updatereqenv(self, env):
356 def updatereqenv(self, env):
346 def splitnetloc(netloc):
357 def splitnetloc(netloc):
347 if ':' in netloc:
358 if ':' in netloc:
348 return netloc.split(':', 1)
359 return netloc.split(':', 1)
349 else:
360 else:
350 return (netloc, None)
361 return (netloc, None)
351
362
352 if self._baseurl is not None:
363 if self._baseurl is not None:
353 urlcomp = urlparse.urlparse(self._baseurl)
364 urlcomp = urlparse.urlparse(self._baseurl)
354 host, port = splitnetloc(urlcomp[1])
365 host, port = splitnetloc(urlcomp[1])
355 path = urlcomp[2]
366 path = urlcomp[2]
356 env['SERVER_NAME'] = host
367 env['SERVER_NAME'] = host
357 if port:
368 if port:
358 env['SERVER_PORT'] = port
369 env['SERVER_PORT'] = port
359 env['SCRIPT_NAME'] = path
370 env['SCRIPT_NAME'] = path
@@ -1,26 +1,29 b''
1 # this is hack to make sure no escape characters are inserted into the output
1 # this is hack to make sure no escape characters are inserted into the output
2 import os
2 import os
3 if 'TERM' in os.environ:
3 if 'TERM' in os.environ:
4 del os.environ['TERM']
4 del os.environ['TERM']
5 import doctest
5 import doctest
6
6
7 import mercurial.changelog
7 import mercurial.changelog
8 doctest.testmod(mercurial.changelog)
8 doctest.testmod(mercurial.changelog)
9
9
10 import mercurial.dagparser
10 import mercurial.dagparser
11 doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE)
11 doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE)
12
12
13 import mercurial.match
13 import mercurial.match
14 doctest.testmod(mercurial.match)
14 doctest.testmod(mercurial.match)
15
15
16 import mercurial.url
16 import mercurial.url
17 doctest.testmod(mercurial.url)
17 doctest.testmod(mercurial.url)
18
18
19 import mercurial.util
19 import mercurial.util
20 doctest.testmod(mercurial.util)
20 doctest.testmod(mercurial.util)
21
21
22 import mercurial.encoding
22 import mercurial.encoding
23 doctest.testmod(mercurial.encoding)
23 doctest.testmod(mercurial.encoding)
24
24
25 import mercurial.hgweb.hgwebdir_mod
26 doctest.testmod(mercurial.hgweb.hgwebdir_mod)
27
25 import hgext.convert.cvsps
28 import hgext.convert.cvsps
26 doctest.testmod(hgext.convert.cvsps)
29 doctest.testmod(hgext.convert.cvsps)
General Comments 0
You need to be logged in to leave comments. Login now