Mercurial > hg
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: |