Metadata-Version: 2.4
Name: spyre2
Version: 0.1.0
Summary: Build interactive data web apps in pure Python
Project-URL: Homepage, https://github.com/adamhajari/spyre2
Project-URL: Repository, https://github.com/adamhajari/spyre2
Project-URL: Issues, https://github.com/adamhajari/spyre2/issues
Author-email: Adam Hajari <adamhajari@gmail.com>
License: MIT
Keywords: dashboard,data,interactive,visualization,web
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.110.0
Requires-Dist: jinja2>=3.1.0
Requires-Dist: numpy>=1.23.0
Requires-Dist: pandas>=1.5.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: uvicorn[standard]>=0.29.0
Provides-Extra: all
Requires-Dist: altair>=5.0.0; extra == 'all'
Requires-Dist: matplotlib>=3.6.0; extra == 'all'
Requires-Dist: plotly>=5.0.0; extra == 'all'
Provides-Extra: altair
Requires-Dist: altair>=5.0.0; extra == 'altair'
Provides-Extra: matplotlib
Requires-Dist: matplotlib>=3.6.0; extra == 'matplotlib'
Provides-Extra: plotly
Requires-Dist: plotly>=5.0.0; extra == 'plotly'
Description-Content-Type: text/markdown

# spyre2

Build interactive data web apps in pure Python — no HTML, CSS, or JavaScript required.

Spyre2 is a ground-up rewrite of [spyre](https://github.com/adamhajari/spyre) using a modern stack: **FastAPI**, **Alpine.js**, and native support for **matplotlib**, **Plotly**, and **Altair**.

---

## Install

```bash
pip install spyre2
pip install spyre2[matplotlib]   # + matplotlib
pip install spyre2[plotly]        # + plotly
pip install spyre2[all]           # everything
```

---

## Quickstart

```python
import numpy as np
import matplotlib.pyplot as plt
import spyre2

class SineApp(spyre2.App):
    title = "Sine Wave"

    inputs = [
        spyre2.Slider("frequency", label="Frequency", min=1, max=20, default=5),
        spyre2.Dropdown("color", label="Color",
                        options=["steelblue", "crimson", "seagreen"]),
    ]

    outputs = [
        spyre2.Plot("sine_plot"),
    ]

    def sine_plot(self, frequency, color):
        fig, ax = plt.subplots(figsize=(8, 4))
        x = np.linspace(0, 2 * np.pi, 500)
        ax.plot(x, np.sin(frequency * x), color=color)
        return fig

SineApp().launch()
```

Open `http://127.0.0.1:8000`.

---

## Inputs

| Class | Description |
|---|---|
| `Slider(id, min, max, step, default)` | Numeric range slider |
| `Dropdown(id, options, default)` | Select dropdown |
| `RadioButtons(id, options, default)` | Radio button group |
| `CheckboxGroup(id, options, default)` | Multi-select checkboxes |
| `TextInput(id, default, placeholder)` | Free-text input |
| `FileUpload(id, accept)` | File picker |

All inputs accept a `label` keyword argument. If omitted, the label is inferred from the `id`.

---

## Outputs

| Class | Handler return type |
|---|---|
| `Plot(id)` | `matplotlib.figure.Figure` or `plotly.Figure` or `altair.Chart` |
| `Table(id)` | `pandas.DataFrame` |
| `HTML(id)` | `str` (HTML) |
| `Download(id)` | `(filename: str, content: str \| bytes)` |

The handler method name must match the output `id`:

```python
outputs = [spyre2.Plot("my_plot")]

def my_plot(self, **kwargs):   # method name = output id
    ...
    return fig
```

---

## Layouts

### Sidebar (default)

Controls on the left, outputs stacked on the right. No configuration needed.

### Grid

```python
from spyre2 import Layout

class MyApp(spyre2.App):
    layout = Layout.grid([
        ["controls",  "controls"  ],
        ["plot_a",    "plot_b"    ],
        ["big_table", "big_table" ],
    ])
```

Repeat an ID across adjacent cells to span columns. `"controls"` is a reserved name for the input panel.

### Tabs

```python
layout = Layout.tabs({
    "Overview": ["trend_plot"],
    "Detail":   ["data_table", "bar_chart"],
})
```

---

## Multiple chart libraries

The chart library is detected automatically from the return type — no configuration needed.

```python
# matplotlib
def my_plot(self, x):
    fig, ax = plt.subplots()
    ax.plot(...)
    return fig              # → rendered as SVG

# Plotly
import plotly.express as px
def my_plot(self, x):
    return px.scatter(df, x="a", y="b")   # → rendered via Plotly.js

# Altair
import altair as alt
def my_plot(self, x):
    return alt.Chart(df).mark_line()...   # → rendered via Vega-Embed
```

---

## Jupyter notebooks

`app.launch()` detects when it's running inside a Jupyter kernel and automatically starts the server in a background thread and displays an inline `IFrame`.

```python
SineApp().launch(port=8765)   # displays inline in the notebook
```

---

## Migrating from spyre v1

Use the included CLI tool to mechanically convert a v1 app:

```bash
spyre2-migrate myapp.py              # preview conversion
spyre2-migrate myapp.py -o new.py    # write to new file
spyre2-migrate myapp.py --in-place   # overwrite (creates .bak backup)
```

The tool converts imports, `inputs`/`outputs` list-of-dicts, and renames `getPlot`/`getTable` etc., leaving `# TODO` comments where manual cleanup is needed.

**What changes:**

| spyre v1 | spyre2 |
|---|---|
| `from spyre import server` | `import spyre2` |
| `class App(server.App)` | `class App(spyre2.App)` |
| `inputs = [{"type": "slider", "key": "x", ...}]` | `inputs = [spyre2.Slider("x", ...)]` |
| `def getPlot(self, params): x = params["freq"]` | `def my_plot(self, freq):` |
| CherryPy on port 9093 | uvicorn on port 8000 |

For apps that can't be fully migrated yet, `spyre2.compat.App` accepts the old dict-based syntax (deprecated, will be removed in a future release).

---

## Examples

| File | Demonstrates |
|---|---|
| `examples/sine_example.py` | Sidebar layout, matplotlib, slider + dropdown |
| `examples/grid_example.py` | Grid layout, matplotlib, multiple outputs + table |
| `examples/plotly_example.py` | Tabs layout, Plotly, scatter + histogram |
