Metadata-Version: 2.4
Name: evariste-core
Version: 0.3.0
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 other ecosystems take for granted:

- **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.

Minimal, zero-frontend-build, 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). The Python import name
> is still just `evariste` — nothing in your code changes.

## The 60-second tour — drop it into your FastAPI app

One line. That's the integration.

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

No second server, no extra port, no code changes beyond that one call.

### 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 — 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 first saw structure as a *graph of relationships*.

## License

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