# HG changeset patch # User Yuya Nishihara # Date 1494657710 -32400 # Node ID e5fbf968760088949a4585803aada0626164d826 # Parent b88d879e468a55854ded7739f501dde1509f78c7 extensions: prohibit registration of command without using @command (API) Detect the problem earlier for better error indication. I'm tired of teaching users that the mq extension is not guilty but the third-party extension is. https://bitbucket.org/tortoisehg/thg/issues?q=%27norepo%27 diff -r b88d879e468a -r e5fbf9687600 mercurial/extensions.py --- a/mercurial/extensions.py Sun May 14 15:46:45 2017 +0900 +++ b/mercurial/extensions.py Sat May 13 15:41:50 2017 +0900 @@ -118,6 +118,20 @@ if ui.debugflag: ui.traceback() +# attributes set by registrar.command +_cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo') + +def _validatecmdtable(cmdtable): + """Check if extension commands have required attributes""" + for c, e in cmdtable.iteritems(): + f = e[0] + missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)] + if not missing: + continue + raise error.ProgrammingError( + 'missing attributes: %s' % ', '.join(missing), + hint="use @command decorator to register '%s'" % c) + def load(ui, name, path): if name.startswith('hgext.') or name.startswith('hgext/'): shortname = name[6:] @@ -139,6 +153,7 @@ ui.warn(_('(third party extension %s requires version %s or newer ' 'of Mercurial; disabling)\n') % (shortname, minver)) return + _validatecmdtable(getattr(mod, 'cmdtable', {})) _extensions[shortname] = mod _order.append(shortname) diff -r b88d879e468a -r e5fbf9687600 tests/test-extension.t --- a/tests/test-extension.t Sun May 14 15:46:45 2017 +0900 +++ b/tests/test-extension.t Sat May 13 15:41:50 2017 +0900 @@ -1534,6 +1534,40 @@ $ cd .. +Prohibit registration of commands that don't use @command (issue5137) + + $ hg init deprecated + $ cd deprecated + + $ cat < deprecatedcmd.py + > def deprecatedcmd(repo, ui): + > pass + > cmdtable = { + > 'deprecatedcmd': (deprecatedcmd, [], ''), + > } + > EOF + $ cat < .hg/hgrc + > [extensions] + > deprecatedcmd = `pwd`/deprecatedcmd.py + > mq = ! + > hgext.mq = ! + > hgext/mq = ! + > EOF + + $ hg deprecatedcmd > /dev/null + *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo + *** (use @command decorator to register 'deprecatedcmd') + hg: unknown command 'deprecatedcmd' + [255] + + the extension shouldn't be loaded at all so the mq works: + + $ hg qseries --config extensions.mq= > /dev/null + *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo + *** (use @command decorator to register 'deprecatedcmd') + + $ cd .. + Test synopsis and docstring extending $ hg init exthelp