sheehan created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers.
REVISION SUMMARY This commit fixes the `@webcommand` decorator used to define webcommands. Although webcommands are currently using this decorator, it does not behave as intended. Using the decorator should record the name of the decorator in a `commands` dict, and when a request arrives to hgweb the correct function to call should be resolved from that dict. In the current implementation the command is added to the commands dict, but when hgweb attempts to resolve the function while processing a request, it instead looks for an attribute with the same name attached to the `webcommands` module. To summarize, a command 'commandname' should be resolved from `mercurial.hgweb.webcommands.commands['commandname']` but is instead being resolved from `mercurial.hgweb.webcommands.commandname` The decorator appears to be working on the core webcommands (such as log, rev, file) however this is because those commands are defined with the same name in the underlying Python function. This commit changes hgweb_mod.py to resolve the function used from webcommands to come from the `commands` dict in the `webcommands` module. Due to the change in how webcommands are resolved, a `wrapwebcommand` function is also added, which wraps a webcommand by applying the wrapper as a partial function and overwrites the existing function in the `commands` dict. Wrapped webcommands in shipped extensions and hgweb tests are changed to use the fixed decorator and `wrapwebcommand` function. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D4283 AFFECTED FILES hgext/highlight/__init__.py hgext/keyword.py hgext/largefiles/uisetup.py mercurial/hgweb/hgweb_mod.py mercurial/hgweb/webcommands.py tests/hgweberror.py CHANGE DETAILS 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') 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/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,15 +416,15 @@ 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: # Set some globals appropriate for web handlers. Commands can # 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/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/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/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) To: sheehan, #hg-reviewers Cc: mercurial-devel _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel