diff --git a/hgext/highlight/__init__.py b/hgext/highlight/__init__.py --- a/hgext/highlight/__init__.py +++ b/hgext/highlight/__init__.py @@ -77,6 +77,7 @@ return orig(web) +@webcommands.webcommand('highlightcss') def generate_css(web): pg_style = web.config('web', 'pygments_style', 'colorful') fmter = highlight.HtmlFormatter(style=pg_style) @@ -91,6 +92,4 @@ # monkeypatch in the new version extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight) - extensions.wrapfunction(webcommands, 'annotate', annotate_highlight) - webcommands.highlightcss = generate_css - webcommands.__all__.append('highlightcss') + webcommands.wrapwebcommand('annotate', annotate_highlight) diff --git a/hgext/keyword.py b/hgext/keyword.py --- a/hgext/keyword.py +++ b/hgext/keyword.py @@ -740,7 +740,7 @@ extensions.wrapfunction(cmdutil, 'copy', kw_copy) extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord) for c in nokwwebcommands.split(): - extensions.wrapfunction(webcommands, c, kwweb_skip) + webcommands.wrapwebcommand(c, kwweb_skip) def reposetup(ui, repo): '''Sets up repo as kwrepo for keyword substitution.''' diff --git a/hgext/largefiles/uisetup.py b/hgext/largefiles/uisetup.py --- a/hgext/largefiles/uisetup.py +++ b/hgext/largefiles/uisetup.py @@ -151,7 +151,7 @@ extensions.wrapfunction(archival, 'archive', overrides.overridearchive) extensions.wrapfunction(subrepo.hgsubrepo, 'archive', overrides.hgsubrepoarchive) - extensions.wrapfunction(webcommands, 'archive', overrides.hgwebarchive) + webcommands.wrapwebcommand('archive', overrides.hgwebarchive) extensions.wrapfunction(cmdutil, 'bailifchanged', overrides.overridebailifchanged) diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -354,7 +354,7 @@ cmd = cmd[style + 1:] # avoid accepting e.g. style parameter as command - if util.safehasattr(webcommands, cmd): + if cmd in webcommands.commands: req.qsparams['cmd'] = cmd if cmd == 'static': @@ -416,7 +416,7 @@ res.headers['ETag'] = tag - if cmd not in webcommands.__all__: + if cmd not in webcommands.commands: msg = 'no such method: %s' % cmd raise ErrorResponse(HTTP_BAD_REQUEST, msg) else: @@ -424,7 +424,7 @@ # override easily enough. res.status = '200 Script output follows' res.headers['Content-Type'] = ctype - return getattr(webcommands, cmd)(rctx) + return webcommands.commands[cmd](rctx) except (error.LookupError, error.RepoLookupError) as err: msg = pycompat.bytestr(err) diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -8,6 +8,7 @@ from __future__ import absolute_import import copy +import functools import mimetypes import os import re @@ -47,7 +48,6 @@ webutil, ) -__all__ = [] commands = {} class webcommand(object): @@ -77,10 +77,34 @@ self.name = name def __call__(self, func): - __all__.append(self.name) commands[self.name] = func return func +def wrapwebcommand(command, wrapper): + """Utility to wrap functions defined as webcommands + + Wrap the webcommand 'command' using the specified wrapper. + The wrapper function should have the signature + + wrapper(orig, web) + + where 'orig' is the webcommand to be wrapped, and 'web' is the + requestcontext instance that will be passed to the webcommand + at runtime.""" + + if command not in commands: + raise error.ProgrammingError('no webcommand named %s' % command) + + if not callable(wrapper): + raise error.ProgrammingError('wrapper must be a callable') + + # Create the new webcommand + orig = commands[command] + newfunc = functools.partial(wrapper, orig) + + # Overwrite the old webcommand with the new one + commands[command] = newfunc + @webcommand('log') def log(web): """ diff --git a/tests/hgweberror.py b/tests/hgweberror.py --- a/tests/hgweberror.py +++ b/tests/hgweberror.py @@ -6,6 +6,7 @@ webcommands, ) +@webcommands.webcommand('raiseerror') def raiseerror(web): '''Dummy web command that raises an uncaught Exception.''' @@ -18,7 +19,3 @@ web.res.getbodyfile().write(b'partial content\n') raise AttributeError('I am an uncaught error!') - -def extsetup(ui): - setattr(webcommands, 'raiseerror', raiseerror) - webcommands.__all__.append('raiseerror')