Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/hgwebdir_mod.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | d8e55c0c642c |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
48 webutil, | 48 webutil, |
49 wsgicgi, | 49 wsgicgi, |
50 ) | 50 ) |
51 from ..utils import dateutil | 51 from ..utils import dateutil |
52 | 52 |
53 | |
53 def cleannames(items): | 54 def cleannames(items): |
54 return [(util.pconvert(name).strip('/'), path) for name, path in items] | 55 return [(util.pconvert(name).strip('/'), path) for name, path in items] |
56 | |
55 | 57 |
56 def findrepos(paths): | 58 def findrepos(paths): |
57 repos = [] | 59 repos = [] |
58 for prefix, root in cleannames(paths): | 60 for prefix, root in cleannames(paths): |
59 roothead, roottail = os.path.split(root) | 61 roothead, roottail = os.path.split(root) |
69 roothead = os.path.normpath(os.path.abspath(roothead)) | 71 roothead = os.path.normpath(os.path.abspath(roothead)) |
70 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse) | 72 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse) |
71 repos.extend(urlrepos(prefix, roothead, paths)) | 73 repos.extend(urlrepos(prefix, roothead, paths)) |
72 return repos | 74 return repos |
73 | 75 |
76 | |
74 def urlrepos(prefix, roothead, paths): | 77 def urlrepos(prefix, roothead, paths): |
75 """yield url paths and filesystem paths from a list of repo paths | 78 """yield url paths and filesystem paths from a list of repo paths |
76 | 79 |
77 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq] | 80 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq] |
78 >>> conv(urlrepos(b'hg', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt'])) | 81 >>> conv(urlrepos(b'hg', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt'])) |
80 >>> conv(urlrepos(b'', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt'])) | 83 >>> conv(urlrepos(b'', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt'])) |
81 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')] | 84 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')] |
82 """ | 85 """ |
83 for path in paths: | 86 for path in paths: |
84 path = os.path.normpath(path) | 87 path = os.path.normpath(path) |
85 yield (prefix + '/' + | 88 yield ( |
86 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path | 89 prefix + '/' + util.pconvert(path[len(roothead) :]).lstrip('/') |
90 ).strip('/'), path | |
91 | |
87 | 92 |
88 def readallowed(ui, req): | 93 def readallowed(ui, req): |
89 """Check allow_read and deny_read config options of a repo's ui object | 94 """Check allow_read and deny_read config options of a repo's ui object |
90 to determine user permissions. By default, with neither option set (or | 95 to determine user permissions. By default, with neither option set (or |
91 both empty), allow all users to read the repo. There are two ways a | 96 both empty), allow all users to read the repo. There are two ways a |
105 if not allow_read or ismember(ui, user, allow_read): | 110 if not allow_read or ismember(ui, user, allow_read): |
106 return True | 111 return True |
107 | 112 |
108 return False | 113 return False |
109 | 114 |
115 | |
110 def rawindexentries(ui, repos, req, subdir=''): | 116 def rawindexentries(ui, repos, req, subdir=''): |
111 descend = ui.configbool('web', 'descend') | 117 descend = ui.configbool('web', 'descend') |
112 collapse = ui.configbool('web', 'collapse') | 118 collapse = ui.configbool('web', 'collapse') |
113 seenrepos = set() | 119 seenrepos = set() |
114 seendirs = set() | 120 seendirs = set() |
115 for name, path in repos: | 121 for name, path in repos: |
116 | 122 |
117 if not name.startswith(subdir): | 123 if not name.startswith(subdir): |
118 continue | 124 continue |
119 name = name[len(subdir):] | 125 name = name[len(subdir) :] |
120 directory = False | 126 directory = False |
121 | 127 |
122 if '/' in name: | 128 if '/' in name: |
123 if not descend: | 129 if not descend: |
124 continue | 130 continue |
138 | 144 |
139 # redefine the path to refer to the directory | 145 # redefine the path to refer to the directory |
140 discarded = '/'.join(nameparts[1:]) | 146 discarded = '/'.join(nameparts[1:]) |
141 | 147 |
142 # remove name parts plus accompanying slash | 148 # remove name parts plus accompanying slash |
143 path = path[:-len(discarded) - 1] | 149 path = path[: -len(discarded) - 1] |
144 | 150 |
145 try: | 151 try: |
146 hg.repository(ui, path) | 152 hg.repository(ui, path) |
147 directory = False | 153 directory = False |
148 except (IOError, error.RepoError): | 154 except (IOError, error.RepoError): |
163 except OSError: | 169 except OSError: |
164 continue | 170 continue |
165 | 171 |
166 # add '/' to the name to make it obvious that | 172 # add '/' to the name to make it obvious that |
167 # the entry is a directory, not a regular repository | 173 # the entry is a directory, not a regular repository |
168 row = {'contact': "", | 174 row = { |
169 'contact_sort': "", | 175 'contact': "", |
170 'name': name + '/', | 176 'contact_sort': "", |
171 'name_sort': name, | 177 'name': name + '/', |
172 'url': url, | 178 'name_sort': name, |
173 'description': "", | 179 'url': url, |
174 'description_sort': "", | 180 'description': "", |
175 'lastchange': d, | 181 'description_sort': "", |
176 'lastchange_sort': d[1] - d[0], | 182 'lastchange': d, |
177 'archives': templateutil.mappinglist([]), | 183 'lastchange_sort': d[1] - d[0], |
178 'isdirectory': True, | 184 'archives': templateutil.mappinglist([]), |
179 'labels': templateutil.hybridlist([], name='label'), | 185 'isdirectory': True, |
180 } | 186 'labels': templateutil.hybridlist([], name='label'), |
187 } | |
181 | 188 |
182 seendirs.add(name) | 189 seendirs.add(name) |
183 yield row | 190 yield row |
184 continue | 191 continue |
185 | 192 |
216 contact = get_contact(get) | 223 contact = get_contact(get) |
217 description = get("web", "description") | 224 description = get("web", "description") |
218 seenrepos.add(name) | 225 seenrepos.add(name) |
219 name = get("web", "name", name) | 226 name = get("web", "name", name) |
220 labels = u.configlist('web', 'labels', untrusted=True) | 227 labels = u.configlist('web', 'labels', untrusted=True) |
221 row = {'contact': contact or "unknown", | 228 row = { |
222 'contact_sort': contact.upper() or "unknown", | 229 'contact': contact or "unknown", |
223 'name': name, | 230 'contact_sort': contact.upper() or "unknown", |
224 'name_sort': name, | 231 'name': name, |
225 'url': url, | 232 'name_sort': name, |
226 'description': description or "unknown", | 233 'url': url, |
227 'description_sort': description.upper() or "unknown", | 234 'description': description or "unknown", |
228 'lastchange': d, | 235 'description_sort': description.upper() or "unknown", |
229 'lastchange_sort': d[1] - d[0], | 236 'lastchange': d, |
230 'archives': webutil.archivelist(u, "tip", url), | 237 'lastchange_sort': d[1] - d[0], |
231 'isdirectory': None, | 238 'archives': webutil.archivelist(u, "tip", url), |
232 'labels': templateutil.hybridlist(labels, name='label'), | 239 'isdirectory': None, |
233 } | 240 'labels': templateutil.hybridlist(labels, name='label'), |
241 } | |
234 | 242 |
235 yield row | 243 yield row |
236 | 244 |
237 def _indexentriesgen(context, ui, repos, req, stripecount, sortcolumn, | 245 |
238 descending, subdir): | 246 def _indexentriesgen( |
247 context, ui, repos, req, stripecount, sortcolumn, descending, subdir | |
248 ): | |
239 rows = rawindexentries(ui, repos, req, subdir=subdir) | 249 rows = rawindexentries(ui, repos, req, subdir=subdir) |
240 | 250 |
241 sortdefault = None, False | 251 sortdefault = None, False |
242 | 252 |
243 if sortcolumn and sortdefault != (sortcolumn, descending): | 253 if sortcolumn and sortdefault != (sortcolumn, descending): |
244 sortkey = '%s_sort' % sortcolumn | 254 sortkey = '%s_sort' % sortcolumn |
245 rows = sorted(rows, key=lambda x: x[sortkey], | 255 rows = sorted(rows, key=lambda x: x[sortkey], reverse=descending) |
246 reverse=descending) | |
247 | 256 |
248 for row, parity in zip(rows, paritygen(stripecount)): | 257 for row, parity in zip(rows, paritygen(stripecount)): |
249 row['parity'] = parity | 258 row['parity'] = parity |
250 yield row | 259 yield row |
251 | 260 |
252 def indexentries(ui, repos, req, stripecount, sortcolumn='', | 261 |
253 descending=False, subdir=''): | 262 def indexentries( |
263 ui, repos, req, stripecount, sortcolumn='', descending=False, subdir='' | |
264 ): | |
254 args = (ui, repos, req, stripecount, sortcolumn, descending, subdir) | 265 args = (ui, repos, req, stripecount, sortcolumn, descending, subdir) |
255 return templateutil.mappinggenerator(_indexentriesgen, args=args) | 266 return templateutil.mappinggenerator(_indexentriesgen, args=args) |
256 | 267 |
268 | |
257 class hgwebdir(object): | 269 class hgwebdir(object): |
258 """HTTP server for multiple repositories. | 270 """HTTP server for multiple repositories. |
259 | 271 |
260 Given a configuration, different repositories will be served depending | 272 Given a configuration, different repositories will be served depending |
261 on the request path. | 273 on the request path. |
262 | 274 |
263 Instances are typically used as WSGI applications. | 275 Instances are typically used as WSGI applications. |
264 """ | 276 """ |
277 | |
265 def __init__(self, conf, baseui=None): | 278 def __init__(self, conf, baseui=None): |
266 self.conf = conf | 279 self.conf = conf |
267 self.baseui = baseui | 280 self.baseui = baseui |
268 self.ui = None | 281 self.ui = None |
269 self.lastrefresh = 0 | 282 self.lastrefresh = 0 |
280 else: | 293 else: |
281 item = configitems.coreitems['web']['refreshinterval'] | 294 item = configitems.coreitems['web']['refreshinterval'] |
282 refreshinterval = item.default | 295 refreshinterval = item.default |
283 | 296 |
284 # refreshinterval <= 0 means to always refresh. | 297 # refreshinterval <= 0 means to always refresh. |
285 if (refreshinterval > 0 and | 298 if ( |
286 self.lastrefresh + refreshinterval > time.time()): | 299 refreshinterval > 0 |
300 and self.lastrefresh + refreshinterval > time.time() | |
301 ): | |
287 return | 302 return |
288 | 303 |
289 if self.baseui: | 304 if self.baseui: |
290 u = self.baseui.copy() | 305 u = self.baseui.copy() |
291 else: | 306 else: |
316 prefix = util.pconvert(prefix) | 331 prefix = util.pconvert(prefix) |
317 for path in scmutil.walkrepos(root, followsym=True): | 332 for path in scmutil.walkrepos(root, followsym=True): |
318 repo = os.path.normpath(path) | 333 repo = os.path.normpath(path) |
319 name = util.pconvert(repo) | 334 name = util.pconvert(repo) |
320 if name.startswith(prefix): | 335 if name.startswith(prefix): |
321 name = name[len(prefix):] | 336 name = name[len(prefix) :] |
322 repos.append((name.lstrip('/'), repo)) | 337 repos.append((name.lstrip('/'), repo)) |
323 | 338 |
324 self.repos = repos | 339 self.repos = repos |
325 self.ui = u | 340 self.ui = u |
326 encoding.encoding = self.ui.config('web', 'encoding') | 341 encoding.encoding = self.ui.config('web', 'encoding') |
336 prefix = prefix[:-1] | 351 prefix = prefix[:-1] |
337 self.prefix = prefix | 352 self.prefix = prefix |
338 self.lastrefresh = time.time() | 353 self.lastrefresh = time.time() |
339 | 354 |
340 def run(self): | 355 def run(self): |
341 if not encoding.environ.get('GATEWAY_INTERFACE', | 356 if not encoding.environ.get('GATEWAY_INTERFACE', '').startswith( |
342 '').startswith("CGI/1."): | 357 "CGI/1." |
343 raise RuntimeError("This function is only intended to be " | 358 ): |
344 "called while running as a CGI script.") | 359 raise RuntimeError( |
360 "This function is only intended to be " | |
361 "called while running as a CGI script." | |
362 ) | |
345 wsgicgi.launch(self) | 363 wsgicgi.launch(self) |
346 | 364 |
347 def __call__(self, env, respond): | 365 def __call__(self, env, respond): |
348 baseurl = self.ui.config('web', 'baseurl') | 366 baseurl = self.ui.config('web', 'baseurl') |
349 req = requestmod.parserequestfromenv(env, altbaseurl=baseurl) | 367 req = requestmod.parserequestfromenv(env, altbaseurl=baseurl) |
407 return self.makeindex(req, res, tmpl) | 425 return self.makeindex(req, res, tmpl) |
408 | 426 |
409 # nested indexes and hgwebs | 427 # nested indexes and hgwebs |
410 | 428 |
411 if virtual.endswith('/index') and virtual not in repos: | 429 if virtual.endswith('/index') and virtual not in repos: |
412 subdir = virtual[:-len('index')] | 430 subdir = virtual[: -len('index')] |
413 if any(r.startswith(subdir) for r in repos): | 431 if any(r.startswith(subdir) for r in repos): |
414 return self.makeindex(req, res, tmpl, subdir) | 432 return self.makeindex(req, res, tmpl, subdir) |
415 | 433 |
416 def _virtualdirs(): | 434 def _virtualdirs(): |
417 # Check the full virtual path, and each parent | 435 # Check the full virtual path, and each parent |
424 if real: | 442 if real: |
425 # Re-parse the WSGI environment to take into account our | 443 # Re-parse the WSGI environment to take into account our |
426 # repository path component. | 444 # repository path component. |
427 uenv = req.rawenv | 445 uenv = req.rawenv |
428 if pycompat.ispy3: | 446 if pycompat.ispy3: |
429 uenv = {k.decode('latin1'): v for k, v in | 447 uenv = { |
430 uenv.iteritems()} | 448 k.decode('latin1'): v for k, v in uenv.iteritems() |
449 } | |
431 req = requestmod.parserequestfromenv( | 450 req = requestmod.parserequestfromenv( |
432 uenv, reponame=virtualrepo, | 451 uenv, |
452 reponame=virtualrepo, | |
433 altbaseurl=self.ui.config('web', 'baseurl'), | 453 altbaseurl=self.ui.config('web', 'baseurl'), |
434 # Reuse wrapped body file object otherwise state | 454 # Reuse wrapped body file object otherwise state |
435 # tracking can get confused. | 455 # tracking can get confused. |
436 bodyfh=req.bodyfh) | 456 bodyfh=req.bodyfh, |
457 ) | |
437 try: | 458 try: |
438 # ensure caller gets private copy of ui | 459 # ensure caller gets private copy of ui |
439 repo = hg.repository(self.ui.copy(), real) | 460 repo = hg.repository(self.ui.copy(), real) |
440 return hgweb_mod.hgweb(repo).run_wsgi(req, res) | 461 return hgweb_mod.hgweb(repo).run_wsgi(req, res) |
441 except IOError as inst: | 462 except IOError as inst: |
471 if descending: | 492 if descending: |
472 sortcolumn = sortcolumn[1:] | 493 sortcolumn = sortcolumn[1:] |
473 if sortcolumn not in sortable: | 494 if sortcolumn not in sortable: |
474 sortcolumn = "" | 495 sortcolumn = "" |
475 | 496 |
476 sort = [("sort_%s" % column, | 497 sort = [ |
477 "%s%s" % ((not descending and column == sortcolumn) | 498 ( |
478 and "-" or "", column)) | 499 "sort_%s" % column, |
479 for column in sortable] | 500 "%s%s" |
501 % ( | |
502 (not descending and column == sortcolumn) and "-" or "", | |
503 column, | |
504 ), | |
505 ) | |
506 for column in sortable | |
507 ] | |
480 | 508 |
481 self.refresh() | 509 self.refresh() |
482 | 510 |
483 entries = indexentries(self.ui, self.repos, req, | 511 entries = indexentries( |
484 self.stripecount, sortcolumn=sortcolumn, | 512 self.ui, |
485 descending=descending, subdir=subdir) | 513 self.repos, |
514 req, | |
515 self.stripecount, | |
516 sortcolumn=sortcolumn, | |
517 descending=descending, | |
518 subdir=subdir, | |
519 ) | |
486 | 520 |
487 mapping = { | 521 mapping = { |
488 'entries': entries, | 522 'entries': entries, |
489 'subdir': subdir, | 523 'subdir': subdir, |
490 'pathdef': hgweb_mod.makebreadcrumb('/' + subdir, self.prefix), | 524 'pathdef': hgweb_mod.makebreadcrumb('/' + subdir, self.prefix), |
494 mapping.update(sort) | 528 mapping.update(sort) |
495 res.setbodygen(tmpl.generate('index', mapping)) | 529 res.setbodygen(tmpl.generate('index', mapping)) |
496 return res.sendresponse() | 530 return res.sendresponse() |
497 | 531 |
498 def templater(self, req, nonce): | 532 def templater(self, req, nonce): |
499 | |
500 def config(section, name, default=uimod._unset, untrusted=True): | 533 def config(section, name, default=uimod._unset, untrusted=True): |
501 return self.ui.config(section, name, default, untrusted) | 534 return self.ui.config(section, name, default, untrusted) |
502 | 535 |
503 vars = {} | 536 vars = {} |
504 styles, (style, mapfile) = hgweb_mod.getstyle(req, config, | 537 styles, (style, mapfile) = hgweb_mod.getstyle( |
505 self.templatepath) | 538 req, config, self.templatepath |
539 ) | |
506 if style == styles[0]: | 540 if style == styles[0]: |
507 vars['style'] = style | 541 vars['style'] = style |
508 | 542 |
509 sessionvars = webutil.sessionvars(vars, '?') | 543 sessionvars = webutil.sessionvars(vars, '?') |
510 logourl = config('web', 'logourl') | 544 logourl = config('web', 'logourl') |
511 logoimg = config('web', 'logoimg') | 545 logoimg = config('web', 'logoimg') |
512 staticurl = (config('web', 'staticurl') | 546 staticurl = ( |
513 or req.apppath.rstrip('/') + '/static/') | 547 config('web', 'staticurl') or req.apppath.rstrip('/') + '/static/' |
548 ) | |
514 if not staticurl.endswith('/'): | 549 if not staticurl.endswith('/'): |
515 staticurl += '/' | 550 staticurl += '/' |
516 | 551 |
517 defaults = { | 552 defaults = { |
518 "encoding": encoding.encoding, | 553 "encoding": encoding.encoding, |
523 "sessionvars": sessionvars, | 558 "sessionvars": sessionvars, |
524 "style": style, | 559 "style": style, |
525 "nonce": nonce, | 560 "nonce": nonce, |
526 } | 561 } |
527 templatekeyword = registrar.templatekeyword(defaults) | 562 templatekeyword = registrar.templatekeyword(defaults) |
563 | |
528 @templatekeyword('motd', requires=()) | 564 @templatekeyword('motd', requires=()) |
529 def motd(context, mapping): | 565 def motd(context, mapping): |
530 if self.motd is not None: | 566 if self.motd is not None: |
531 yield self.motd | 567 yield self.motd |
532 else: | 568 else: |