# Shorter and reversed format for traceback

**URL:** <https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139>\
**Category:** Ideas\
**Created:** [April 16, 2024, 2:46pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139 "2024-04-16T14:46:58Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [April 16, 2024, 2:46pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/1 "2024-04-16T14:46:58Z")

</div>

Current traceback seems optimized for terminal. See this example.

[Source code](https://github.com/methane/notes/blob/master/2024/mini_traceback/sample.py)

Output from `traceback.print_exception(e)`:

```python
Traceback (most recent call last):
  File "/Users/inada-n/notes/2024/mini_traceback/sample.py", line 8, in main
    conn = engine.connect()
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 3280, in connect
    return self._connection_cls(self)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 148, in __init__
    Connection._handle_dbapi_exception_noconnection(
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 2444, in _handle_dbapi_exception_noconnection
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 146, in __init__
    self._dbapi_connection = engine.raw_connection()
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 3304, in raw_connection
    return self.pool.connect()
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 449, in connect
    return _ConnectionFairy._checkout(self)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 1263, in _checkout
    fairy = _ConnectionRecord.checkout(pool)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 712, in checkout
    rec = pool._do_get()
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/impl.py", line 179, in _do_get
    with util.safe_reraise():
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/impl.py", line 177, in _do_get
    return self._create_connection()
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 390, in _create_connection
    return _ConnectionRecord(self)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 674, in __init__
    self.__connect()
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 900, in __connect
    with util.safe_reraise():
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 896, in __connect
    self.dbapi_connection = connection = pool._invoke_creator(self)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/create.py", line 643, in connect
    return dialect.connect(*cargs, **cparams)
  File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/default.py", line 617, in connect
    return self.loaded_dbapi.connect(*cargs, **cparams)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unable to open database file
(Background on this error at: https://sqlalche.me/e/20/e3q8)

```

Note that:

- If I don’t set `chain=False`, this traceback becomes two times longer.
- I used `PYTHONNODEBUGRANGES=1` to stop showing range markers.

Although this traceback is good for terminal, this is too large for structured logging + web based log viewer because:

- On web-based log viewer, users see the top first and scroll down. So the exception and the most recent call should be first.
- On textural log, one line in traceback is one line in the log. But structured logging with JSON lines can creates more than 8000bytes when traceback in the log record.

So I tried to create toy project named [minitraceback](https://pypi.org/project/minitraceback/). It creates shorter traceback with reversed order:

```python
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unable to open database file
(Background on this error at: https://sqlalche.me/e/20/e3q8)
Traceback (most recent call first):
  sqlalchemy/engine/default.py:617 connect
  sqlalchemy/engine/create.py:643 connect
  sqlalchemy/pool/base.py:896 __connect
  sqlalchemy/util/langhelpers.py:146 __exit__
  sqlalchemy/pool/base.py:900 __connect
  sqlalchemy/pool/base.py:674 __init__
  sqlalchemy/pool/base.py:390 _create_connection
  sqlalchemy/pool/impl.py:177 _do_get
  sqlalchemy/util/langhelpers.py:146 __exit__
  sqlalchemy/pool/impl.py:179 _do_get
  sqlalchemy/pool/base.py:712 checkout
  sqlalchemy/pool/base.py:1263 _checkout
  sqlalchemy/pool/base.py:449 connect
  sqlalchemy/engine/base.py:3304 raw_connection
  sqlalchemy/engine/base.py:146 __init__
  sqlalchemy/engine/base.py:2444 _handle_dbapi_exception_noconnection
  sqlalchemy/engine/base.py:148 __init__
  sqlalchemy/engine/base.py:3280 connect
  sample_mini.py:8 main

```

This looks good for my personal use. But I think shorter&reversed traceback in stdlib traceback would be better because some PaaS/Observability platforms may parse the traceback.

How do you think adding these options to print/format traceback functions in the traceback module?

- Add `reverse=False` option.
  - If it is true, traceback is shown in “most recent call first” order.

- Add `showsource=True` option.
  - If it is false, source code is not shown.

- Add `remove_path_prefix=False` option.
  - If it is true, remove the longest `sys.path` prefix from the file path.

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [April 16, 2024, 4:37pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/2 "2024-04-16T16:37:29Z")

</div>

> [@methane](#):
>
> On web-based log viewer, users see the top first and scroll down. So the exception and the most recent call should be first.

While I don’t see any reason not to allow presenting tracebacks in the opposite order, I’m not sure the quote above is necessarily good motivation. Instead, those tools should probably scroll to the end first and allow scrolling back so in my mind that’s a design error of those tools.

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [April 16, 2024, 4:54pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/3 "2024-04-16T16:54:24Z")

</div>

Hm this is somewhat a design choice, I think. Most-recent-first is a pretty common experience on the web (blogs, social media, news, forums, etc).

For a very long-running tool you don’t want to load all the logs, and you probably want to show the most recent thing. Putting the newest logs on top and scrolling down for history is a familiar experience from other contexts.

But given all that, this seems like a good thing to include in a structured logging package? As part of setting up such logging, you’d add this tool to reformat exceptions. Is there an additional benefit to incorporating it into the stdlib?

---

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [April 17, 2024, 2:58am UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/4 "2024-04-17T02:58:16Z")

</div>

> [@jamestwebber](#):
>
> But given all that, this seems like a good thing to include in a structured logging package? As part of setting up such logging, you’d add this tool to reformat exceptions. Is there an additional benefit to incorporating it into the stdlib?

For example, OpenTelemetry provides stdlib logging handler that uses stdlib traceback module.

Additionally, OpenTelemetry " Semantic Conventions for Exceptions" says:

> A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG.  
> [semantic-conventions/docs/exceptions/exceptions-spans.md at main · open-telemetry/semantic-conventions · GitHub](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-spans.md#stacktrace-representation)

That is reason for having a standard traceback format for log collection tools, instead of each log library have own traceback format.

---

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [April 17, 2024, 2:59am UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/5 "2024-04-17T02:59:15Z")

</div>

> [@ajoino](#):
>
> Instead, those tools should probably scroll to the end first and allow scrolling back so in my mind that’s a design error of those tools.

Java, C#, Go, and many other languages uses “most recent call first” traceback format.  
And observability tools are not optimized only for Python.

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [April 17, 2024, 10:57am UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/6 "2024-04-17T10:57:45Z")

</div>

Indeed. Maybe I don’t work with enough systems that mixes the order enough to appreciate it. My experience with reading logs comes mostly from the command line and reading jenkins logs, and in the latter case I wish it auto-scrolled to the bottom because that’s 99% where the error is and what I want to read.

---

<div class="post-metadata">

**Author:** ![AlexWaygood](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/alexwaygood/32/4722_2.png) [@AlexWaygood](https://discuss.python.org/u/AlexWaygood)\
**Post date:** [April 17, 2024, 12:59pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/7 "2024-04-17T12:59:05Z")

</div>

> [@methane](#):
>
> Add `remove_path_prefix=False` option.
> 
> - If it is true, remove the longest `sys.path` prefix from the file path.

I would prefer `show_path_prefix=True` or `include_path_prefix=True`. `remove_path_prefix=False` feels like a double negative to me (the default is to “not remove” the path prefix – aka “keep/show/include” the path prefix 🙂 ).

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [April 17, 2024, 1:12pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/8 "2024-04-17T13:12:47Z")

</div>

> [@AlexWaygood](#):
>
> (the default is to “not remove” the path prefix – aka “keep/show/include” the path prefix 🙂 ).

This is maybe a bit too much bike-shedding, but I disagree: The default is to show the paths as they are present in the traceback objects, i.e. as absolute paths. Removing the prefixes is an extra operation, so it should be enabled. With `show_path_prefix=False` we are disabling (i.e. `False`) not removing (i.e. `showing`) the prefixes, which is way more of a double negative.

Unless I am misunderstanding what is being stored and showing the prefixes is extra work that needs to be performed.

---

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [April 18, 2024, 2:27am UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/9 "2024-04-18T02:27:58Z")

</div>

No opinion about `reverse=False` and `showsource=True`?

There is no time until 3.13 beta. I want to implement one option in 3.13 if possible. My gut feeling, importance is:

1. showsource (reduce traceback size \<1/2)
2. reverse (show error without scroll)
3. remove\_path\_prefix (reduce more traceback size).

If there are no objections, I will start implementing the `showsource` option.  
What is your preferred name for it?

_Poll ([view on site](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/9))_

---

<div class="post-metadata">

**Author:** ![hugovk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/hugovk/32/14505_2.png) [@hugovk](https://discuss.python.org/u/hugovk)\
**Post date:** [April 18, 2024, 6:35am UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/10 "2024-04-18T06:35:28Z")

</div>

Whichever you choose, I recommend using underscores (for example, `show_source` or `show_lines`) for readability and accessibility. Compare this [screen reader video](https://discuss.python.org/t/change-environment-variable-style/35180/2). It’s also consistent with the other options in `traceback`.

---

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [April 19, 2024, 7:44am UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/11 "2024-04-19T07:44:56Z")

</div>

OK. For consisntency with `.line`, `lookup_lines=`, and `show_group=`, I decide to use `show_lines`.

---

<div class="post-metadata">

**Author:** ![parmentelat](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/parmentelat/32/19381_2.png) [@parmentelat](https://discuss.python.org/u/parmentelat)\
**Post date:** [April 21, 2024, 5:27pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/12 "2024-04-21T17:27:41Z")

</div>

with respect to ordering, I’d like to suggest using a flag named `latest_first=True` or a similar variant, rather than `reverse`, as it would be more immediately clear what we’ll get

I take this chance to support this idea, and to mention that in the context of the Jupyter notebooks as well, using latest\_first order would result in a more effective feedback

---

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [June 20, 2025, 12:37pm UTC](https://discuss.python.org/t/shorter-and-reversed-format-for-traceback/51139/13 "2025-06-20T12:37:43Z")

</div>

I created the PR.

> <https://github.com/python/cpython/pull/135752>
>
> \* Issue: gh-135751
> 
> 
> 
> 
> \----
> 📚 Documentation preview 📚: https://cpython-previe…ws--135752.org.readthedocs.build/
