Patchwork [2,of,3,STABLE,V2] ui: add support for fully printing chained exception tracebacks

login
register
mail settings
Submitter Matt Harbison
Date Feb. 9, 2013, 6:06 a.m.
Message ID <1744b3545df41d6202a0.1360390004@Envy>
Download mbox | patch
Permalink /patch/853/
State Superseded, archived
Headers show

Comments

Matt Harbison - Feb. 9, 2013, 6:06 a.m.
# HG changeset patch
# User Matt Harbison <matt_harbison@yahoo.com>
# Date 1360388586 18000
# Branch stable
# Node ID 1744b3545df41d6202a0f0857a17d1053ad44b71
# Parent  8b82d3b7f96e76877e0f3babf7a27b42791ab949
ui: add support for fully printing chained exception tracebacks

Currently, only SubrepoAbort has a cause chained to it.
Mads Kiilerich - Feb. 9, 2013, 9:52 a.m.
On 02/09/2013 07:06 AM, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison@yahoo.com>
> # Date 1360388586 18000
> # Branch stable
> # Node ID 1744b3545df41d6202a0f0857a17d1053ad44b71
> # Parent  8b82d3b7f96e76877e0f3babf7a27b42791ab949
> ui: add support for fully printing chained exception tracebacks

I think it is worth mentioning that it is a change to ui.traceback().

> Currently, only SubrepoAbort has a cause chained to it.
>
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -686,11 +686,23 @@
>           only to call in exception handler. returns true if traceback
>           printed.'''
>           if self.tracebackflag:
> -            if exc:
> +            if exc is None:
> +                exc = sys.exc_info()
> +            cause = getattr(exc[1], 'cause', None)
> +
> +            if cause is not None:
> +                causetb = traceback.format_tb(cause[2])
> +                exctb = traceback.format_tb(exc[2])
> +                exconly = traceback.format_exception_only(cause[0], cause[1])
> +
> +                # exclude frame where 'exc' was chained and rethrown from exctb
> +                self.write_err('Traceback (most recent call last):\n',
> +                               ''.join(exctb[:-1]),
> +                               ''.join(causetb),
> +                               ''.join(exconly))
> +            else:
>                   traceback.print_exception(exc[0], exc[1], exc[2],
>                                             file=self.ferr)
> -            else:
> -                traceback.print_exc(file=self.ferr)
>           return self.tracebackflag
>   
>       def geteditor(self):
> diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t
> --- a/tests/test-subrepo.t
> +++ b/tests/test-subrepo.t
> @@ -644,7 +644,43 @@
>     adding file changes
>     added 2 changesets with 3 changes to 2 files
>     (run 'hg update' to get a working copy)
> -  $ hg -R issue1852b update
> +  $ hg -R issue1852b update --traceback
> +  Traceback (most recent call last):
> +    File "*/mercurial/dispatch.py", line *, in _runcatch (glob)
> +      return _dispatch(req)
> +    File "*/mercurial/dispatch.py", line *, in _dispatch (glob)
> +      cmdpats, cmdoptions)
> +    File "*/mercurial/dispatch.py", line *, in runcommand (glob)
> +      ret = _runcommand(ui, options, cmd, d)
> +    File "*/mercurial/dispatch.py", line *, in _runcommand (glob)
> +      return checkargs()
> +    File "*/mercurial/dispatch.py", line *, in checkargs (glob)
> +      return cmdfunc()
> +    File "*/mercurial/dispatch.py", line *, in <lambda> (glob)
> +      d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
> +    File "*/mercurial/util.py", line *, in check (glob)
> +      return func(*args, **kwargs)
> +    File "*/mercurial/commands.py", line *, in update (glob)
> +      ret = hg.update(repo, rev)
> +    File "*/mercurial/hg.py", line *, in update (glob)
> +      stats = updaterepo(repo, node, False)
> +    File "*/mercurial/hg.py", line *, in updaterepo (glob)
> +      return mergemod.update(repo, node, False, overwrite, None)
> +    File "*/mercurial/merge.py", line *, in update (glob)
> +      stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
> +    File "*/mercurial/merge.py", line *, in applyupdates (glob)
> +      subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
> +    File "*/mercurial/subrepo.py", line *, in submerge (glob)
> +      mctx.sub(s).get(r)
> +    File "*/mercurial/subrepo.py", line *, in decoratedmethod (glob)
> +      res = func(self, *args, **kargs)
> +    File "*/mercurial/subrepo.py", line *, in get (glob)
> +      self._get(state)
> +    File "*/mercurial/subrepo.py", line *, in _get (glob)
> +      srcurl = _abssource(self._repo)
> +    File "*/mercurial/subrepo.py", line *, in _abssource (glob)
> +      raise util.Abort(_("default path for subrepository not found"))
> +  Abort: default path for subrepository not found
>     abort: default path for subrepository not found (in subrepo sub/repo) (glob)
>     [255]

I don't know how stable the backtrace format is over Python versions - I 
wouldn't rely on it being stable. This test will also fail whenever the 
code path changes slightly. Instead I suggest grepping for an essential 
part of a single line.

/Mads

Patch

diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -686,11 +686,23 @@ 
         only to call in exception handler. returns true if traceback
         printed.'''
         if self.tracebackflag:
