Metadata-Version: 2.4
Name: evariste-core
Version: 0.3.1
Summary: Live, dynamic network visualization of Python code — static call graph + runtime tracing in your browser.
Project-URL: Homepage, https://github.com/nzer94/evariste-core
Project-URL: Repository, https://github.com/nzer94/evariste-core
Project-URL: Issues, https://github.com/nzer94/evariste-core/issues
Project-URL: Documentation, https://github.com/nzer94/evariste-core#readme
Project-URL: Changelog, https://github.com/nzer94/evariste-core/blob/main/CHANGELOG.md
Author-email: nzer94 <nzer94@protonmail.ch>
License: MIT
License-File: LICENSE
Keywords: ast,call-graph,fastapi,observability,tracing,visualization
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Requires-Dist: fastapi>=0.110
Requires-Dist: httpx>=0.27
Requires-Dist: uvicorn>=0.27
Requires-Dist: websockets>=12.0
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Description-Content-Type: text/markdown

# Évariste

> Live, dynamic network visualization of Python code, see your code *as a living graph*.

`evariste-core` gives Python the kind of interactive, in-browser call-graph
view that lazy fuckers like me need to understand their code.

- **Static graph** — an AST-based map of your project (like *Go to Definition*
  rendered as a network). FastAPI routes are highlighted.
- **Runtime overlay** — a `@trace` decorator streams real calls over a
  WebSocket; nodes warm, edges light, and timings appear as calls happen.
- **FastAPI Explorer** — call your own endpoints from the UI and watch
  exactly which functions got touched, with captured arg values.

MIT-licensed.

## Install

```bash
pip install evariste-core
```

> The PyPI distribution is called `evariste-core` (the bare `evariste`
> name on PyPI belongs to an unrelated project of some French math teacher, Kudos to that bro, I'm a French math guy too). The Python import name
> is still just `evariste` — nothing in your code changes.

## Drop it into your FastAPI app


```python
from fastapi import FastAPI
import evariste

app = FastAPI()
evariste.attach(app)      # <-- the whole thing

@app.get("/users/{user_id}")
def get_user(user_id: str):
    return load(user_id)

@app.post("/orders")
def create_order(cart: Cart):
    return checkout(cart)
```

Run your app normally (`uvicorn myapp:app --reload`), open
**`http://localhost:8000/__evariste__/ui`** in a browser, and you'll see:

- The static graph of every function in your source tree.
- Endpoints highlighted and browsable from the left drawer.
- Live runtime overlay — every request lights up its call tree.
- A **FastAPI** tab where you can invoke routes from the UI and watch
  exactly which code paths fired, with captured arg values.


### What `attach()` does under the hood

- **Scans your source tree.** Walks up from the caller's file to the
  outermost Python package to find your project root, then runs the AST
  analyzer on it.
- **Auto-traces every route.** Wraps each registered `APIRoute`'s handler
  with `@evariste.trace` so HTTP requests appear in the graph without
  you decorating anything manually.
- **Mounts the UI + API** under `/__evariste__/*` on your own app —
  shares your port, your TLS, your reverse proxy.
- **Refuses non-loopback traffic** by default. Accidentally shipping
  `attach()` to prod does not leak source code.

### Safety knobs (all optional)

```python
evariste.attach(
    app,
    # Defaults: loopback-only, no auth, no arg capture.
    allow_hosts=("127.0.0.1", "::1"),       # widen if behind a trusted proxy
    auth_token=None,                         # set a bearer token to gate access
    capture_args=False,                      # include arg/return reprs in events
    source_path=None,                        # override the auto-detected scan root
    prefix="/__evariste__",                  # change the URL mount prefix
    enabled=None,                            # None → defer to EVARISTE_DISABLED env
)
```

**For production:** set `EVARISTE_DISABLED=1` in your prod environment.
The `attach()` call becomes a no-op with zero code changes. One-line
kill-switch your ops team controls.

## Install + run an example

A realistic FastAPI demo ships in `examples/demo_api.py`:

```bash
pip install evariste-core
python examples/demo_api.py
# → FastAPI on http://localhost:8000
# → Évariste UI on http://localhost:8000/__evariste__/ui
```

The demo seeds its own traffic in a background thread so you get
something visibly alive the moment you open the UI.

## Manual decoration (when you need more control)

`attach()` auto-traces your *endpoints*. If you want internal helpers
to appear in the live graph too, decorate them explicitly:

```python
import evariste

@evariste.trace
def validate(token: str) -> bool: ...

@evariste.trace
async def fetch_user(user_id: str) -> User: ...
```

`@trace` works on sync, async, and methods. It uses `functools.wraps` so
FastAPI's dependency system, Pydantic validation, and every other
introspecting tool keep seeing the original signature.

To wrap a whole module at once:

```python
import evariste, myapp.services
evariste.module_trace(myapp.services)
```

## Standalone mode and no FastAPI?

You can still use Évariste with any Python codebase. Decorate what you
care about, scan the source, start the server:

```python
import evariste

@evariste.trace
def compute(n):
    return sum(i * i for i in range(n))

@evariste.trace
def handle_request():
    return compute(10_000)

evariste.scan(".")              # populate the static graph
evariste.start()                # http://localhost:7878 opens your browser

for _ in range(50):
    handle_request()
```

Or skip the code entirely and run the CLI:

```bash
evariste view path/to/your/project
# scans + serves the static graph on localhost:7878
```

## Capturing args and return values

Off by default (zero overhead, zero leakage). Turn it on explicitly:

```python
evariste.attach(app, capture_args=True)     # for FastAPI apps
# or
evariste.set_capture(True, max_repr=200)    # anywhere
```

Captured values appear in the inspector's "last invocation" panel,
truncated to `max_repr` chars, safe-`repr`'d so broken `__repr__`
implementations can never break tracing.

## Caching events to disk + replay

```python
evariste.set_cache("evariste_events.jsonl")   # append-only JSONL

# Later:
evariste.replay("evariste_events.jsonl", speed=0.5)   # half-speed replay
```

Or from the CLI:

```bash
evariste replay evariste_events.jsonl --speed 1.0
```

## Public API

| Symbol | Purpose |
|---|---|
| `evariste.attach(app, **kw)`             | **FastAPI one-line integration.** Scans, auto-traces, mounts the UI. |
| `evariste.trace`                         | Decorator — wraps sync & async callables. |
| `evariste.module_trace(module)`          | Auto-wrap every public function/class in a module. |
| `evariste.scan(path, *, module=None)`    | AST analyzer — populate the static graph. |
| `evariste.start(host, port, **kw)`       | Launch the standalone viz server in a background thread. |
| `evariste.stop()`                        | Shut the standalone server down. |
| `evariste.set_capture(enabled, *, max_repr=120)` | Toggle arg/return capture. |
| `evariste.set_cache(path)`               | Persist events to a JSONL file. |
| `evariste.replay(path, *, speed=1.0)`    | Re-emit a cached event stream through the live UI. |
| `evariste.events()`                      | Snapshot of the in-memory ring buffer (for tests). |

## Why "Évariste"?

After [Évariste Galois](https://en.wikipedia.org/wiki/%C3%89variste_Galois) —
the mathematician who preferred dying a duel rather than losing face, and wrote Group theory the night before the fight.

Talk about badass.

## License

MIT — see [`LICENSE`](./LICENSE). Bug reports and PRs welcome.
