Skip to content

baolingao/butwhy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

butwhy

Stop guessing. Start understanding.

Your Python errors, explained — in plain English.

pip install butwhyimport butwhy → done.

PyPI Python 3.9+ License: MIT CI Zero Dependencies


The 30-second pitch

You know how Python tracebacks look like this:

Traceback (most recent call last):
  File "demo.py", line 2, in <module>
    result = data.split(",")
AttributeError: 'NoneType' object has no attribute 'split'

And then you spend 5 minutes on Google figuring out what went wrong?

why turns that into this:

============================================================
  AttributeError: 'NoneType' object has no attribute 'split'
  at demo.py:2
============================================================

  [pattern match · 93%]

  Summary:
    Calling .split() on None

  Cause:
    The variable data is None, not a string.
    This usually means a function returned None (maybe it
    forgot a return statement), or a lookup failed silently.

  Variables at error:
    data = None  (NoneType)

  Fix:
    Add a None check: if data is not None: data.split(",")
    Or trace back to find why data is None.

============================================================

One import. Zero config. Instant understanding.


Quick start

pip install butwhy
import butwhy  # That's it. Errors are now explained.

data = None
result = data.split(",")  # Boom — why explains it

Want AI-powered deep explanations?

Set one environment variable and why upgrades automatically:

export OPENAI_API_KEY=sk-...   # or ANTHROPIC_API_KEY

No API key? why still works — it falls back to built-in pattern matching that covers 12+ common error types with zero dependencies.

Prefer a local model? (No API key, fully offline)

# Install Ollama: https://ollama.com
ollama pull qwen2.5-coder:7b

export BUTWHY_PROVIDER=ollama

Three ways to use why

1. Global (recommended) — import butwhy

import butwhy

# Every uncaught exception in your script is now explained
1 / 0

2. Context manager — with butwhy.trace()

import butwhy

with butwhy.trace():
    # Only errors inside this block are explained
    risky_operation()

3. Decorator — @butwhy.explain

import butwhy

@butwhy.explain
def divide(a, b):
    return a / b

divide(10, 0)  # Error is explained, then re-raised

butwhy.fix() — Don't just explain, fix

After an error, call butwhy.fix() to get an AI-generated patch:

import butwhy

data = None
result = data.split(",")  # crashes

# In interactive mode:
>>> butwhy.fix()

  Suggested fix for demo.py:2
  --------------------------------------------------
  - result = data.split(",")
  + if data is not None:
  +     result = data.split(",")
  + else:
  +     result = []
  --------------------------------------------------

  Apply this fix? [y/N] y
  Applied fix to demo.py:2

Requires an AI provider (OpenAI/Anthropic/Ollama).


butwhy.this — The Zen of Why

>>> import butwhy
>>> butwhy.this
The Zen of Why, by why

Errors are not failures, they are teachers.
The traceback tells you what; why tells you why.
A good message answers the question before you ask it.
Read the variables, not just the line numbers.
...

Jupyter / IPython support

# In a notebook:
%load_ext why

# Or just:
import butwhy

# Now all cell errors are explained inline

Configuration

All settings via environment variables — no config file needed:

Variable Default Description
OPENAI_API_KEY OpenAI API key (enables AI explanations)
ANTHROPIC_API_KEY Anthropic API key (enables AI explanations)
BUTWHY_PROVIDER auto auto / openai / anthropic / ollama / patterns
BUTWHY_LANGUAGE en en / zh (Chinese)
BUTWHY_MODEL Override the model name for the active provider
BUTWHY_TIMEOUT 30 API timeout in seconds
OLLAMA_URL http://localhost:11434 Ollama server URL
OLLAMA_MODEL qwen2.5-coder:7b Ollama model to use
BUTWHY_AUTOSTART 1 Set to 0 to disable auto-install of the hook
BUTWHY_NO_COLOR Set to disable colored output
NO_COLOR Standard no-color env var (respected)

Programmatic config

import butwhy

butwhy.set_config(butwhy.WhyConfig(
    provider="openai",
    openai_api_key="sk-...",
    language="zh",        # Chinese explanations
    show_source=True,
    show_vars=True,
    cache=True,
))

How it works

why uses a three-layer fallback to ensure you always get a useful explanation:

Layer When How Quality
AI explanation API key available Sends error + source context + variables to LLM Best — covers everything, gives specific fixes
Pattern matching No API key (or AI failed) Heuristic matching on 12+ common error types Good — covers the errors you hit daily
Enhanced traceback No pattern matched Colored output with variables and source context Fallback — still better than default

Key design principles:

  • Zero dependencies — pure Python stdlib. No rich, no httpx, no openai SDK. Just urllib and friends.
  • Never crashes — if the AI fails, pattern matching takes over. If that fails, you still get a prettier traceback.
  • Privacy-aware — your code only goes to an LLM if you explicitly set an API key. Pattern matching is 100% local.
  • Caching — repeated errors don't re-call the API. Cached for 7 days.

Comparison

Feature why rich better-exceptions stackprinter Copilot
Works without API key
AI-powered explanations
import and done
Local model support ✅ Ollama
Zero dependencies
Variable values in output
Auto-fix (butwhy.fix())
Works in CI/CD & servers
Chinese explanations
Jupyter magic

Supported error types (pattern matching)

The built-in pattern matcher covers these error types without any API key:

  • NameError — undefined variable
  • TypeError — wrong type operations
  • ValueError — invalid values (int conversion, unpacking)
  • IndexError — list index out of range
  • KeyError — missing dictionary key
  • AttributeError — wrong attribute/method (including NoneType)
  • ZeroDivisionError — division by zero
  • ImportError / ModuleNotFoundError — missing modules (with install hints)
  • FileNotFoundError — file path issues
  • SyntaxError — common syntax mistakes
  • RecursionError — infinite recursion
  • UnicodeDecodeError — encoding issues
  • IndentationError / TabError — indentation problems

More patterns are added regularly. With an AI provider, all error types are covered.


Examples

See the examples/ directory for runnable demos:

  • basic.py — common errors with pattern matching
  • with_ai.py — AI-powered deep explanations
  • jupyter_demo.ipynb — notebook usage
  • fix_demo.py — auto-fix workflow

Contributing

Contributions welcome! Especially:

  • New pattern matchers for error types not yet covered
  • Translations for BUTWHY_LANGUAGE
  • Bug reports and edge cases
git clone https://github.com/BaolinGao/butwhy.git
cd butwhy
pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.


import butwhy — because every error deserves an explanation.

About

Stop guessing. Start understanding. Your Python errors, explained — in plain English. import butwhy and every crash gets a human-readable explanation + fix suggestion.

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages