athena-python-docx 0.1.1__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/PKG-INFO +1 -1
  2. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/__init__.py +1 -1
  3. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/client.py +16 -8
  4. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/document.py +41 -14
  5. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/pyproject.toml +1 -1
  6. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/.gitignore +0 -0
  7. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/CLAUDE.md +0 -0
  8. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/README.md +0 -0
  9. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/_batching.py +0 -0
  10. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/api.py +0 -0
  11. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/enum/__init__.py +0 -0
  12. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/enum/table.py +0 -0
  13. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/enum/text.py +0 -0
  14. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/errors.py +0 -0
  15. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/shared.py +0 -0
  16. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/table.py +0 -0
  17. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/text/__init__.py +0 -0
  18. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/text/paragraph.py +0 -0
  19. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/text/run.py +0 -0
  20. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/docx/typing.py +0 -0
  21. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/scripts/publish.sh +0 -0
  22. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/tests/__init__.py +0 -0
  23. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/tests/conftest.py +0 -0
  24. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/tests/test_commands.py +0 -0
  25. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/tests/test_python_docx_api_parity.py +0 -0
  26. {athena_python_docx-0.1.1 → athena_python_docx-0.1.3}/tests/test_smoke_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-docx
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack
5
5
  Project-URL: Homepage, https://athenaintelligence.ai
6
6
  Author-email: Athena Intelligence <engineering@athenaintelligence.ai>
@@ -6,7 +6,7 @@ See CLAUDE.md for the API parity contract.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __version__ = "0.1.1"
9
+ __version__ = "0.1.3"
10
10
 
11
11
  from docx.api import Document
12
12
 
@@ -172,20 +172,28 @@ class Session:
172
172
  )
173
173
  return self._doc_handle
174
174
 
175
- async def save(self, *, in_place: bool = True) -> None:
176
- """Flush pending mutations to Keryx and persist.
177
-
178
- The AsyncSuperDocClient writes over WebSocket; we add a short
179
- flush delay (matches superdoc_write_utils.py:227-237) to
180
- ensure updates land before dispose().
175
+ async def save(self, *, in_place: bool = True) -> None: # noqa: ARG002
176
+ """Ensure pending mutations have flushed to Keryx.
177
+
178
+ In collab mode (our only mode), the Y-Websocket provider streams
179
+ updates to Keryx as they happen — there's no explicit save RPC
180
+ to call. The underlying Superdoc SDK's ``save({"inPlace": True})``
181
+ targets file-backed docs and errors with "this session has no
182
+ source path" for collab sessions. So save() is a thin flush:
183
+ sleep briefly so in-flight WebSocket frames land, then return.
184
+
185
+ The ``in_place`` kwarg is accepted for python-docx parity but
186
+ has no effect in collab mode (there is no alternate target).
181
187
  """
182
188
  if not self._opened:
183
189
  raise SessionError("Cannot save a session that was never opened.")
184
190
  if self._closed:
185
191
  raise DocumentClosedError(f"Session {self._asset_id} is closed.")
186
192
 
187
- await self._doc_handle.save({"inPlace": in_place})
188
- _log_info(f"Saved {self._asset_id}")
193
+ # Short flush — matches superdoc_write_utils.py's 1-second pre-close
194
+ # delay. Keeps a single in-flight frame from being dropped on close.
195
+ await asyncio.sleep(1)
196
+ _log_info(f"Saved (flushed) {self._asset_id}")
189
197
 
190
198
  async def close(self) -> None:
191
199
  """Close the Superdoc session. Idempotent."""
@@ -100,22 +100,49 @@ class Document:
100
100
  from docx.text.paragraph import Paragraph
101
101
 
102
102
  self._ensure_open()
103
- md: str
104
- if style and style.lower().startswith("heading"):
105
- try:
106
- level: int = int(style.rsplit(" ", 1)[-1])
107
- except ValueError:
108
- level = 1
109
- md = f"{'#' * level} {text}\n\n"
103
+
104
+ # Superdoc's markdown parser rejects empty input with
105
+ # "Markdown produced no content to insert." For an empty paragraph
106
+ # (python-docx's `doc.add_paragraph()` with no args), we use the
107
+ # text-type insert with a single space and immediately clear it —
108
+ # same pattern as `agora/web/api/super_docs/superdoc_write_utils.py`
109
+ # around line 1085.
110
+ result: dict
111
+ node_id: str
112
+ if not text and not (style and style.lower().startswith("heading")):
113
+ result = run_sync(
114
+ self._session.doc.insert(
115
+ {"value": " ", "type": "text"},
116
+ ),
117
+ )
118
+ node_id = _extract_inserted_node_id(result, expected_type="paragraph")
119
+ if node_id:
120
+ # Clear the placeholder space so the paragraph reads as empty
121
+ # (matches python-docx semantics: a freshly-added blank paragraph
122
+ # has empty `.text`).
123
+ run_sync(
124
+ self._session.doc.blocks.update_text(
125
+ {"nodeId": node_id, "text": ""},
126
+ ),
127
+ )
110
128
  else:
111
- md = f"{text}\n\n"
129
+ md: str
130
+ if style and style.lower().startswith("heading"):
131
+ try:
132
+ level: int = int(style.rsplit(" ", 1)[-1])
133
+ except ValueError:
134
+ level = 1
135
+ md = f"{'#' * level} {text}\n\n"
136
+ else:
137
+ md = f"{text}\n\n"
138
+
139
+ result = run_sync(
140
+ self._session.doc.insert(
141
+ {"value": md, "type": "markdown"},
142
+ ),
143
+ )
144
+ node_id = _extract_inserted_node_id(result, expected_type="paragraph")
112
145
 
113
- result: dict = run_sync(
114
- self._session.doc.insert(
115
- {"value": md, "type": "markdown"},
116
- ),
117
- )
118
- node_id: str = _extract_inserted_node_id(result, expected_type="paragraph")
119
146
  if not node_id:
120
147
  raise RuntimeError(
121
148
  f"Superdoc did not return a nodeId for add_paragraph: {result!r}",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-docx"
7
- version = "0.1.1"
7
+ version = "0.1.3"
8
8
  description = "Drop-in replacement for python-docx that connects to Athena's Superdoc/Keryx collaborative document stack"
9
9
  readme = "README.md"
10
10
  license = "MIT"