-            if exc:
+            if exc is None:
+                exc = sys.exc_info()
+            cause = getattr(exc[1], 'cause', None)
+
+            if cause is not None:
+                causetb = traceback.format_tb(cause[2])
+                exctb = traceback.format_tb(exc[2])
+                exconly = traceback.format_exception_only(cause[0], cause[1])
+
+                # exclude frame where 'exc' was chained and rethrown from exctb
+                self.write_err('Traceback (most recent call last):\n',
+                               ''.join(exctb[:-1]),
+                               ''.join(causetb),
+                               ''.join(exconly))
+            else:
                 traceback.print_exception(exc[0], exc[1], exc[2],
                                           file=self.ferr)
-            else:
-                traceback.print_exc(file=self.ferr)
         return self.tracebackflag
 
     def geteditor(self):
diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t
--- a/tests/test-subrepo.t
+++ b/tests/test-subrepo.t
@@ -644,7 +644,43 @@ 
   adding file changes
   added 2 changesets with 3 changes to 2 files
   (run 'hg update' to get a working copy)
-  $ hg -R issue1852b update
+  $ hg -R issue1852b update --traceback
+  Traceback (most recent call last):
+    File "*/mercurial/dispatch.py", line *, in _runcatch (glob)
+      return _dispatch(req)
+    File "*/mercurial/dispatch.py", line *, in _dispatch (glob)
+      cmdpats, cmdoptions)
+    File "*/mercurial/dispatch.py", line *, in runcommand (glob)
+      ret = _runcommand(ui, options, cmd, d)
+    File "*/mercurial/dispatch.py", line *, in _runcommand (glob)
+      return checkargs()
+    File "*/mercurial/dispatch.py", line *, in checkargs (glob)
+      return cmdfunc()
+    File "*/mercurial/dispatch.py", line *, in <lambda> (glob)
+      d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
+    File "*/mercurial/util.py", line *, in check (glob)
+      return func(*args, **kwargs)
+    File "*/mercurial/commands.py", line *, in update (glob)
+      ret = hg.update(repo, rev)
+    File "*/mercurial/hg.py", line *, in update (glob)
+      stats = updaterepo(repo, node, False)
+    File "*/mercurial/hg.py", line *, in updaterepo (glob)
+      return mergemod.update(repo, node, False, overwrite, None)
+    File "*/mercurial/merge.py", line *, in update (glob)
+      stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
+    File "*/mercurial/merge.py", line *, in applyupdates (glob)
+      subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
+    File "*/mercurial/subrepo.py", line *, in submerge (glob)
+      mctx.sub(s).get(r)
+    File "*/mercurial/subrepo.py", line *, in decoratedmethod (glob)
+      res = func(self, *args, **kargs)
+    File "*/mercurial/subrepo.py", line *, in get (glob)
+      self._get(state)
+    File "*/mercurial/subrepo.py", line *, in _get (glob)
+      srcurl = _abssource(self._repo)
+    File "*/mercurial/subrepo.py", line *, in _abssource (glob)
+      raise util.Abort(_("default path for subrepository not found"))
+  Abort: default path for subrepository not found
   abort: default path for subrepository not found (in subrepo sub/repo) (glob)
   [255]