Patchwork [01,of,22,hgweb-help] webcommands: define web commands using a decorator

login
register
mail settings
Submitter Gregory Szorc
Date Feb. 7, 2015, 7:15 a.m.
Message ID <a54bf6a609310887f4fa.1423293339@gps-mbp.local>
Download mbox | patch
Permalink /patch/7739/
State Accepted
Headers show

Comments

Gregory Szorc - Feb. 7, 2015, 7:15 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1423278377 28800
#      Fri Feb 06 19:06:17 2015 -0800
# Node ID a54bf6a609310887f4fa824579b9db3e29b3e550
# Parent  ff5caa8dfd993680d9602ca6ebb14da9de10d5f4
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.
Martin von Zweigbergk - Feb. 7, 2015, 3:54 p.m.
On Fri Feb 06 2015 at 11:16:48 PM Gregory Szorc <gregory.szorc@gmail.com>
wrote:

> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1423278377 28800
> #      Fri Feb 06 19:06:17 2015 -0800
> # Node ID a54bf6a609310887f4fa824579b9db3e29b3e550
> # Parent  ff5caa8dfd993680d9602ca6ebb14da9de10d5f4
> 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.
>
> diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.
> py
> --- a/mercurial/hgweb/webcommands.py
> +++ b/mercurial/hgweb/webcommands.py
> @@ -21,20 +21,38 @@ from mercurial import revset
>
>  # __all__ is populated with the allowed commands. Be sure to add to it if
>  # you're adding a new command, or the new command won't work.
>

Remove the second sentence?
Sean Farley - Feb. 7, 2015, 11 p.m.
Martin von Zweigbergk writes:

> On Fri Feb 06 2015 at 11:16:48 PM Gregory Szorc <gregory.szorc@gmail.com>
> wrote:
>
>> # HG changeset patch
>> # User Gregory Szorc <gregory.szorc@gmail.com>
>> # Date 1423278377 28800
>> #      Fri Feb 06 19:06:17 2015 -0800
>> # Node ID a54bf6a609310887f4fa824579b9db3e29b3e550
>> # Parent  ff5caa8dfd993680d9602ca6ebb14da9de10d5f4
>> 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.
>>
>> diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.
>> py
>> --- a/mercurial/hgweb/webcommands.py
>> +++ b/mercurial/hgweb/webcommands.py
>> @@ -21,20 +21,38 @@ from mercurial import revset
>>
>>  # __all__ is populated with the allowed commands. Be sure to add to it if
>>  # you're adding a new command, or the new command won't work.
>>
>
> Remove the second sentence?

I think the second sentence is ok? The first six patches of this series
look good to me also.
Gregory Szorc - Feb. 8, 2015, 1:05 a.m.
On Sat, Feb 7, 2015 at 3:00 PM, Sean Farley <sean.michael.farley@gmail.com>
wrote:

>
> Martin von Zweigbergk writes:
>
> > On Fri Feb 06 2015 at 11:16:48 PM Gregory Szorc <gregory.szorc@gmail.com
> >
> > wrote:
> >
> >> # HG changeset patch
> >> # User Gregory Szorc <gregory.szorc@gmail.com>
> >> # Date 1423278377 28800
> >> #      Fri Feb 06 19:06:17 2015 -0800
> >> # Node ID a54bf6a609310887f4fa824579b9db3e29b3e550
> >> # Parent  ff5caa8dfd993680d9602ca6ebb14da9de10d5f4
> >> 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.
> >>
> >> diff --git a/mercurial/hgweb/webcommands.py
> b/mercurial/hgweb/webcommands.
> >> py
> >> --- a/mercurial/hgweb/webcommands.py
> >> +++ b/mercurial/hgweb/webcommands.py
> >> @@ -21,20 +21,38 @@ from mercurial import revset
> >>
> >>  # __all__ is populated with the allowed commands. Be sure to add to it
> if
> >>  # you're adding a new command, or the new command won't work.
> >>
> >
> > Remove the second sentence?
>
> I think the second sentence is ok? The first six patches of this series
> look good to me also.
>

