comparison mercurial/hgweb/webcommands.py @ 24076:b53d2afd11fb

webcommands: define web commands using a decorator Other parts of Mercurial have evolved to use decorators to declare commands or handlers. This patch gives the same treatment to web commands.
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 06 Feb 2015 19:06:17 -0800
parents db85e454fccc
children e8046ca0405d
comparison
equal deleted inserted replaced
24075:4bf484276787 24076:b53d2afd11fb
17 from mercurial import scmutil 17 from mercurial import scmutil
18 from mercurial.i18n import _ 18 from mercurial.i18n import _
19 from mercurial.error import ParseError, RepoLookupError, Abort 19 from mercurial.error import ParseError, RepoLookupError, Abort
20 from mercurial import revset 20 from mercurial import revset
21 21
22 # __all__ is populated with the allowed commands. Be sure to add to it if 22 __all__ = []
23 # you're adding a new command, or the new command won't work. 23
24 24 class webcommand(object):
25 __all__ = [ 25 """Decorator used to register a web command handler.
26 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev', 26
27 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff', 27 The decorator takes as its positional arguments the name/path the
28 'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help', 28 command should be accessible under.
29 ] 29
30 30 Usage:
31
32 @webcommand('mycommand')
33 def mycommand(web, req, tmpl):
34 pass
35 """
36
37 def __init__(self, name):
38 self.name = name
39
40 def __call__(self, func):
41 __all__.append(self.name)
42 return func
43
44 @webcommand('log')
31 def log(web, req, tmpl): 45 def log(web, req, tmpl):
32 if 'file' in req.form and req.form['file'][0]: 46 if 'file' in req.form and req.form['file'][0]:
33 return filelog(web, req, tmpl) 47 return filelog(web, req, tmpl)
34 else: 48 else:
35 return changelog(web, req, tmpl) 49 return changelog(web, req, tmpl)
36 50
51 @webcommand('rawfile')
37 def rawfile(web, req, tmpl): 52 def rawfile(web, req, tmpl):
38 guessmime = web.configbool('web', 'guessmime', False) 53 guessmime = web.configbool('web', 'guessmime', False)
39 54
40 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) 55 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
41 if not path: 56 if not path:
96 parent=webutil.parents(fctx), 111 parent=webutil.parents(fctx),
97 child=webutil.children(fctx), 112 child=webutil.children(fctx),
98 rename=webutil.renamelink(fctx), 113 rename=webutil.renamelink(fctx),
99 permissions=fctx.manifest().flags(f)) 114 permissions=fctx.manifest().flags(f))
100 115
116 @webcommand('file')
101 def file(web, req, tmpl): 117 def file(web, req, tmpl):
102 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) 118 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
103 if not path: 119 if not path:
104 return manifest(web, req, tmpl) 120 return manifest(web, req, tmpl)
105 try: 121 try:
265 entries=changelist, archives=web.archivelist("tip"), 281 entries=changelist, archives=web.archivelist("tip"),
266 morevars=morevars, lessvars=lessvars, 282 morevars=morevars, lessvars=lessvars,
267 modedesc=searchfunc[1], 283 modedesc=searchfunc[1],
268 showforcekw=showforcekw, showunforcekw=showunforcekw) 284 showforcekw=showforcekw, showunforcekw=showunforcekw)
269 285
286 @webcommand('changelog')
270 def changelog(web, req, tmpl, shortlog=False): 287 def changelog(web, req, tmpl, shortlog=False):
271 288
272 query = '' 289 query = ''
273 if 'node' in req.form: 290 if 'node' in req.form:
274 ctx = webutil.changectx(web.repo, req) 291 ctx = webutil.changectx(web.repo, req)
324 entries=entries, 341 entries=entries,
325 latestentry=latestentry, nextentry=nextentry, 342 latestentry=latestentry, nextentry=nextentry,
326 archives=web.archivelist("tip"), revcount=revcount, 343 archives=web.archivelist("tip"), revcount=revcount,
327 morevars=morevars, lessvars=lessvars, query=query) 344 morevars=morevars, lessvars=lessvars, query=query)
328 345
346 @webcommand('shortlog')
329 def shortlog(web, req, tmpl): 347 def shortlog(web, req, tmpl):
330 return changelog(web, req, tmpl, shortlog=True) 348 return changelog(web, req, tmpl, shortlog=True)
331 349
350 @webcommand('changeset')
332 def changeset(web, req, tmpl): 351 def changeset(web, req, tmpl):
333 ctx = webutil.changectx(web.repo, req) 352 ctx = webutil.changectx(web.repo, req)
334 basectx = webutil.basechangectx(web.repo, req) 353 basectx = webutil.basechangectx(web.repo, req)
335 if basectx is None: 354 if basectx is None:
336 basectx = ctx.p1() 355 basectx = ctx.p1()
380 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()), 399 bookmarks=webutil.nodebookmarksdict(web.repo, ctx.node()),
381 branch=webutil.nodebranchnodefault(ctx), 400 branch=webutil.nodebranchnodefault(ctx),
382 inbranch=webutil.nodeinbranch(web.repo, ctx), 401 inbranch=webutil.nodeinbranch(web.repo, ctx),
383 branches=webutil.nodebranchdict(web.repo, ctx)) 402 branches=webutil.nodebranchdict(web.repo, ctx))
384 403
385 rev = changeset 404 rev = webcommand('rev')(changeset)
386 405
387 def decodepath(path): 406 def decodepath(path):
388 """Hook for mapping a path in the repository to a path in the 407 """Hook for mapping a path in the repository to a path in the
389 working copy. 408 working copy.
390 409
391 Extensions (e.g., largefiles) can override this to remap files in 410 Extensions (e.g., largefiles) can override this to remap files in
392 the virtual file system presented by the manifest command below.""" 411 the virtual file system presented by the manifest command below."""
393 return path 412 return path
394 413
414 @webcommand('manifest')
395 def manifest(web, req, tmpl): 415 def manifest(web, req, tmpl):
396 ctx = webutil.changectx(web.repo, req) 416 ctx = webutil.changectx(web.repo, req)
397 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) 417 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
398 mf = ctx.manifest() 418 mf = ctx.manifest()
399 node = ctx.node() 419 node = ctx.node()
472 tags=webutil.nodetagsdict(web.repo, node), 492 tags=webutil.nodetagsdict(web.repo, node),
473 bookmarks=webutil.nodebookmarksdict(web.repo, node), 493 bookmarks=webutil.nodebookmarksdict(web.repo, node),
474 inbranch=webutil.nodeinbranch(web.repo, ctx), 494 inbranch=webutil.nodeinbranch(web.repo, ctx),
475 branches=webutil.nodebranchdict(web.repo, ctx)) 495 branches=webutil.nodebranchdict(web.repo, ctx))
476 496
497 @webcommand('tags')
477 def tags(web, req, tmpl): 498 def tags(web, req, tmpl):
478 i = list(reversed(web.repo.tagslist())) 499 i = list(reversed(web.repo.tagslist()))
479 parity = paritygen(web.stripecount) 500 parity = paritygen(web.stripecount)
480 501
481 def entries(notip, latestonly, **map): 502 def entries(notip, latestonly, **map):
494 node=hex(web.repo.changelog.tip()), 515 node=hex(web.repo.changelog.tip()),
495 entries=lambda **x: entries(False, False, **x), 516 entries=lambda **x: entries(False, False, **x),
496 entriesnotip=lambda **x: entries(True, False, **x), 517 entriesnotip=lambda **x: entries(True, False, **x),
497 latestentry=lambda **x: entries(True, True, **x)) 518 latestentry=lambda **x: entries(True, True, **x))
498 519
520 @webcommand('bookmarks')
499 def bookmarks(web, req, tmpl): 521 def bookmarks(web, req, tmpl):
500 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo] 522 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
501 parity = paritygen(web.stripecount) 523 parity = paritygen(web.stripecount)
502 524
503 def entries(latestonly, **map): 525 def entries(latestonly, **map):
514 return tmpl("bookmarks", 536 return tmpl("bookmarks",
515 node=hex(web.repo.changelog.tip()), 537 node=hex(web.repo.changelog.tip()),
516 entries=lambda **x: entries(latestonly=False, **x), 538 entries=lambda **x: entries(latestonly=False, **x),
517 latestentry=lambda **x: entries(latestonly=True, **x)) 539 latestentry=lambda **x: entries(latestonly=True, **x))
518 540
541 @webcommand('branches')
519 def branches(web, req, tmpl): 542 def branches(web, req, tmpl):
520 tips = [] 543 tips = []
521 heads = web.repo.heads() 544 heads = web.repo.heads()
522 parity = paritygen(web.stripecount) 545 parity = paritygen(web.stripecount)
523 sortkey = lambda item: (not item[1], item[0].rev()) 546 sortkey = lambda item: (not item[1], item[0].rev())
545 568
546 return tmpl('branches', node=hex(web.repo.changelog.tip()), 569 return tmpl('branches', node=hex(web.repo.changelog.tip()),
547 entries=lambda **x: entries(0, **x), 570 entries=lambda **x: entries(0, **x),
548 latestentry=lambda **x: entries(1, **x)) 571 latestentry=lambda **x: entries(1, **x))
549 572
573 @webcommand('summary')
550 def summary(web, req, tmpl): 574 def summary(web, req, tmpl):
551 i = reversed(web.repo.tagslist()) 575 i = reversed(web.repo.tagslist())
552 576
553 def tagentries(**map): 577 def tagentries(**map):
554 parity = paritygen(web.stripecount) 578 parity = paritygen(web.stripecount)
630 branches=branches, 654 branches=branches,
631 shortlog=changelist, 655 shortlog=changelist,
632 node=tip.hex(), 656 node=tip.hex(),
633 archives=web.archivelist("tip")) 657 archives=web.archivelist("tip"))
634 658
659 @webcommand('filediff')
635 def filediff(web, req, tmpl): 660 def filediff(web, req, tmpl):
636 fctx, ctx = None, None 661 fctx, ctx = None, None
637 try: 662 try:
638 fctx = webutil.filectx(web.repo, req) 663 fctx = webutil.filectx(web.repo, req)
639 except LookupError: 664 except LookupError:
670 branch=webutil.nodebranchnodefault(ctx), 695 branch=webutil.nodebranchnodefault(ctx),
671 parent=webutil.parents(ctx), 696 parent=webutil.parents(ctx),
672 child=webutil.children(ctx), 697 child=webutil.children(ctx),
673 diff=diffs) 698 diff=diffs)
674 699
675 diff = filediff 700 diff = webcommand('diff')(filediff)
676 701
702 @webcommand('comparison')
677 def comparison(web, req, tmpl): 703 def comparison(web, req, tmpl):
678 ctx = webutil.changectx(web.repo, req) 704 ctx = webutil.changectx(web.repo, req)
679 if 'file' not in req.form: 705 if 'file' not in req.form:
680 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given') 706 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
681 path = webutil.cleanpath(web.repo, req.form['file'][0]) 707 path = webutil.cleanpath(web.repo, req.form['file'][0])
730 leftnode=hex(leftnode), 756 leftnode=hex(leftnode),
731 rightrev=rightrev, 757 rightrev=rightrev,
732 rightnode=hex(rightnode), 758 rightnode=hex(rightnode),
733 comparison=comparison) 759 comparison=comparison)
734 760
761 @webcommand('annotate')
735 def annotate(web, req, tmpl): 762 def annotate(web, req, tmpl):
736 fctx = webutil.filectx(web.repo, req) 763 fctx = webutil.filectx(web.repo, req)
737 f = fctx.path() 764 f = fctx.path()
738 parity = paritygen(web.stripecount) 765 parity = paritygen(web.stripecount)
739 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True, 766 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True,
782 branch=webutil.nodebranchnodefault(fctx), 809 branch=webutil.nodebranchnodefault(fctx),
783 parent=webutil.parents(fctx), 810 parent=webutil.parents(fctx),
784 child=webutil.children(fctx), 811 child=webutil.children(fctx),
785 permissions=fctx.manifest().flags(f)) 812 permissions=fctx.manifest().flags(f))
786 813
814 @webcommand('filelog')
787 def filelog(web, req, tmpl): 815 def filelog(web, req, tmpl):
788 816
789 try: 817 try:
790 fctx = webutil.filectx(web.repo, req) 818 fctx = webutil.filectx(web.repo, req)
791 f = fctx.path() 819 f = fctx.path()
860 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav, 888 return tmpl("filelog", file=f, node=fctx.hex(), nav=nav,
861 entries=entries, 889 entries=entries,
862 latestentry=latestentry, 890 latestentry=latestentry,
863 revcount=revcount, morevars=morevars, lessvars=lessvars) 891 revcount=revcount, morevars=morevars, lessvars=lessvars)
864 892
893 @webcommand('archive')
865 def archive(web, req, tmpl): 894 def archive(web, req, tmpl):
866 type_ = req.form.get('type', [None])[0] 895 type_ = req.form.get('type', [None])[0]
867 allowed = web.configlist("web", "allow_archive") 896 allowed = web.configlist("web", "allow_archive")
868 key = req.form['node'][0] 897 key = req.form['node'][0]
869 898
909 matchfn=matchfn, 938 matchfn=matchfn,
910 subrepos=web.configbool("web", "archivesubrepos")) 939 subrepos=web.configbool("web", "archivesubrepos"))
911 return [] 940 return []
912 941
913 942
943 @webcommand('static')
914 def static(web, req, tmpl): 944 def static(web, req, tmpl):
915 fname = req.form['file'][0] 945 fname = req.form['file'][0]
916 # a repo owner may set web.static in .hg/hgrc to get any file 946 # a repo owner may set web.static in .hg/hgrc to get any file
917 # readable by the user running the CGI script 947 # readable by the user running the CGI script
918 static = web.config("web", "static", None, untrusted=False) 948 static = web.config("web", "static", None, untrusted=False)
922 tp = [tp] 952 tp = [tp]
923 static = [os.path.join(p, 'static') for p in tp] 953 static = [os.path.join(p, 'static') for p in tp]
924 staticfile(static, fname, req) 954 staticfile(static, fname, req)
925 return [] 955 return []
926 956
957 @webcommand('graph')
927 def graph(web, req, tmpl): 958 def graph(web, req, tmpl):
928 959
929 ctx = webutil.changectx(web.repo, req) 960 ctx = webutil.changectx(web.repo, req)
930 rev = ctx.rev() 961 rev = ctx.rev()
931 962
1045 doc = _(doc).split('\n')[0] 1076 doc = _(doc).split('\n')[0]
1046 else: 1077 else:
1047 doc = _('(no help text available)') 1078 doc = _('(no help text available)')
1048 return doc 1079 return doc
1049 1080
1081 @webcommand('help')
1050 def help(web, req, tmpl): 1082 def help(web, req, tmpl):
1051 from mercurial import commands # avoid cycle 1083 from mercurial import commands # avoid cycle
1052 1084
1053 topicname = req.form.get('node', [None])[0] 1085 topicname = req.form.get('node', [None])[0]
1054 if not topicname: 1086 if not topicname: