Metadata-Version: 2.4
Name: xoople-sdk
Version: 0.3.0
Summary: Official Python SDK for the Xoople Products API.
License-Expression: LicenseRef-Xoople-Proprietary
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3.12
Requires-Dist: requests>=2.28.1,<3
Requires-Dist: pydantic>=2.8.2
Requires-Dist: azure-identity>=1.16 ; extra == 'entra'
Requires-Dist: delta-sharing>=1.3.2 ; extra == 'sharing'
Requires-Dist: pandas>=2.2 ; extra == 'sharing'
Requires-Python: >=3.12
Project-URL: Homepage, https://xoople.com
Provides-Extra: entra
Provides-Extra: sharing
Description-Content-Type: text/markdown

# xoople-sdk

Official Python SDK for the Xoople Products API.

```python
from xoople.sdk import Config, XoopleClient, StaticToken, models

client = XoopleClient.from_credentials(
    "https://api.xoople.com", StaticToken("...")
)
# Or pass the complete transport configuration directly:
# client = XoopleClient(Config.from_env())

estimate = client.analyses.estimate(request)
analysis = client.analyses.create(request)

# `list` returns a paginator: iterate it for items across every page.
for run in client.analyses.runs.list(analysis.uid):
    print(run.state)

# Sub-resources take the parent analysis uid:
output = client.analyses.outputs.get(analysis.uid, output_uid)
access = client.analyses.outputs.generate_access_url(analysis.uid, output_uid, ttl_seconds=900)

# A table output resolves to a handle that reads itself:
table = client.table(analysis.uid)          # or client.table(uid, output=output_uid)
print(table.shared)                         # measures, grid, size, created time
df = table.to_pandas()                      # pip install "xoople-sdk[sharing]"
sdf = table.to_spark(spark)                 # your session, your connector

# Custom verbs are plain methods:
client.analyses.cancel(analysis.uid)
client.analyses.schedules.pause(analysis.uid, schedule_uid)

# Cross-analysis reads and catalogs are top-level:
client.schedules.list(state=models.ScheduleStatus.ACTIVE)
client.usage.limits()
client.catalog.measures(product="tabular_time_series")
```

`Config.from_env()` reads `XOOPLE_API_URL` and `XOOPLE_TOKEN`.

## Connection profiles

`Config.from_profile(path)` reads the URL and the credential from one file, for tools
that take a file and not an environment:

```json
{
  "version": 1,
  "api_url": "https://gateway.xoople.com",
  "auth": { "type": "bearer", "token": "<token>" },
  "expires_at": "2026-09-30T00:00:00Z"
}
```

`expires_at` is optional and is what the issuer stamped. It carries a UTC offset. A
profile past it raises `ProfileExpiredError` on load, not at the first request.

The file is a credential. Keep it out of projects and repositories. Restrict who can
read it.

`client.table()` is the authenticated half of `xoople.sdk.sharing`, which reads
a `.share` profile with no API credential at all. It downloads one profile for
the client's lifetime and removes it on `close()`, so keep the client open
through every read — Spark reads lazily. `client.table(uid,
profile_path=...)` uses a profile you own instead; the SDK never writes or
removes that one.

Every failure raises a subclass of `xoople.sdk.errors.XoopleError`.
