|
1 | 1 | (ns basilisp.stacktrace
|
2 |
| - "Prints stacktraces." |
| 2 | + "Utility functions for printing stack traces." |
3 | 3 | (:require [basilisp.string :as str])
|
4 | 4 | (:import [traceback :as tb]))
|
5 | 5 |
|
| 6 | +(defn root-cause |
| 7 | + "Return the root cause exception of the possible chain of exceptions ``exc``." |
| 8 | + [^python/BaseException exc] |
| 9 | + (loop [e exc] |
| 10 | + (if-let [cause (.-__cause__ e)] |
| 11 | + (recur cause) |
| 12 | + e))) |
| 13 | + |
| 14 | +(defn context |
| 15 | + "Return any context exception to the exception ``exc``. |
| 16 | + |
| 17 | + Context exceptions may be the same as cause exceptions. Typically, when throwing an |
| 18 | + exception with an explicit cause the context exception is suppressed (via |
| 19 | + ``BaseException.__suppress_context__``). If called with one argument, this function |
| 20 | + will use the value of ``__suppress_context__`` for ``suppress-context?``. If called |
| 21 | + with two arguments, the caller can specify if context should be returned or suppressed." |
| 22 | + ([^python/BaseException exc] |
| 23 | + (context exc (.-__suppress_context__ exc))) |
| 24 | + ([^python/BaseException exc suppress-context?] |
| 25 | + (when-not suppress-context? |
| 26 | + (.-__context__ exc)))) |
| 27 | + |
| 28 | +(defn print-stack-trace |
| 29 | + "Prints up to ``n`` stack frames from the traceback of the exception ``exc``, not |
| 30 | + including chained exceptions (causes and context exceptions). |
| 31 | + |
| 32 | + To print exception tracebacks including causes, use :lpy:fn:`print-cause-trace`. |
| 33 | + |
| 34 | + If ``n`` is not given, return all frames." |
| 35 | + ([exc] |
| 36 | + (print-stack-trace exc nil)) |
| 37 | + ([exc n] |
| 38 | + (->> (tb/format_exception (python/type exc) |
| 39 | + exc |
| 40 | + (.-__traceback__ exc) |
| 41 | + ** |
| 42 | + :limit n |
| 43 | + :chain false) |
| 44 | + (str/join " ") |
| 45 | + print))) |
| 46 | + |
6 | 47 | (defn print-cause-trace
|
7 |
| - "Prints the stacktrace of chained ``exc`` (cause), using ``n`` stack |
8 |
| - frames (defaults to all)." |
| 48 | + "Prints up to ``n`` stack frames from the traceback of the exception ``exc``, |
| 49 | + including chained exceptions (causes and context exceptions). |
| 50 | + |
| 51 | + To print only the trace for the given exception, use :lpy:fn:`print-stack-trace`. |
| 52 | + |
| 53 | + If ``n`` is not given, return all frames." |
9 | 54 | ([exc]
|
10 | 55 | (print-cause-trace exc nil))
|
11 | 56 | ([exc n]
|
12 |
| - (print (str/join " " (tb/format_exception (python/type exc) exc (.-__traceback__ exc) |
13 |
| - ** :limit n :chain true))))) |
| 57 | + (->> (tb/format_exception (python/type exc) |
| 58 | + exc |
| 59 | + (.-__traceback__ exc) |
| 60 | + ** |
| 61 | + :limit n |
| 62 | + :chain true) |
| 63 | + (str/join " ") |
| 64 | + print))) |
| 65 | + |
| 66 | +(defn print-throwable |
| 67 | + "Print the type and message of exception ``exc``. |
| 68 | + |
| 69 | + Prints the :lpy:fn:`ex-data` map if present." |
| 70 | + [exc] |
| 71 | + (let [exc-type (type exc) |
| 72 | + data-str (if-let [d (ex-data exc)] |
| 73 | + (str " " d) |
| 74 | + "")] |
| 75 | + (println |
| 76 | + (str (.-__module__ exc-type) "." (.-__qualname__ exc-type) ": " (ex-message exc) data-str)))) |
| 77 | + |
| 78 | +(defn e |
| 79 | + "REPL utility for printing the root cause (via :lpy:fn:`root-cause`) of :lpy:var:`*e` |
| 80 | + if an exception is bound." |
| 81 | + [] |
| 82 | + (when *e |
| 83 | + (print-stack-trace (root-cause *e)))) |
0 commit comments