Augie Fackler avatar Augie Fackler committed 1c9f58a

dispatch: try and identify third-party extensions as sources of tracebacks

Extension authors should explicitly declare their supported hg
versions and include a buglink attribute in their extension. In the
event that a traceback occurs, we'll identify the
least-recently-tested extensionas the most likely source of the defect
and suggest the user disable that extension.

Packagers should make every effort to ship hg versions from exact
tags, or with as few modifications as possible so that the versioning
can work appropriately.

Comments (0)

Files changed (2)

mercurial/dispatch.py

     except socket.error, inst:
         ui.warn(_("abort: %s\n") % inst.args[-1])
     except: # re-raises
-        ui.warn(_("** unknown exception encountered,"
-                  " please report by visiting\n"))
-        ui.warn(_("**  http://mercurial.selenic.com/wiki/BugTracker\n"))
-        ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
-        ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
-               % util.version())
-        ui.warn(_("** Extensions loaded: %s\n")
-               % ", ".join([x[0] for x in extensions.extensions()]))
+        myver = util.version()
+        # For compatibility checking, we discard the portion of the hg
+        # version after the + on the assumption that if a "normal
+        # user" is running a build with a + in it the packager
+        # probably built from fairly close to a tag and anyone with a
+        # 'make local' copy of hg (where the version number can be out
+        # of date) will be clueful enough to notice the implausible
+        # version number and try updating.
+        compare = myver.split('+')[0]
+        ct = tuplever(compare)
+        worst = None, ct, ''
+        for name, mod in extensions.extensions():
+            testedwith = getattr(mod, 'testedwith', 'unknown')
+            report = getattr(mod, 'buglink', _('the extension author.'))
+            if testedwith == 'unknown':
+                # We found an untested extension. It's likely the culprit.
+                worst = name, testedwith, report
+                break
+            if compare not in testedwith.split() and testedwith != 'internal':
+                tested = [tuplever(v) for v in testedwith.split()]
+                nearest = max([t for t in tested if t < ct])
+                if nearest < worst[1]:
+                    worst = name, nearest, report
+        if worst[0] is not None:
+            name, testedwith, report = worst
+            if not isinstance(testedwith, str):
+                testedwith = '.'.join([str(c) for c in testedwith])
+            warning = (_('** Unknown exception encountered with '
+                         'possibly-broken third-party extension %s\n'
+                         '** which supports versions %s of Mercurial.\n'
+                         '** Please disable %s and try your action again.\n'
+                         '** If that fixes the bug please report it to %s\n')
+                       % (name, testedwith, name, report))
+        else:
+            warning = (_("** unknown exception encountered, "
+                         "please report by visiting\n") +
+                       _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
+        warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
+                    (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
+                    (_("** Extensions loaded: %s\n") %
+                     ", ".join([x[0] for x in extensions.extensions()])))
+        ui.warn(warning)
         raise
 
     return -1
 
+def tuplever(v):
+    return tuple([int(i) for i in v.split('.')])
+
+
 def aliasargs(fn, givenargs):
     args = getattr(fn, 'args', [])
     if args:

tests/test-extension.t

   hg: unknown command 'foo'
   warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
   [255]
+
+  $ cat > throw.py <<EOF
+  > from mercurial import cmdutil, commands
+  > cmdtable = {}
+  > command = cmdutil.command(cmdtable)
+  > class Bogon(Exception): pass
+  > 
+  > @command('throw', [], 'hg throw')
+  > def throw(ui, **opts):
+  >     """throws an exception"""
+  >     raise Bogon()
+  > commands.norepo += " throw"
+  > EOF
+No declared supported version, extension complains:
+  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
+  ** Unknown exception encountered with possibly-broken third-party extension throw
+  ** which supports versions unknown of Mercurial.
+  ** Please disable throw and try your action again.
+  ** If that fixes the bug please report it to the extension author.
+  ** Python * (glob)
+  ** Mercurial Distributed SCM * (glob)
+  ** Extensions loaded: throw
+If the extension specifies a buglink, show that:
+  $ echo 'buglink = "http://example.com/bts"' >> throw.py
+  $ rm -f throw.pyc throw.pyo
+  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
+  ** Unknown exception encountered with possibly-broken third-party extension throw
+  ** which supports versions unknown of Mercurial.
+  ** Please disable throw and try your action again.
+  ** If that fixes the bug please report it to http://example.com/bts
+  ** Python * (glob)
+  ** Mercurial Distributed SCM (*) (glob)
+  ** Extensions loaded: throw
+If the extensions declare outdated versions, accuse the older extension first:
+  $ echo "testedwith = '1.9.3'" >> older.py
+  $ echo "testedwith = '2.1.1'" >> throw.py
+  $ rm -f throw.pyc throw.pyo
+  $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
+  >   throw 2>&1 | egrep '^\*\*'
+  ** Unknown exception encountered with possibly-broken third-party extension older
+  ** which supports versions 1.9.3 of Mercurial.
+  ** Please disable older and try your action again.
+  ** If that fixes the bug please report it to the extension author.
+  ** Python * (glob)
+  ** Mercurial Distributed SCM (*) (glob)
+  ** Extensions loaded: throw, older
+
+Declare the version as supporting this hg version, show regular bts link:
+  $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
+  $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
+  $ rm -f throw.pyc throw.pyo
+  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
+  ** unknown exception encountered, please report by visiting
+  ** http://mercurial.selenic.com/wiki/BugTracker
+  ** Python * (glob)
+  ** Mercurial Distributed SCM (*) (glob)
+  ** Extensions loaded: throw
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.