athena-openpyxl
openpyxl-shaped Python SDK that talks to xlsx-studio's HTTP API. Writes go through POST /workbooks/:id/commands; the Fastify service applies them to a server-side pycrdt Doc and pushes Y.Doc updates to Keryx so Olympus's @rowsncolumns/y-spreadsheet renderer sees the change live. The SDK process never opens its own y-websocket — there is no pycrdt dependency on the SDK side.
Status: v0.2.0 — Tier-A surface complete (full openpyxl public API up to formatting + dates), Tier-B surfaces (tables / data validation / conditional formatting / defined names / comments / images / hyperlinks) wired through the Keryx Y.Doc.
Quick start
from openpyxl import Workbook
with Workbook("asset_abc123") as wb: # opens Keryx session, runs initial sync
ws = wb.active
ws["A1"] = "Hello"
ws["A2"] = 42
ws["A3"] = "=SUM(A2)"
wb.save() # flushes pending updates to Keryx
Bulk writes via batch context (one logical activity-log entry):
from openpyxl import Workbook
with Workbook("asset_abc") as wb:
ws = wb.active
with wb.batch():
for row_idx in range(1, 1001):
ws.cell(row_idx, 1, f"row {row_idx}")
wb.save()
Environment
The SDK reads these env vars (Daytona-managed in production):
| Variable | Required | Notes |
|---|---|---|
ATHENA_XLSX_BASE_URL |
✓ | xlsx-studio API URL, e.g. https://xlsx-api.stg.athenaintel.com |
ATHENA_XLSX_API_KEY |
Bearer token for the apps/api ownership middleware (omit for local dev with no auth). The workspace is resolved server-side from this key. |
Install (development)
cd xlsx-studio/python-sdk
uv venv && uv pip install -e ".[dev]"
uv run pytest tests/
Public surface (v0.2.0)
Tier A — Core (fully implemented):
| Surface | Status |
|---|---|
Workbook lifecycle (Workbook(asset_id), load_workbook, save, close, ctx mgr) |
✅ |
Sheet listing (sheetnames, worksheets, active, wb[title]) |
✅ |
Single-cell I/O (ws['A1'], ws.cell(r, c), cell.value) |
✅ |
Range access (ws['A1:C3'], ws['A:C'], ws[1]) |
✅ |
Bulk writes (ws.append, iter_rows, iter_cols) |
✅ |
Dimensions (column_dimensions, row_dimensions, width, height, hidden, outline_level, group) |
✅ |
Structural mutations (create_sheet, remove, move_sheet, copy_worksheet, ws.title = x) |
✅ |
Merged cells (merge_cells, unmerge_cells, merged_cells) |
✅ |
Freeze panes (ws.freeze_panes) |
✅ |
Auto filter (ws.auto_filter.ref) |
✅ |
Cell formatting (font, fill, alignment, border, protection, number_format, style) |
✅ |
| Date / datetime / time values | ✅ |
Tier B — Common (wired through Keryx Y.Doc):
| Surface | Status |
|---|---|
wb.defined_names / DefinedName |
✅ |
ws.tables / Table / TableStyleInfo |
✅ |
ws.data_validations / DataValidation |
✅ |
ws.conditional_formatting / Rule classes |
✅ |
cell.comment / Comment |
✅ |
cell.hyperlink / Hyperlink |
✅ |
ws.add_image / Image |
✅ |
wb.named_styles / NamedStyle (object model only) |
partial |
Tier C — Advanced (raises UnsupportedFeatureError):
- Charts and chartsheets (waiting on the cross-studio chart engine extraction from
pptx-studio— seepptx-studio/docs/CHARTING_REFACTOR_PLAN_2026-04-21.md). - Pivot tables.
- Sheet/workbook protection write side.
- Page setup write side.
- Macros / VBA passthrough.
See docs/API_PARITY_EXCEPTIONS.md for the canonical list of intentional deviations.
Common gotchas
-
Vertical alignment defaults to
bottom, notcenter. Both Excel and openpyxl defaultAlignment.verticalto"bottom", so a brand-new KPI tile, banner row, or centered headline cell will draw text glued to the bottom edge until you setvertical="center"explicitly. Set it on the cell that anchors the visual:from openpyxl.styles import Alignment cell.alignment = Alignment(horizontal="center", vertical="center")
The same applies to merged ranges — set the alignment on the anchor cell (top-left of the merge) before or after
ws.merge_cells(...); the renderer reads the anchor cell's style. Thews.merge_and_style(...)helper does this correctly out of the box.
Architecture
Agent code (openpyxl idiom)
↓
Workbook / Worksheet / Cell (sync facade)
↓ CommandBuffer.append(<cmd>)
batching.CommandBuffer (groups + flushes)
↓ Client.post_commands(...)
HTTP POST /workbooks/:id/commands (Bearer auth + ownership middleware)
↓
xlsx-studio apps/api (Fastify) (validates schema, runs applier.ts)
↓ pycrdt Doc transactions
Keryx ←→ Olympus (@rowsncolumns/y-spreadsheet renderer)
(Y.Doc lives server-side; room = "{workspace}/{asset_id}")
client.py—requests-backed HTTP client: auth, retries (urllib3 Retry), 401/409/4xx error mapping (AuthenticationError/ConflictError/RemoteError), and the workbook / commands / snapshot / export endpoints.batching.py—CommandBufferqueues SDK calls into a singlePOST /commandspayload (so one cell or one bulk write becomes one HTTP round-trip, not N) andflush_all()for end-of-script drain.commands.py— typed dataclasses (SetCellValue,SetCellStyle,SetIterativeCalculation, …) with.validate()and.to_dict(). Each one round-trips againstapps/api/src/commands/types.tsZod schemas.cell/,worksheet/,workbook/,styles/,comments/,formatting/,drawing/,pivot/,tables.py— openpyxl-shaped surface; reads materialize from snapshot (Client.get_snapshot), writes append commands to the buffer.
Publishing
bash ../scripts/publish-python-sdk.sh 0.2.0
Development workflow
# Install editable + dev tools.
uv venv && uv pip install -e ".[dev]"
# Run the unit suite (no Keryx required).
uv run pytest tests/ -x
# Lint.
uv run ruff check openpyxl tests
Tests
The SDK ships ~190 unit tests covering: utility helpers, all five style descriptors (Font / Fill / Alignment / Border / Protection / NamedStyle), Workbook lifecycle / registry / flush_all, single-cell I/O, range access, iter_rows / iter_cols, append, max_row / max_column, dimensions, structural mutations, merges, freeze panes, auto filter, cell formatting, dates, comments, hyperlinks, and the UnsupportedFeatureError boundary.
Tests stub the HTTP transport (Client._request) and assert on the captured command payloads — no network access required.
The integration / parity / fidelity lanes live separately (see the roadmap doc at xlsx-studio/docs/ATHENA_OPENPYXL_ROADMAP_2026-04-29.md).
Release files for athena-openpyxl 0.24.17
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| athena_openpyxl-0.24.17.tar.gz | 490.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| athena_openpyxl-0.24.17-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 933.1 kB
Release files / athena_openpyxl-0.24.17.tar.gz
| Download URL | athena_openpyxl-0.24.17.tar.gz |
|---|---|
| Size | 490.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
618c27f35457d2db27587e817a63e6046469fd05bd84573a87b916d51bb306d9
|
|
BLAKE2b-256 checksum How to use checksums |
42f6e34f1d0849728a8815bc8a5383c1f7a49ddfefce82822182354f79e2b6d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.14
|
Release files / athena_openpyxl-0.24.17-py3-none-any.whl
| Download URL | athena_openpyxl-0.24.17-py3-none-any.whl |
|---|---|
| Size | 442.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b5e7524ee91d70077ab4bbf3064446d5ef157cf89b5e66e493006439eae7567
|
|
BLAKE2b-256 checksum How to use checksums |
9e7a0700e6eb9f9e6e8175aeaf865c375e569ca838b108417a4cb4c72c8026e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.14
|