Metadata-Version: 2.4
Name: polnor
Version: 1.0.0
Summary: Polnor Python SDK — query Iceberg lakehouses, manage notebooks/jobs/models, MLflow-compatible tracking.
Author-email: Polnor <contact@polnor.net>
License: Apache-2.0
Project-URL: Homepage, https://polnor.net
Project-URL: Documentation, https://docs.polnor.net/sdk/python
Project-URL: Source, https://github.com/polnor/polnor
Keywords: polnor,lakehouse,iceberg,sql,mlflow,data-platform
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
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: License :: OSI Approved :: Apache Software License
Classifier: Topic :: Database
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Requires-Dist: tomli>=2.0; python_version < "3.11"
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: responses>=0.24; extra == "test"
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == "pandas"
Requires-Dist: pyarrow>=14.0; extra == "pandas"

# Polnor Python SDK

The official Python client for the [Polnor](https://polnor.net) lakehouse —
query Iceberg tables with SQL, manage notebooks / jobs / models / endpoints,
read & write pandas DataFrames, and use the PEP 249 driver for dbt, pandas,
sqlalchemy, and any tool that speaks standard Python DB API.

## Install

```bash
pip install polnor
```

## Quick start

```python
import polnor

# `polnor.sql()` runs against your default warehouse. Inside a Polnor
# notebook or job, credentials are injected automatically. On a laptop,
# set env vars first (see "Auth" below).
result = polnor.sql("SELECT * FROM demo.orders LIMIT 5")
print(result.rows)            # list of lists
print(result.to_pandas())     # pandas DataFrame
```

## Auth

The SDK resolves credentials in this order (first match wins):

1. `polnor.set_token("...", api_url="...", warehouse_id="...")` — explicit call
2. Environment variables: `POLNOR_API_URL`, `POLNOR_TOKEN`, `POLNOR_WAREHOUSE_ID`
3. `~/.polnor/config.toml` — TOML config file written by the `polnor` CLI

Inside a Polnor notebook or job, **all three POLNOR_* env vars are injected
by the runtime** — your cell-1 `polnor.sql(...)` works with no setup.

Local example (TOML config):

```toml
# ~/.polnor/config.toml
host = "https://api.polnor.net"
token = "<personal access token>"
workspace_id = "<your workspace>"
warehouse_id = "<your default warehouse>"
```

## What's in the box

| Module                | What it does                                                |
| --------------------- | ----------------------------------------------------------- |
| `polnor.sql(query)`   | Run SQL on a SQL warehouse, return rows / pandas / dicts.   |
| `polnor.warehouses`   | List / get / start / stop / default SQL warehouses.         |
| `polnor.compute`      | Manage compute VMs (start, stop, install_library, logs).    |
| `polnor.notebooks`    | Create / start / stop Jupyter notebooks on a compute.       |
| `polnor.jobs`         | Create + run DAG jobs, fetch logs, cancel, wait.            |
| `polnor.models`       | Model registry: versions, artifact URIs, framework tags.    |
| `polnor.endpoints`    | Serve a model image: deploy, predict, scale, stop.          |
| `polnor.tables`       | Browse databases (Iceberg namespaces) and tables.           |
| `polnor.read_pandas`  | Read a table (with `where=`, `limit=`) into a DataFrame.    |
| `polnor.write_pandas` | Bulk-insert a DataFrame (append / overwrite / create).      |
| `polnor.dbapi`        | PEP 249 driver: `connect()` / `cursor()` for dbt / pandas.  |
| `polnor.mlflow`       | MLflow-compatible tracking: `log_param`, `log_metric`, etc. |

## Examples

### SQL → pandas DataFrame

```python
df = polnor.sql("SELECT region, COUNT(*) c FROM demo.orders GROUP BY region").to_pandas()
df.plot(kind="bar")
```

### Push a DataFrame to a table

```python
import pandas as pd
df = pd.DataFrame({"id": [1, 2, 3], "name": ["alice", "bob", "carol"]})
polnor.write_pandas(df, "polnor.demo.default.users", mode="create")
```

### PEP 249 (works with dbt, pandas.read_sql, sqlalchemy)

```python
import pandas as pd
import polnor.dbapi as dbapi

conn = dbapi.connect()
df = pd.read_sql("SELECT * FROM demo.orders", conn)
```

### Notebooks, jobs, models, endpoints

```python
# Start a notebook on a CPU compute
polnor.compute.start("my-cpu", wait=True)
nb = polnor.notebooks.create(name="explore", compute_id="my-cpu")
polnor.notebooks.start("explore", wait=True)

# Run a DAG job
job = polnor.jobs.create(name="etl", tasks=[
    {"key": "extract",   "command": "python extract.py",   "image": "python:3.11"},
    {"key": "transform", "command": "python transform.py", "image": "python:3.11",
     "depends_on": ["extract"]},
])
run_id = polnor.jobs.run("etl")
polnor.jobs.wait(run_id)
print(polnor.jobs.logs(run_id))

# Serve a model
polnor.endpoints.create(name="fraud-v1", image="my-registry/fraud:1.0",
                        environment={"CONTAINER_PORT": "8080"})
polnor.endpoints.deploy("fraud-v1", wait=True)
pred = polnor.endpoints.predict("fraud-v1", inputs=[[0.1, 0.2, 0.3]])
```

### MLflow-compatible tracking

```python
from polnor import mlflow
with mlflow.start_run() as run:
    mlflow.log_param("lr", 0.01)
    mlflow.log_metric("accuracy", 0.93)
    mlflow.log_artifact("./model.pkl")
```

## Errors

The SDK raises these typed exceptions:

- `polnor.PolnorAPIError` — non-2xx HTTP from the control plane.
- `polnor.SQLError` — statement failed, cancelled, or timed out.
- `polnor.PolnorConfigError` — no credentials could be resolved.
- `polnor.dbapi.ProgrammingError` / `OperationalError` / etc. — PEP 249 hierarchy.

## Compatibility

- Python ≥ 3.9 (`tomli` falls in for 3.9 / 3.10; 3.11+ has `tomllib` built in)
- pandas integration: optional, lazily imported
- dbt: works via `polnor.dbapi` (a dedicated `dbt-polnor` adapter is in V2)

## Development

```bash
pip install -e ".[test]"
pytest
```

Tests use [responses](https://github.com/getsentry/responses) to mock HTTP —
no live API needed. The `tests/test_e2e_prod.py` suite (opt-in via env)
hits `api.polnor.net` and requires a running warehouse + valid PAT.

## Links

- Homepage: https://polnor.net
- API docs: https://docs.polnor.net
- Source / issues: https://github.com/polnor/polnor

## License

Apache-2.0.
