🌏 Language: 简体中文 | English
Make Python errors tsundere~
A global exception beautifier built on rich — zero-intrusion, purely configuration-driven, install-and-go, and shipped with a built-in tsundere personality.
Without installation / when disabled:

After installation and enabling:

- Theme switching: errors are no longer cold, bare stack traces. Change one line —
[general] theme = "..."— and freely switch to any style: sarcastic, healing, chuunibyou… whatever you like - Install & go: hooks are installed automatically at interpreter startup via
tsuntrack_auto.pth; zero changes to your business code - Configuration-driven: all error messages and styles come from a TOML config; colors are customizable, with support for partial overrides and extended placeholders
- Manual API: you can also enable it manually with
import tsuntrack; tsuntrack.install() - Smart hint enhancement: keeps and beautifies Python's native
did-you-meansuggestions - Rich text: stack traces come with syntax highlighting and multiple themes; the config supports Rich markup such as
[green]/[red] - Coroutine & thread support
Just want the error beautification? Set the
themefield to an empty string and give it a try.
Requires Python 3.11+. Install with pip:
pip install tsuntrackNo import needed after installation — just run any script that raises an error and see the result:
python -c "print(1/0)"importtsuntracktsuntrack.install() # Enabletsuntrack.uninstall() # Disable, restoring default behavior- The path specified by the
TSUNTRACK_CONFIGenvironment variable tsuntrack.tomlin the current working directory~/.config/tsuntrack/config.tomlin the user's home directory- The bundled
defaults.tomlinside the package
User configuration and defaults are deep-merged — just write the fields you want to change.
Five themes are built in; switch with [general] theme:
| Theme | Style | Description |
|---|---|---|
tsundere | Tsundere | Default theme; tough on the outside, soft inside |
neko | Catgirl | Calls you master and serves you dutifully |
sarcastic | Sharp-tongued | Biting sarcasm, but always tells the truth |
yandere | Yandere | Sweet on the surface, dangerously possessive |
chuunibyou | Chuunibyou | Every error comes with an anime trope |
Two languages are built in; switch with [general] language (default en): zh (Simplified Chinese) / en (English)
Want to contribute a new language or a new theme? copy
locales/en.toml, translate it, save it aslocales/xx.toml, and setlanguage = "xx".
[general]
enabled = true# Master switchtheme = "tsundere"# Current themelanguage = "zh"# Language: zh / enmax_frames = 5# Max stack frames shown (keeps the innermost ones)context_lines = 1# Lines of context code to showerror_line_style = "bold red"# Highlight color for the error lineline_number_style = "dim"# Line-number colorshow_hints = true# Whether to show hints# Context code highlighting:
[general.syntax]
# - theme: a built-in Pygments theme name (e.g. monokai / default / emacs / friendly / tango)theme = "monokai"
[general.syntax.styles]
# - styles: Pygments token name → Rich style. Token names such as Keyword / Name.Function / String.Doc.# Keys must be real Pygments tokens; made-up ones are hidden by default."Comment.Special" = "bold #5C6370""Comment" = "#5C6370""String.Doc" = "italic #5C6370""String" = "#98C379"...# Override the message for a specific exception inside a theme
[theme."tsundere".exceptions.NameError]
template = 'You dummy! I-I would never tell you that "{name}" is not defined. Hmph ╯^╰'The configuration merges in three layers, highest priority first:
- User config (
tsuntrack.toml/ file specified by the env var /~/.config/...) — directly overrides anything below - Theme override layer (
[theme."theme-name".*]) — overrides the base config according to the theme selected by[general] theme - Base config (the parts of
defaults.tomlother than[theme.*]) — shared by all themes, withexceptions.defaultas the fallback
# 1) Switch built-in themes
[general]
theme = "tsundere"# 2) Override any config directly
[exceptions.NameError]
template = 'My custom message: {name}'# 3) Custom themes (just add a [theme."name"...] section to your own config)
[general]
theme = "我的主题"
[theme."我的主题".exceptions.NameError]
template = 'My theme message: {name}'| Placeholder | Meaning | Example |
|---|---|---|
{name} | Variable / attribute / module / key name (NameError, AttributeError, ModuleNotFoundError, KeyError, etc.) | some_undefined_name |
{message} | The exception's own message text | division by zero |
{exc_type} | Exception type name | NameError |
{filename} | Path of the file where the error occurred (innermost stack frame) | C:\demo\app.py |
{lineno} | Line number of the error | 8 |
{func_name} | Name of the function where the error occurred | level_1 |
{module} | Name of the module where the error occurred | app |
You can add an [extra] section to your user config or a theme to define arbitrary extra placeholders:
# tsuntrack.toml (or the config specified by the environment variable)
[extra]
service = "order-api"env = "production"[theme."neko".exceptions.default]
template = 'Master, {service} failed in the {env} environment ┭┮﹏┭┮: {message}. Leave it to me!'Output: Master, order-api failed in production ┭┮﹏┭┮: ...
- Keys in
[extra]override built-in fields when they share a name with one in[general] - The theme layer works the same way:
[theme."X".extra]lets you define different placeholders per theme - You can also call it from code:
formatter.format_message(exc_type, exc_value, tb, cfg, extra={"service": "x"})
[general]
enabled = falsePut this in a config file at any priority level to disable globally (the hooks will not be installed).
sys.unraisablehookis not handled yetasynciointrudes into the global policy; foruvloopwe can only wrap it for nowIf another library also replaces
sys.excepthook, it may silently override this one- In modes such as
python -S(which doesn't loadsite),.pthfiles are not executed, so you need to calltsuntrack.install()manually
MIT