Repository: idsia/sacred
Issue: print_filtered_stacktrace hides chained exceptions


```python
import sacred

try:
    raise Exception('e1') from Exception('e2')
except Exception as e:
    print('#### Message from print_filtered_stacktrace ####')
    sacred.utils.print_filtered_stacktrace()
    print('#### Original message ####')
    raise e
```
```
#### Message from print_filtered_stacktrace ####
Traceback (most recent calls WITHOUT Sacred internals):
  File "a.py", line 3, in <module>
    raise Exception('e1') from Exception('e2')
Exception: e1
#### Original message ####
Exception: e2

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "a.py", line 8, in <module>
    raise e
  File "a.py", line 3, in <module>
    raise Exception('e1') from Exception('e2')
Exception: e1
```
print_filtered_stacktrace is used at https://github.com/IDSIA/sacred/blob/master/sacred/experiment.py#L255, and it hides the traceback from chained exceptions.

print_filtered_stacktrace doesn't call  traceback.print_exception but only use traceback.format_exception_only in case that "WITHOUT Sacred internals", and it never show chained exceptions. 



Changed files:
sacred/utils.py

diff --git a/sacred/utils.py b/sacred/utils.py
index d2732e2..4c4e68b 100755
--- a/sacred/utils.py
+++ b/sacred/utils.py
@@ -259,20 +259,56 @@ def print_filtered_stacktrace():
     while current_tb.tb_next is not None:
         current_tb = current_tb.tb_next
     if '__sacred__' in current_tb.tb_frame.f_globals:
-        print("Exception originated from within Sacred.\\n"
-              "Traceback (most recent calls):", file=sys.stderr)
-        tb.print_tb(exc_traceback)
-        tb.print_exception(exc_type, exc_value, None)
+        header = ["Exception originated from within Sacred.\\n"
+                  "Traceback (most recent calls):\\n"]
+        texts = tb.format_exception(exc_type, exc_value, current_tb)
+        print(''.join(header + texts[1:]).strip(), file=sys.stderr)
     else:
-        print("Traceback (most recent calls WITHOUT Sacred internals):",
-              file=sys.stderr)
-        current_tb = exc_traceback
-        while current_tb is not None:
-            if '__sacred__' not in current_tb.tb_frame.f_globals:
-                tb.print_tb(current_tb, 1)
-            current_tb = current_tb.tb_next
-        print("\\n".join(tb.format_exception_only(exc_type, exc_value)).strip(),
-              file=sys.stderr)
+        if sys.version_info >= (3, 3):
+            tb_exception =\\
+                tb.TracebackException(exc_type, exc_value, exc_traceback,
+                                      limit=None)
+            for line in filtered_traceback_format(tb_exception):
+                print(line, file=sys.stderr, end="")
+        else:
+            print("Traceback (most recent calls WITHOUT Sacred internals):",
+                  file=sys.stderr)
+            current_tb = exc_traceback
+            while current_tb is not None:
+                if '__sacred__' not in current_tb.tb_frame.f_globals:
+                    tb.print_tb(current_tb, 1)
+                current_tb = current_tb.tb_next
+            print("\\n".join(tb.format_exception_only(exc_type,
+                                                     exc_value)).strip(),
+                  file=sys.stderr)
+
+
+def filtered_traceback_format(tb_exception, chain=True):
+    if chain:
+        if tb_exception.__cause__ is not None:
+            for line in filtered_traceback_format(tb_exception.__cause__,
+                                                  chain=chain):
+                yield line
+            yield tb._cause_message
+        elif (tb_exception.__context__ is not None and
+            not tb_exception.__suppress_context__):
+            for line in filtered_traceback_format(tb_exception.__context__,
+                    chain=chain):
+                yield line
+            yield tb._context_message
+    yield 'Traceback (most recent calls WITHOUT Sacred internals):\\n'
+    current_tb = tb_exception.exc_traceback
+    while current_tb is not None:
+        if '__sacred__' not in current_tb.tb_frame.f_globals:
+            stack = tb.StackSummary.extract(tb.walk_tb(current_tb),
+                                            limit=1,
+                                            lookup_lines=True,
+                                            capture_locals=False)
+            for line in stack.format():
+                yield line
+        current_tb = current_tb.tb_next
+    for line in tb_exception.format_exception_only():
+        yield line
 
 
 def is_subdir(path, directory):