comparison mercurial/hgweb/webcommands.py @ 36877:9fc3d814646e

hgweb: port most @webcommand to use modern response type This only focused on porting the return value. raw file requests are wonky because they go through a separate code path at the dispatch layer. Now that everyone is using the same API, we could clean this up. It's worth noting that wsgirequest.respond() allows sending the Content-Disposition header, but the only user of that feature was removed as part of this change (with the setting of the header now being performed inline). A few @webcommand are not as straightforward as the others and they have not been ported yet. Differential Revision: https://phab.mercurial-scm.org/D2787
author Gregory Szorc <gregory.szorc@gmail.com>
date Sat, 10 Mar 2018 20:36:34 -0800
parents 1f42d621f090
children 89002d07a114
comparison
equal deleted inserted replaced
36876:1f42d621f090 36877:9fc3d814646e
104 def rawfile(web, req, tmpl): 104 def rawfile(web, req, tmpl):
105 guessmime = web.configbool('web', 'guessmime') 105 guessmime = web.configbool('web', 'guessmime')
106 106
107 path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', '')) 107 path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
108 if not path: 108 if not path:
109 content = manifest(web, req, tmpl) 109 return manifest(web, req, tmpl)
110 req.respond(HTTP_OK, web.ctype)
111 return content
112 110
113 try: 111 try:
114 fctx = webutil.filectx(web.repo, req) 112 fctx = webutil.filectx(web.repo, req)
115 except error.LookupError as inst: 113 except error.LookupError as inst:
116 try: 114 try:
117 content = manifest(web, req, tmpl) 115 return manifest(web, req, tmpl)
118 req.respond(HTTP_OK, web.ctype)
119 return content
120 except ErrorResponse: 116 except ErrorResponse:
121 raise inst 117 raise inst
122 118
123 path = fctx.path() 119 path = fctx.path()
124 text = fctx.data() 120 text = fctx.data()
131 else: 127 else:
132 mt = 'text/plain' 128 mt = 'text/plain'
133 if mt.startswith('text/'): 129 if mt.startswith('text/'):
134 mt += '; charset="%s"' % encoding.encoding 130 mt += '; charset="%s"' % encoding.encoding
135 131
136 req.respond(HTTP_OK, mt, path, body=text) 132 web.res.headers['Content-Type'] = mt
137 return [] 133 filename = (path.rpartition('/')[-1]
134 .replace('\\', '\\\\').replace('"', '\\"'))
135 web.res.headers['Content-Disposition'] = 'inline; filename="%s"' % filename
136 web.res.setbodybytes(text)
137 return web.res
138 138
139 def _filerevision(web, req, tmpl, fctx): 139 def _filerevision(web, req, tmpl, fctx):
140 f = fctx.path() 140 f = fctx.path()
141 text = fctx.data() 141 text = fctx.data()
142 parity = paritygen(web.stripecount) 142 parity = paritygen(web.stripecount)
151 yield {"line": t, 151 yield {"line": t,
152 "lineid": "l%d" % (lineno + 1), 152 "lineid": "l%d" % (lineno + 1),
153 "linenumber": "% 6d" % (lineno + 1), 153 "linenumber": "% 6d" % (lineno + 1),
154 "parity": next(parity)} 154 "parity": next(parity)}
155 155
156 return tmpl("filerevision", 156 web.res.setbodygen(tmpl(
157 file=f, 157 'filerevision',
158 path=webutil.up(f), 158 file=f,
159 text=lines(), 159 path=webutil.up(f),
160 symrev=webutil.symrevorshortnode(req, fctx), 160 text=lines(),
161 rename=webutil.renamelink(fctx), 161 symrev=webutil.symrevorshortnode(req, fctx),
162 permissions=fctx.manifest().flags(f), 162 rename=webutil.renamelink(fctx),
163 ishead=int(ishead), 163 permissions=fctx.manifest().flags(f),
164 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx))) 164 ishead=int(ishead),
165 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx))))
166
167 return web.res
165 168
166 @webcommand('file') 169 @webcommand('file')
167 def file(web, req, tmpl): 170 def file(web, req, tmpl):
168 """ 171 """
169 /file/{revision}[/{path}] 172 /file/{revision}[/{path}]
333 searchfunc = searchfuncs[mode] 336 searchfunc = searchfuncs[mode]
334 337
335 tip = web.repo['tip'] 338 tip = web.repo['tip']
336 parity = paritygen(web.stripecount) 339 parity = paritygen(web.stripecount)
337 340
338 return tmpl('search', query=query, node=tip.hex(), symrev='tip', 341 web.res.setbodygen(tmpl(
339 entries=changelist, archives=web.archivelist("tip"), 342 'search',
340 morevars=morevars, lessvars=lessvars, 343 query=query,
341 modedesc=searchfunc[1], 344 node=tip.hex(),
342 showforcekw=showforcekw, showunforcekw=showunforcekw) 345 symrev='tip',
346 entries=changelist,
347 archives=web.archivelist('tip'),
348 morevars=morevars,
349 lessvars=lessvars,
350 modedesc=searchfunc[1],
351 showforcekw=showforcekw,
352 showunforcekw=showunforcekw))
353
354 return web.res
343 355
344 @webcommand('changelog') 356 @webcommand('changelog')
345 def changelog(web, req, tmpl, shortlog=False): 357 def changelog(web, req, tmpl, shortlog=False):
346 """ 358 """
347 /changelog[/{revision}] 359 /changelog[/{revision}]
421 nextentry = entries[-1:] 433 nextentry = entries[-1:]
422 entries = entries[:-1] 434 entries = entries[:-1]
423 else: 435 else:
424 nextentry = [] 436 nextentry = []
425 437
426 return tmpl('shortlog' if shortlog else 'changelog', changenav=changenav, 438 web.res.setbodygen(tmpl(
427 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count, 439 'shortlog' if shortlog else 'changelog',
428 entries=entries, 440 changenav=changenav,
429 latestentry=latestentry, nextentry=nextentry, 441 node=ctx.hex(),
430 archives=web.archivelist("tip"), revcount=revcount, 442 rev=pos,
431 morevars=morevars, lessvars=lessvars, query=query) 443 symrev=symrev,
444 changesets=count,
445 entries=entries,
446 latestentry=latestentry,
447 nextentry=nextentry,
448 archives=web.archivelist('tip'),
449 revcount=revcount,
450 morevars=morevars,
451 lessvars=lessvars,
452 query=query))
453
454 return web.res
432 455
433 @webcommand('shortlog') 456 @webcommand('shortlog')
434 def shortlog(web, req, tmpl): 457 def shortlog(web, req, tmpl):
435 """ 458 """
436 /shortlog 459 /shortlog
459 The ``changeset`` template is rendered. Contents of the ``changesettag``, 482 The ``changeset`` template is rendered. Contents of the ``changesettag``,
460 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many 483 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
461 templates related to diffs may all be used to produce the output. 484 templates related to diffs may all be used to produce the output.
462 """ 485 """
463 ctx = webutil.changectx(web.repo, req) 486 ctx = webutil.changectx(web.repo, req)
464 487 web.res.setbodygen(tmpl('changeset',
465 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx)) 488 **webutil.changesetentry(web, req, tmpl, ctx)))
489 return web.res
466 490
467 rev = webcommand('rev')(changeset) 491 rev = webcommand('rev')(changeset)
468 492
469 def decodepath(path): 493 def decodepath(path):
470 """Hook for mapping a path in the repository to a path in the 494 """Hook for mapping a path in the repository to a path in the
561 yield {"parity": next(parity), 585 yield {"parity": next(parity),
562 "path": path, 586 "path": path,
563 "emptydirs": "/".join(emptydirs), 587 "emptydirs": "/".join(emptydirs),
564 "basename": d} 588 "basename": d}
565 589
566 return tmpl("manifest", 590 web.res.setbodygen(tmpl(
567 symrev=symrev, 591 'manifest',
568 path=abspath, 592 symrev=symrev,
569 up=webutil.up(abspath), 593 path=abspath,
570 upparity=next(parity), 594 up=webutil.up(abspath),
571 fentries=filelist, 595 upparity=next(parity),
572 dentries=dirlist, 596 fentries=filelist,
573 archives=web.archivelist(hex(node)), 597 dentries=dirlist,
574 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))) 598 archives=web.archivelist(hex(node)),
599 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))))
600
601 return web.res
575 602
576 @webcommand('tags') 603 @webcommand('tags')
577 def tags(web, req, tmpl): 604 def tags(web, req, tmpl):
578 """ 605 """
579 /tags 606 /tags
598 yield {"parity": next(parity), 625 yield {"parity": next(parity),
599 "tag": k, 626 "tag": k,
600 "date": web.repo[n].date(), 627 "date": web.repo[n].date(),
601 "node": hex(n)} 628 "node": hex(n)}
602 629
603 return tmpl("tags", 630 web.res.setbodygen(tmpl(
604 node=hex(web.repo.changelog.tip()), 631 'tags',
605 entries=lambda **x: entries(False, False, **x), 632 node=hex(web.repo.changelog.tip()),
606 entriesnotip=lambda **x: entries(True, False, **x), 633 entries=lambda **x: entries(False, False, **x),
607 latestentry=lambda **x: entries(True, True, **x)) 634 entriesnotip=lambda **x: entries(True, False, **x),
635 latestentry=lambda **x: entries(True, True, **x)))
636
637 return web.res
608 638
609 @webcommand('bookmarks') 639 @webcommand('bookmarks')
610 def bookmarks(web, req, tmpl): 640 def bookmarks(web, req, tmpl):
611 """ 641 """
612 /bookmarks 642 /bookmarks
636 if i: 666 if i:
637 latestrev = i[0][1] 667 latestrev = i[0][1]
638 else: 668 else:
639 latestrev = -1 669 latestrev = -1
640 670
641 return tmpl("bookmarks", 671 web.res.setbodygen(tmpl(
642 node=hex(web.repo.changelog.tip()), 672 'bookmarks',
643 lastchange=[{"date": web.repo[latestrev].date()}], 673 node=hex(web.repo.changelog.tip()),
644 entries=lambda **x: entries(latestonly=False, **x), 674 lastchange=[{'date': web.repo[latestrev].date()}],
645 latestentry=lambda **x: entries(latestonly=True, **x)) 675 entries=lambda **x: entries(latestonly=False, **x),
676 latestentry=lambda **x: entries(latestonly=True, **x)))
677
678 return web.res
646 679
647 @webcommand('branches') 680 @webcommand('branches')
648 def branches(web, req, tmpl): 681 def branches(web, req, tmpl):
649 """ 682 """
650 /branches 683 /branches
658 691
659 The ``branches`` template is rendered. 692 The ``branches`` template is rendered.
660 """ 693 """
661 entries = webutil.branchentries(web.repo, web.stripecount) 694 entries = webutil.branchentries(web.repo, web.stripecount)
662 latestentry = webutil.branchentries(web.repo, web.stripecount, 1) 695 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
663 return tmpl('branches', node=hex(web.repo.changelog.tip()), 696
664 entries=entries, latestentry=latestentry) 697 web.res.setbodygen(tmpl(
698 'branches',
699 node=hex(web.repo.changelog.tip()),
700 entries=entries,
701 latestentry=latestentry))
702
703 return web.res
665 704
666 @webcommand('summary') 705 @webcommand('summary')
667 def summary(web, req, tmpl): 706 def summary(web, req, tmpl):
668 """ 707 """
669 /summary 708 /summary
729 end = min(count, start + web.maxchanges) 768 end = min(count, start + web.maxchanges)
730 769
731 desc = web.config("web", "description") 770 desc = web.config("web", "description")
732 if not desc: 771 if not desc:
733 desc = 'unknown' 772 desc = 'unknown'
734 return tmpl("summary", 773
735 desc=desc, 774 web.res.setbodygen(tmpl(
736 owner=get_contact(web.config) or "unknown", 775 'summary',
737 lastchange=tip.date(), 776 desc=desc,
738 tags=tagentries, 777 owner=get_contact(web.config) or 'unknown',
739 bookmarks=bookmarks, 778 lastchange=tip.date(),
740 branches=webutil.branchentries(web.repo, web.stripecount, 10), 779 tags=tagentries,
741 shortlog=changelist, 780 bookmarks=bookmarks,
742 node=tip.hex(), 781 branches=webutil.branchentries(web.repo, web.stripecount, 10),
743 symrev='tip', 782 shortlog=changelist,
744 archives=web.archivelist("tip"), 783 node=tip.hex(),
745 labels=web.configlist('web', 'labels')) 784 symrev='tip',
785 archives=web.archivelist('tip'),
786 labels=web.configlist('web', 'labels')))
787
788 return web.res
746 789
747 @webcommand('filediff') 790 @webcommand('filediff')
748 def filediff(web, req, tmpl): 791 def filediff(web, req, tmpl):
749 """ 792 """
750 /diff/{revision}/{path} 793 /diff/{revision}/{path}
780 rename = webutil.renamelink(fctx) 823 rename = webutil.renamelink(fctx)
781 ctx = fctx 824 ctx = fctx
782 else: 825 else:
783 rename = [] 826 rename = []
784 ctx = ctx 827 ctx = ctx
785 return tmpl("filediff", 828
786 file=path, 829 web.res.setbodygen(tmpl(
787 symrev=webutil.symrevorshortnode(req, ctx), 830 'filediff',
788 rename=rename, 831 file=path,
789 diff=diffs, 832 symrev=webutil.symrevorshortnode(req, ctx),
790 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))) 833 rename=rename,
834 diff=diffs,
835 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))))
836
837 return web.res
791 838
792 diff = webcommand('diff')(filediff) 839 diff = webcommand('diff')(filediff)
793 840
794 @webcommand('comparison') 841 @webcommand('comparison')
795 def comparison(web, req, tmpl): 842 def comparison(web, req, tmpl):
851 rename = webutil.renamelink(fctx) 898 rename = webutil.renamelink(fctx)
852 ctx = fctx 899 ctx = fctx
853 else: 900 else:
854 rename = [] 901 rename = []
855 ctx = ctx 902 ctx = ctx
856 return tmpl('filecomparison', 903
857 file=path, 904 web.res.setbodygen(tmpl(
858 symrev=webutil.symrevorshortnode(req, ctx), 905 'filecomparison',
859 rename=rename, 906 file=path,
860 leftrev=leftrev, 907 symrev=webutil.symrevorshortnode(req, ctx),
861 leftnode=hex(leftnode), 908 rename=rename,
862 rightrev=rightrev, 909 leftrev=leftrev,
863 rightnode=hex(rightnode), 910 leftnode=hex(leftnode),
864 comparison=comparison, 911 rightrev=rightrev,
865 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))) 912 rightnode=hex(rightnode),
913 comparison=comparison,
914 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx))))
915
916 return web.res
866 917
867 @webcommand('annotate') 918 @webcommand('annotate')
868 def annotate(web, req, tmpl): 919 def annotate(web, req, tmpl):
869 """ 920 """
870 /annotate/{revision}/{path} 921 /annotate/{revision}/{path}
942 "revdate": f.date()} 993 "revdate": f.date()}
943 994
944 diffopts = webutil.difffeatureopts(req, web.repo.ui, 'annotate') 995 diffopts = webutil.difffeatureopts(req, web.repo.ui, 'annotate')
945 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults} 996 diffopts = {k: getattr(diffopts, k) for k in diffopts.defaults}
946 997
947 return tmpl("fileannotate", 998 web.res.setbodygen(tmpl(
948 file=f, 999 'fileannotate',
949 annotate=annotate, 1000 file=f,
950 path=webutil.up(f), 1001 annotate=annotate,
951 symrev=webutil.symrevorshortnode(req, fctx), 1002 path=webutil.up(f),
952 rename=webutil.renamelink(fctx), 1003 symrev=webutil.symrevorshortnode(req, fctx),
953 permissions=fctx.manifest().flags(f), 1004 rename=webutil.renamelink(fctx),
954 ishead=int(ishead), 1005 permissions=fctx.manifest().flags(f),
955 diffopts=diffopts, 1006 ishead=int(ishead),
956 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx))) 1007 diffopts=diffopts,
1008 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx))))
1009
1010 return web.res
957 1011
958 @webcommand('filelog') 1012 @webcommand('filelog')
959 def filelog(web, req, tmpl): 1013 def filelog(web, req, tmpl):
960 """ 1014 """
961 /filelog/{revision}/{path} 1015 /filelog/{revision}/{path}
1316 1370
1317 yield entry 1371 yield entry
1318 1372
1319 rows = len(tree) 1373 rows = len(tree)
1320 1374
1321 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount, 1375 web.res.setbodygen(tmpl(
1322 uprev=uprev, 1376 'graph',
1323 lessvars=lessvars, morevars=morevars, downrev=downrev, 1377 rev=rev,
1324 graphvars=graphvars, 1378 symrev=symrev,
1325 rows=rows, 1379 revcount=revcount,
1326 bg_height=bg_height, 1380 uprev=uprev,
1327 changesets=count, 1381 lessvars=lessvars,
1328 nextentry=nextentry, 1382 morevars=morevars,
1329 jsdata=lambda **x: jsdata(), 1383 downrev=downrev,
1330 nodes=lambda **x: nodes(), 1384 graphvars=graphvars,
1331 node=ctx.hex(), changenav=changenav) 1385 rows=rows,
1386 bg_height=bg_height,
1387 changesets=count,
1388 nextentry=nextentry,
1389 jsdata=lambda **x: jsdata(),
1390 nodes=lambda **x: nodes(),
1391 node=ctx.hex(),
1392 changenav=changenav))
1393
1394 return web.res
1332 1395
1333 def _getdoc(e): 1396 def _getdoc(e):
1334 doc = e[0].__doc__ 1397 doc = e[0].__doc__
1335 if doc: 1398 if doc:
1336 doc = _(doc).partition('\n')[0] 1399 doc = _(doc).partition('\n')[0]
1382 1445
1383 def othercommands(**map): 1446 def othercommands(**map):
1384 for c, doc in other: 1447 for c, doc in other:
1385 yield {'topic': c, 'summary': doc} 1448 yield {'topic': c, 'summary': doc}
1386 1449
1387 return tmpl('helptopics', topics=topics, earlycommands=earlycommands, 1450 web.res.setbodygen(tmpl(
1388 othercommands=othercommands, title='Index') 1451 'helptopics',
1452 topics=topics,
1453 earlycommands=earlycommands,
1454 othercommands=othercommands,
1455 title='Index'))
1456 return web.res
1389 1457
1390 # Render an index of sub-topics. 1458 # Render an index of sub-topics.
1391 if topicname in helpmod.subtopics: 1459 if topicname in helpmod.subtopics:
1392 topics = [] 1460 topics = []
1393 for entries, summary, _doc in helpmod.subtopics[topicname]: 1461 for entries, summary, _doc in helpmod.subtopics[topicname]:
1395 'topic': '%s.%s' % (topicname, entries[0]), 1463 'topic': '%s.%s' % (topicname, entries[0]),
1396 'basename': entries[0], 1464 'basename': entries[0],
1397 'summary': summary, 1465 'summary': summary,
1398 }) 1466 })
1399 1467
1400 return tmpl('helptopics', topics=topics, title=topicname, 1468 web.res.setbodygen(tmpl(
1401 subindex=True) 1469 'helptopics',
1470 topics=topics,
1471 title=topicname,
1472 subindex=True))
1473 return web.res
1402 1474
1403 u = webutil.wsgiui.load() 1475 u = webutil.wsgiui.load()
1404 u.verbose = True 1476 u.verbose = True
1405 1477
1406 # Render a page from a sub-topic. 1478 # Render a page from a sub-topic.
1416 1488
1417 try: 1489 try:
1418 doc = helpmod.help_(u, commands, topic, subtopic=subtopic) 1490 doc = helpmod.help_(u, commands, topic, subtopic=subtopic)
1419 except error.Abort: 1491 except error.Abort:
1420 raise ErrorResponse(HTTP_NOT_FOUND) 1492 raise ErrorResponse(HTTP_NOT_FOUND)
1421 return tmpl('help', topic=topicname, doc=doc) 1493
1494 web.res.setbodygen(tmpl(
1495 'help',
1496 topic=topicname,
1497 doc=doc))
1498
1499 return web.res
1422 1500
1423 # tell hggettext to extract docstrings from these functions: 1501 # tell hggettext to extract docstrings from these functions:
1424 i18nfunctions = commands.values() 1502 i18nfunctions = commands.values()