Well, the comment isn't totally accurate post decorator. This reminds me, I
need to follow this up with a patch to change dispatch from getattr(mod,
command) to looking in webcommands.commands instead. __all__ is a bit hacky
and now redundant.

Patch

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -21,20 +21,38 @@  from mercurial import revset
 
 # __all__ is populated with the allowed commands. Be sure to add to it if
 # you're adding a new command, or the new command won't work.
 
-__all__ = [
-   'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
-   'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
-   'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
-]
+__all__ = []
 
+class webcommand(object):
+    """Decorator used to register a web command handler.
+
+    The decorator takes as its positional arguments the name/path the
+    command should be accessible under.
+
+    Usage:
+
+    @webcommand('mycommand')
+    def mycommand(web, req, tmpl):
+        pass
+    """
+
+    def __init__(self, name):
+        self.name = name
+
+    def __call__(self, func):
+        __all__.append(self.name)
+        return func
+
+@webcommand('log')
 def log(web, req, tmpl):
     if 'file' in req.form and req.form['file'][0]:
         return filelog(web, req, tmpl)
     else:
         return changelog(web, req, tmpl)
 
+@webcommand('rawfile')
 def rawfile(web, req, tmpl):
     guessmime = web.configbool('web', 'guessmime', False)
 
     path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
@@ -97,8 +115,9 @@  def _filerevision(web, tmpl, fctx):
                 child=webutil.children(fctx),
                 rename=webutil.renamelink(fctx),
                 permissions=fctx.manifest().flags(f))
 
+@webcommand('file')
 def file(web, req, tmpl):
     path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
     if not path:
         return manifest(web, req, tmpl)
@@ -266,8 +285,9 @@  def _search(web, req, tmpl):
                 morevars=morevars, lessvars=lessvars,
                 modedesc=searchfunc[1],
                 showforcekw=showforcekw, showunforcekw=showunforcekw)
 
+@webcommand('changelog')
 def changelog(web, req, tmpl, shortlog=False):
 
     query = ''
     if 'node' in req.form:
@@ -325,11 +345,13 @@  def changelog(web, req, tmpl, shortlog=F
                 latestentry=latestentry, nextentry=nextentry,
                 archives=web.archivelist("tip"), revcount=revcount,
                 morevars=morevars, lessvars=lessvars, query=query)
 
+@webcommand('shortlog')
 def shortlog(web, req, tmpl):
     return changelog(web, req, tmpl, shortlog=True)
 
+@webcommand('changeset')
 def changeset(web, req, tmpl):
     ctx = webutil.changectx(web.repo, req)
     basectx = webutil.basechangectx(web.repo, req)
     if basectx is None:
@@ -381,9 +403,9 @@  def changeset(web, req, tmpl):
                 branch=webutil.nodebranchnodefault(ctx),
                 inbranch=webutil.nodeinbranch(web.repo, ctx),
                 branches=webutil.nodebranchdict(web.repo, ctx))
 
-rev = changeset
+rev = webcommand('rev')(changeset)
 
 def decodepath(path):
     """Hook for mapping a path in the repository to a path in the
     working copy.
@@ -391,8 +413,9 @@  def decodepath(path):
     Extensions (e.g., largefiles) can override this to remap files in
     the virtual file system presented by the manifest command below."""
     return path
 
+@webcommand('manifest')
 def manifest(web, req, tmpl):
     ctx = webutil.changectx(web.repo, req)
     path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
     mf = ctx.manifest()
@@ -473,8 +496,9 @@  def manifest(web, req, tmpl):
                 bookmarks=webutil.nodebookmarksdict(web.repo, node),
                 inbranch=webutil.nodeinbranch(web.repo, ctx),
                 branches=webutil.nodebranchdict(web.repo, ctx))
 
+@webcommand('tags')
 def tags(web, req, tmpl):
     i = list(reversed(web.repo.tagslist()))
     parity = paritygen(web.stripecount)
 
