Drop-in replacement for openpyxl that connects to XLSX Studio for real-time collaboration
Project description
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
Branch + agent attribution:
from openpyxl import Workbook
with Workbook(
"asset_abc",
branch="suggest-run-7",
custom_attributions=[
{"k": "agent_id", "v": "spreadsheet-author"},
{"k": "user_message_tracking_id", "v": "msg_xyz"},
],
) as wb:
wb.active["A1"] = "agent edit"
wb.save()
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) | |
ATHENA_ORG_ID |
Workspace/org override; the API resolves the workspace from the API key when this is unset |
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).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file athena_openpyxl-0.15.0.tar.gz.
File metadata
- Download URL: athena_openpyxl-0.15.0.tar.gz
- Upload date:
- Size: 347.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ebf268f4e66054fd902a9ca4823a42bf98f0a1fafbef65b701cce206c2408d0
|
|
| MD5 |
de205e11e6c8fbd36c0d55b75a1a1279
|
|
| BLAKE2b-256 |
1d97561645e8111939e761c4255080d95ffc0c630d43fac72f47f5268b5f2eb9
|
File details
Details for the file athena_openpyxl-0.15.0-py3-none-any.whl.
File metadata
- Download URL: athena_openpyxl-0.15.0-py3-none-any.whl
- Upload date:
- Size: 337.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
161bcc7f7680daf20d44429e96c770b11bba03aa912a8652dafa7699db5173ce
|
|
| MD5 |
f3fdc8c5823fa389dd536e24e506a710
|
|
| BLAKE2b-256 |
9fcb2ae63199917d1e7546a45358b450a0bf75c3676d7bb6316f4d957ec42e61
|