Skip to content

tyc trace

tyc trace [TRACEBACK_FILE] [--map-dir DIR]

Reads a Python traceback (from the given file, or from stdin when the argument is omitted), locates the matching .py.map v2 source maps, and rewrites File "build/main.py", line N frames to File "src/main.ty", line M.

Examples

Terminal window
python build/main.py 2>err.log
tyc trace err.log
# Or pipe directly (no argument → read stdin):
python build/main.py 2>&1 | tyc trace

What it does

  1. Parse the traceback text (standard Python traceback shape).
  2. For each frame referring to a build/*.py, find the matching .py.map v2.
  3. Look up the out_line → ty_line mapping for the frame’s line number.
  4. Rewrite the frame to point at src/*.ty line M.
  5. Replace the source row CPython printed under the frame with the real .ty row, and drop the column anchors (~~~~^^^^) — their columns refer to the emitted line, not this one.
  6. Print the remapped traceback.

The output looks like a normal Python traceback, but the frames are .ty paths and line numbers. Frames inside an ExceptionGroup traceback (every failed gather: block prints one) are rewritten too, keeping CPython’s | gutter.

Step 5 matters as much as step 4. Rewriting only the header leaves a frame reading File "src/main.ty", line 10 above a row of emitted Python — sometimes a generated name like __typhon_qi_0__ — that does not exist at main.ty:10, so opening the line named in the traceback shows something else. Where the .ty file is not readable from where tyc trace runs (a traceback pasted from another machine), the original row is kept rather than dropped.

When to use

  • After a production crash, on the captured stderr.
  • During development, to make pdb frames legible.
  • In CI logs, to show team members the .ty source rather than the emitted .py.

Flags

FlagDefaultEffect
--map-dir DIR—Extra directory to search for .py.map files (<DIR>/.sourcemaps/<file>.py.map, then the legacy adjacent <DIR>/<file>.py.map). Without it, each frame’s map is located from the frame’s own .py path: <dir>/.sourcemaps/<rel>.py.map, walking up to the build root, then a legacy <file>.py.map next to the .py.

There is no --no-color flag; the remapped traceback is printed as plain text.

Limitations

  • Works per-statement (the .py.map v2 granularity). Column-level remapping is a future enhancement.
  • Only frames whose .py has a .py.map sidecar are rewritten; stdlib and third-party frames are left alone.

A worked example

A program that raises:

src/main.ty
def divide(a: int, b: int) -> int:
return a // b
def main() -> None:
print(divide(10, 0)) # line 5
if __name__ == "__main__":
main()

Run and capture:

Terminal window
python build/main.py 2>err.log

err.log:

Traceback (most recent call last):
File "build/main.py", line 7, in <module>
main()
File "build/main.py", line 5, in main
print(divide(10, 0))
File "build/main.py", line 2, in divide
return a // b
ZeroDivisionError: integer division or modulo by zero

After tyc trace err.log:

Traceback (most recent call last):
File "src/main.ty", line 7, in <module>
main()
File "src/main.ty", line 5, in main
print(divide(10, 0))
File "src/main.ty", line 2, in divide
return a // b
ZeroDivisionError: integer division or modulo by zero

Where next