@@ -495,8 +519,9 @@  def tags(web, req, tmpl):
                 entries=lambda **x: entries(False, False, **x),
                 entriesnotip=lambda **x: entries(True, False, **x),
                 latestentry=lambda **x: entries(True, True, **x))
 
+@webcommand('bookmarks')
 def bookmarks(web, req, tmpl):
     i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
     parity = paritygen(web.stripecount)
 
@@ -515,8 +540,9 @@  def bookmarks(web, req, tmpl):
                 node=hex(web.repo.changelog.tip()),
                 entries=lambda **x: entries(latestonly=False, **x),
                 latestentry=lambda **x: entries(latestonly=True, **x))
 
+@webcommand('branches')
 def branches(web, req, tmpl):
     tips = []
     heads = web.repo.heads()
     parity = paritygen(web.stripecount)
@@ -546,8 +572,9 @@  def branches(web, req, tmpl):
     return tmpl('branches', node=hex(web.repo.changelog.tip()),
                 entries=lambda **x: entries(0, **x),
                 latestentry=lambda **x: entries(1, **x))
 
+@webcommand('summary')
 def summary(web, req, tmpl):
     i = reversed(web.repo.tagslist())
 
     def tagentries(**map):
@@ -631,8 +658,9 @@  def summary(web, req, tmpl):
                 shortlog=changelist,
                 node=tip.hex(),
                 archives=web.archivelist("tip"))
 
+@webcommand('filediff')
 def filediff(web, req, tmpl):
     fctx, ctx = None, None
     try:
         fctx = webutil.filectx(web.repo, req)
@@ -671,10 +699,11 @@  def filediff(web, req, tmpl):
                 parent=webutil.parents(ctx),
                 child=webutil.children(ctx),
                 diff=diffs)
 
-diff = filediff
+diff = webcommand('diff')(filediff)
 
+@webcommand('comparison')
 def comparison(web, req, tmpl):
     ctx = webutil.changectx(web.repo, req)
     if 'file' not in req.form:
         raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
@@ -731,8 +760,9 @@  def comparison(web, req, tmpl):
                 rightrev=rightrev,
                 rightnode=hex(rightnode),
                 comparison=comparison)
 
+@webcommand('annotate')
 def annotate(web, req, tmpl):
     fctx = webutil.filectx(web.repo, req)
     f = fctx.path()
     parity = paritygen(web.stripecount)
@@ -783,8 +813,9 @@  def annotate(web, req, tmpl):
                 parent=webutil.parents(fctx),
                 child=webutil.children(fctx),
                 permissions=fctx.manifest().flags(f))
 
+@webcommand('filelog')
 def filelog(web, req, tmpl):
 
     try:
         fctx = webutil.filectx(web.repo, req)
@@ -861,8 +892,9 @@  def filelog(web, req, tmpl):
                 entries=entries,
                 latestentry=latestentry,
                 revcount=revcount, morevars=morevars, lessvars=lessvars)
 
+@webcommand('archive')
 def archive(web, req, tmpl):
     type_ = req.form.get('type', [None])[0]
     allowed = web.configlist("web", "allow_archive")
     key = req.form['node'][0]
@@ -910,8 +942,9 @@  def archive(web, req, tmpl):
                      subrepos=web.configbool("web", "archivesubrepos"))
     return []
 
 
+@webcommand('static')
 def static(web, req, tmpl):
     fname = req.form['file'][0]
     # a repo owner may set web.static in .hg/hgrc to get any file
     # readable by the user running the CGI script
@@ -923,8 +956,9 @@  def static(web, req, tmpl):
         static = [os.path.join(p, 'static') for p in tp]
     staticfile(static, fname, req)
     return []
 
+@webcommand('graph')
 def graph(web, req, tmpl):
 
     ctx = webutil.changectx(web.repo, req)
     rev = ctx.rev()
@@ -1046,8 +1080,9 @@  def _getdoc(e):
     else:
         doc = _('(no help text available)')
     return doc
 
+@webcommand('help')
 def help(web, req, tmpl):
     from mercurial import commands # avoid cycle
 
     topicname = req.form.get('node', [None])[0]