Metadata-Version: 2.4
Name: butwhy
Version: 0.1.0
Summary: Stop guessing. Start understanding. Your Python errors, explained.
Author: butwhy contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/yourname/butwhy
Project-URL: Repository, https://github.com/yourname/butwhy
Project-URL: Issues, https://github.com/yourname/butwhy/issues
Project-URL: Changelog, https://github.com/yourname/butwhy/blob/main/CHANGELOG.md
Keywords: error,debugging,traceback,ai,explanation,developer-tools,cli,llm,openai,anthropic,ollama,butwhy
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

<div align="center">

# butwhy

**Stop guessing. Start understanding.**

Your Python errors, explained — in plain English.

`pip install butwhy` → `import butwhy` → done.

[![PyPI](https://img.shields.io/pypi/v/butwhy)](https://pypi.org/project/butwhy/)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![CI](https://github.com/yourname/butwhy/actions/workflows/ci.yml/badge.svg)](https://github.com/yourname/butwhy/actions)
[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-green.svg)](#)

</div>

---

## 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

```bash
pip install butwhy
```

```python
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:

```bash
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)

```bash
# 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`

```python
import butwhy

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

### 2. Context manager — `with butwhy.trace()`

```python
import butwhy

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

### 3. Decorator — `@butwhy.explain`

```python
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:

```python
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

```python
>>> 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

```python
# 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

```python
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/`](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

```bash
git clone https://github.com/yourname/butwhy.git
cd butwhy
pip install -e ".[dev]"
pytest
```

---

## License

MIT — see [LICENSE](LICENSE).

---

<div align="center">

**`import butwhy`** — because every error deserves an explanation.

</div>
