athena-python-pptx 0.4.1__tar.gz → 0.4.2__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.
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/CHANGELOG.md +25 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/CLAUDE.md +3 -2
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/DEV-GUIDE.md +3 -3
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/PKG-INFO +16 -13
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/README.md +15 -12
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/docs/API_PARITY_EXCEPTIONS.md +23 -6
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/__init__.py +4 -3
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/commands.py +4 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/presentation.py +69 -34
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/text/__init__.py +87 -21
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pyproject.toml +1 -1
- athena_python_pptx-0.4.2/uv.lock +1163 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/.gitignore +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/API_PARITY_REPORT.md +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/PARITY_QUESTIONS.md +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/PUBLISHING.md +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/docs/athena-api.json +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/docs/athena-api.md +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/_athena_extension.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/_ptc.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/_references.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/action.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/batching.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/__init__.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/axis.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/category.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/chart.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/data.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/datalabel.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/legend.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/marker.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/plot.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/point.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/series.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/chart/xlsx.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/client.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/decorators.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/dml/__init__.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/dml/chtfmt.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/dml/color.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/dml/effect.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/dml/fill.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/dml/line.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/docgen.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/__init__.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/action.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/chart.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/dml.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/lang.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/shapes.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/enum/text.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/errors.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/exc.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/media.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/package.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/__init__.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/_base.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/chart.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/coreprops.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/embeddedpackage.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/image.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/media.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/presentation.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/parts/slide.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/__init__.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/autoshape.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/base.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/connector.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/freeform.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/graphfrm.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/group.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/picture.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/placeholder.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shapes/shapetree.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/shared.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/slide.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/slides.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/spec.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/table.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/text/fonts.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/text/layout.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/text/text.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/types.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/typing.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/units.py +0 -0
- {athena_python_pptx-0.4.1 → athena_python_pptx-0.4.2}/pptx/util.py +0 -0
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `athena-python-pptx` are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.4.2
|
|
6
|
+
|
|
7
|
+
**Paragraph-level font fidelity — `paragraph.font` now matches stock
|
|
8
|
+
python-pptx `defRPr` semantics (fidelity-pipeline findings batch).**
|
|
9
|
+
|
|
10
|
+
- **`paragraph.font` is a real paragraph-level font.** Previously it
|
|
11
|
+
returned the first run's font ("approximate by targeting the first
|
|
12
|
+
run"), which (a) produced exports with explicit `<a:rPr>` on every run
|
|
13
|
+
where stock python-pptx writes `<a:pPr><a:defRPr/>` (the fidelity
|
|
14
|
+
pipeline's sdk-projection leg counted 229 structural diffs on the
|
|
15
|
+
25-slide diagnostic deck) and (b) mis-styled multi-run paragraphs (only
|
|
16
|
+
run 0 changed, where stock applies the default to ALL runs). Writes now
|
|
17
|
+
emit `SetParagraphStyle` with a `defaultRunStyle` payload; the studio
|
|
18
|
+
stores it as the paragraph's authored defaults and exports `defRPr`
|
|
19
|
+
exactly like stock.
|
|
20
|
+
- **`run.font.color.brightness` now round-trips.** Previously stored
|
|
21
|
+
locally and silently dropped; now emitted with the theme color and
|
|
22
|
+
serialized as `lumMod`/`lumOff` modifiers on `<a:schemeClr>` using
|
|
23
|
+
python-pptx's mapping.
|
|
24
|
+
- Studio-side (same batch): theme color references (`schemeColor`) now
|
|
25
|
+
survive on SDK-created textbox runs, and new paragraphs added via
|
|
26
|
+
`add_paragraph()`/multi-line `tf.text` no longer inherit the previous
|
|
27
|
+
paragraph's authored level/spacing/alignment (server-side paragraph
|
|
28
|
+
template bleed).
|
|
29
|
+
|
|
5
30
|
## 0.3.1
|
|
6
31
|
|
|
7
32
|
**Chart title runs, slide insertion, and shape-delete local state — Insight
|
|
@@ -45,8 +45,9 @@ A small number of REST-SDK-specific departures are documented in
|
|
|
45
45
|
[`docs/API_PARITY_EXCEPTIONS.md`](docs/API_PARITY_EXCEPTIONS.md):
|
|
46
46
|
|
|
47
47
|
- **`Presentation(filename)` not supported** — REST SDK uses
|
|
48
|
-
`Presentation.upload(path)` or `Presentation(
|
|
49
|
-
there's no local OPC
|
|
48
|
+
`Presentation.upload(path)` or `Presentation(asset_id=…)` instead
|
|
49
|
+
(`deck_id=` is the accepted legacy alias); there's no local OPC
|
|
50
|
+
package to open.
|
|
50
51
|
- **`NotesSlide.shapes` / `.placeholders` / `.notes_placeholder`** not yet exposed —
|
|
51
52
|
use `slide.notes_slide.notes_text_frame` for notes text.
|
|
52
53
|
- **`XyChartData.add_series(name, values)`** signature drift vs upstream's
|
|
@@ -23,9 +23,9 @@ Create `scratch.py` in the `python-sdk/` directory:
|
|
|
23
23
|
```python
|
|
24
24
|
from pptx import Presentation
|
|
25
25
|
|
|
26
|
-
# Upload a test file or use an existing
|
|
26
|
+
# Upload a test file or use an existing asset id
|
|
27
27
|
prs = Presentation.upload("path/to/test.pptx", name="test")
|
|
28
|
-
print(f"
|
|
28
|
+
print(f"Presentation: {prs.asset_id}, Slides: {prs.slide_count}")
|
|
29
29
|
|
|
30
30
|
slide = prs.slides[0]
|
|
31
31
|
for shape in slide.shapes:
|
|
@@ -100,5 +100,5 @@ The SDK is a **remote proxy** over the PPTX Studio API:
|
|
|
100
100
|
| Method | Example |
|
|
101
101
|
|--------|---------|
|
|
102
102
|
| Env vars | `ATHENA_PPTX_BASE_URL=http://localhost:4000` |
|
|
103
|
-
| Explicit | `Presentation(
|
|
103
|
+
| Explicit | `Presentation(asset_id="asset_...", base_url="http://localhost:4000")` (`deck_id=` is the legacy alias) |
|
|
104
104
|
| API key (optional) | `ATHENA_PPTX_API_KEY=your-key` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-pptx
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Drop-in replacement for python-pptx that connects to PPTX Studio for real-time collaboration
|
|
5
5
|
Project-URL: Homepage, https://github.com/pptx-studio/python-sdk
|
|
6
6
|
Project-URL: Documentation, https://docs.pptx-studio.com/sdk/python
|
|
@@ -202,7 +202,7 @@ export ATHENA_PPTX_API_KEY=your-api-key # Optional
|
|
|
202
202
|
Or pass them explicitly:
|
|
203
203
|
|
|
204
204
|
```python
|
|
205
|
-
prs = Presentation(
|
|
205
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5", base_url="...", api_key="...")
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
## Quick Start
|
|
@@ -246,24 +246,27 @@ slide.shapes[0].text_frame.text = "Updated Title"
|
|
|
246
246
|
prs.save("modified.pptx")
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
-
### Connect to an Existing
|
|
249
|
+
### Connect to an Existing Presentation
|
|
250
250
|
|
|
251
251
|
```python
|
|
252
252
|
from pptx import Presentation
|
|
253
253
|
|
|
254
|
-
# Connect to a
|
|
255
|
-
prs = Presentation(
|
|
254
|
+
# Connect to a presentation by its Athena asset id
|
|
255
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5")
|
|
256
256
|
|
|
257
257
|
# Work with slides
|
|
258
258
|
for slide in prs.slides:
|
|
259
259
|
print(f"Slide {slide.slide_index}: {len(slide.shapes)} shapes")
|
|
260
260
|
```
|
|
261
261
|
|
|
262
|
-
|
|
262
|
+
`deck_id=` is accepted as a legacy alias for `asset_id=` (it is the same
|
|
263
|
+
identifier string); prefer `asset_id=` in new code.
|
|
264
|
+
|
|
265
|
+
> **Important:** When working with an existing presentation, you **MUST** include the `asset_id` parameter:
|
|
263
266
|
> ```python
|
|
264
|
-
> prs = Presentation(
|
|
267
|
+
> prs = Presentation(asset_id="asset_d822b6e3-0a73-4214-9e71-8f28a3f7c9d9")
|
|
265
268
|
> ```
|
|
266
|
-
> Without `
|
|
269
|
+
> Without `asset_id`, the SDK cannot connect to PPTX Studio and will fail.
|
|
267
270
|
|
|
268
271
|
---
|
|
269
272
|
|
|
@@ -271,7 +274,7 @@ for slide in prs.slides:
|
|
|
271
274
|
|
|
272
275
|
### Presentation
|
|
273
276
|
|
|
274
|
-
The main entry point for working with a
|
|
277
|
+
The main entry point for working with a presentation.
|
|
275
278
|
|
|
276
279
|
#### Class Methods
|
|
277
280
|
|
|
@@ -286,14 +289,14 @@ prs = Presentation.open("path/to/file.pptx") # Alias for upload()
|
|
|
286
289
|
# Connect from a full URL
|
|
287
290
|
prs = Presentation.from_url("https://api.example.com/decks/deck_123")
|
|
288
291
|
|
|
289
|
-
# Connect to an existing
|
|
290
|
-
prs = Presentation(
|
|
292
|
+
# Connect to an existing presentation by asset id
|
|
293
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5")
|
|
291
294
|
```
|
|
292
295
|
|
|
293
296
|
#### Properties
|
|
294
297
|
|
|
295
298
|
```python
|
|
296
|
-
prs.
|
|
299
|
+
prs.asset_id # str: Athena asset id (prs.deck_id is the legacy alias)
|
|
297
300
|
prs.slides # Slides: Collection of slides
|
|
298
301
|
prs.slide_width # Emu: Width of slides
|
|
299
302
|
prs.slide_height # Emu: Height of slides
|
|
@@ -889,7 +892,7 @@ print(info['authToken'])
|
|
|
889
892
|
|
|
890
893
|
See [`docs/API_PARITY_EXCEPTIONS.md`](docs/API_PARITY_EXCEPTIONS.md) for the full list. Highlights:
|
|
891
894
|
|
|
892
|
-
- **`Presentation(filename)` not supported** — use `Presentation.upload(path)` or `Presentation(
|
|
895
|
+
- **`Presentation(filename)` not supported** — use `Presentation.upload(path)` or `Presentation(asset_id=…)` instead (`deck_id=` is the legacy alias).
|
|
893
896
|
- **`NotesSlide.shapes` / `.placeholders` / `.notes_placeholder`** not yet exposed — use `slide.notes_slide.notes_text_frame` for text.
|
|
894
897
|
- **`XyChartData.add_series(name, values)`** signature drift vs upstream's `add_series(name, number_format=None)`.
|
|
895
898
|
- **`TextFitter.best_fit_font_size()`** returns `max_size` unchanged (auto-fit is server-side).
|
|
@@ -162,7 +162,7 @@ export ATHENA_PPTX_API_KEY=your-api-key # Optional
|
|
|
162
162
|
Or pass them explicitly:
|
|
163
163
|
|
|
164
164
|
```python
|
|
165
|
-
prs = Presentation(
|
|
165
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5", base_url="...", api_key="...")
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
## Quick Start
|
|
@@ -206,24 +206,27 @@ slide.shapes[0].text_frame.text = "Updated Title"
|
|
|
206
206
|
prs.save("modified.pptx")
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
### Connect to an Existing
|
|
209
|
+
### Connect to an Existing Presentation
|
|
210
210
|
|
|
211
211
|
```python
|
|
212
212
|
from pptx import Presentation
|
|
213
213
|
|
|
214
|
-
# Connect to a
|
|
215
|
-
prs = Presentation(
|
|
214
|
+
# Connect to a presentation by its Athena asset id
|
|
215
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5")
|
|
216
216
|
|
|
217
217
|
# Work with slides
|
|
218
218
|
for slide in prs.slides:
|
|
219
219
|
print(f"Slide {slide.slide_index}: {len(slide.shapes)} shapes")
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
`deck_id=` is accepted as a legacy alias for `asset_id=` (it is the same
|
|
223
|
+
identifier string); prefer `asset_id=` in new code.
|
|
224
|
+
|
|
225
|
+
> **Important:** When working with an existing presentation, you **MUST** include the `asset_id` parameter:
|
|
223
226
|
> ```python
|
|
224
|
-
> prs = Presentation(
|
|
227
|
+
> prs = Presentation(asset_id="asset_d822b6e3-0a73-4214-9e71-8f28a3f7c9d9")
|
|
225
228
|
> ```
|
|
226
|
-
> Without `
|
|
229
|
+
> Without `asset_id`, the SDK cannot connect to PPTX Studio and will fail.
|
|
227
230
|
|
|
228
231
|
---
|
|
229
232
|
|
|
@@ -231,7 +234,7 @@ for slide in prs.slides:
|
|
|
231
234
|
|
|
232
235
|
### Presentation
|
|
233
236
|
|
|
234
|
-
The main entry point for working with a
|
|
237
|
+
The main entry point for working with a presentation.
|
|
235
238
|
|
|
236
239
|
#### Class Methods
|
|
237
240
|
|
|
@@ -246,14 +249,14 @@ prs = Presentation.open("path/to/file.pptx") # Alias for upload()
|
|
|
246
249
|
# Connect from a full URL
|
|
247
250
|
prs = Presentation.from_url("https://api.example.com/decks/deck_123")
|
|
248
251
|
|
|
249
|
-
# Connect to an existing
|
|
250
|
-
prs = Presentation(
|
|
252
|
+
# Connect to an existing presentation by asset id
|
|
253
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5")
|
|
251
254
|
```
|
|
252
255
|
|
|
253
256
|
#### Properties
|
|
254
257
|
|
|
255
258
|
```python
|
|
256
|
-
prs.
|
|
259
|
+
prs.asset_id # str: Athena asset id (prs.deck_id is the legacy alias)
|
|
257
260
|
prs.slides # Slides: Collection of slides
|
|
258
261
|
prs.slide_width # Emu: Width of slides
|
|
259
262
|
prs.slide_height # Emu: Height of slides
|
|
@@ -849,7 +852,7 @@ print(info['authToken'])
|
|
|
849
852
|
|
|
850
853
|
See [`docs/API_PARITY_EXCEPTIONS.md`](docs/API_PARITY_EXCEPTIONS.md) for the full list. Highlights:
|
|
851
854
|
|
|
852
|
-
- **`Presentation(filename)` not supported** — use `Presentation.upload(path)` or `Presentation(
|
|
855
|
+
- **`Presentation(filename)` not supported** — use `Presentation.upload(path)` or `Presentation(asset_id=…)` instead (`deck_id=` is the legacy alias).
|
|
853
856
|
- **`NotesSlide.shapes` / `.placeholders` / `.notes_placeholder`** not yet exposed — use `slide.notes_slide.notes_text_frame` for text.
|
|
854
857
|
- **`XyChartData.add_series(name, values)`** signature drift vs upstream's `add_series(name, number_format=None)`.
|
|
855
858
|
- **`TextFitter.best_fit_font_size()`** returns `max_size` unchanged (auto-fit is server-side).
|
|
@@ -746,18 +746,35 @@ Athena-only. Equivalent to calling `prs.set_slide_size(Emu(14630400),
|
|
|
746
746
|
Emu(8229600))` immediately after creation. For non-standard sizes call
|
|
747
747
|
`prs.set_slide_size(width, height)` after `create()`.
|
|
748
748
|
|
|
749
|
-
### `Presentation.asset_id` —
|
|
749
|
+
### `Presentation(asset_id=...)` / `Presentation.asset_id` — preferred spelling; `deck_id` is the legacy alias
|
|
750
750
|
|
|
751
751
|
The deck id and the Athena asset id are the same string
|
|
752
|
-
(`asset_<uuid>`); the
|
|
753
|
-
schemas, and most agent-facing copy refer to "asset id".
|
|
754
|
-
|
|
752
|
+
(`asset_<uuid>`); `asset_id` is the preferred name because Olympus
|
|
753
|
+
URLs, GraphQL schemas, and most agent-facing copy refer to "asset id".
|
|
754
|
+
Upstream `python-pptx` opens a local OPC package via
|
|
755
|
+
`Presentation(pptx_path)` — the REST SDK has no local package, so the
|
|
756
|
+
constructor reattaches to a server-side asset by id instead. Both the
|
|
757
|
+
constructor keyword and the read property exist in both spellings:
|
|
755
758
|
|
|
756
759
|
```python
|
|
757
|
-
prs
|
|
758
|
-
prs
|
|
760
|
+
prs = Presentation(asset_id="asset_3a93...") # preferred
|
|
761
|
+
prs = Presentation(deck_id="asset_3a93...") # legacy alias — same behaviour
|
|
762
|
+
|
|
763
|
+
prs.asset_id # "asset_3a93..." — preferred
|
|
764
|
+
prs.deck_id # same value — legacy alias
|
|
759
765
|
```
|
|
760
766
|
|
|
767
|
+
Rules (mirrors the `name`/`title` reconciliation on
|
|
768
|
+
`Presentation.create()`):
|
|
769
|
+
|
|
770
|
+
- `asset_id` and `deck_id` are interchangeable; internally both bind the
|
|
771
|
+
same identifier (`self._deck_id`).
|
|
772
|
+
- Passing **both** is allowed only when the values match; differing
|
|
773
|
+
values raise `ValueError`.
|
|
774
|
+
- Passing **neither** raises `TypeError` (previously `deck_id` was the
|
|
775
|
+
required first positional parameter; positional
|
|
776
|
+
`Presentation("asset_...")` still works unchanged).
|
|
777
|
+
|
|
761
778
|
### `Presentation.close()` — alias for `flush()`
|
|
762
779
|
|
|
763
780
|
Stock python-pptx has no flush/close concept (it writes packages
|
|
@@ -19,8 +19,9 @@ for real-time collaboration. Use exactly the same code you would with python-ppt
|
|
|
19
19
|
# Or upload an existing file
|
|
20
20
|
prs = Presentation.upload("my_presentation.pptx")
|
|
21
21
|
|
|
22
|
-
# Or connect to an existing
|
|
23
|
-
|
|
22
|
+
# Or connect to an existing presentation asset
|
|
23
|
+
# (deck_id= is accepted as a legacy alias for asset_id=)
|
|
24
|
+
prs = Presentation(asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5")
|
|
24
25
|
|
|
25
26
|
# Work with slides and shapes (same API as python-pptx)
|
|
26
27
|
slide = prs.slides[0]
|
|
@@ -132,7 +133,7 @@ def flush_all() -> None:
|
|
|
132
133
|
_active_buffers[:] = alive
|
|
133
134
|
|
|
134
135
|
|
|
135
|
-
__version__ = "0.4.
|
|
136
|
+
__version__ = "0.4.2"
|
|
136
137
|
|
|
137
138
|
__all__ = [
|
|
138
139
|
# Main entry point
|
|
@@ -294,6 +294,10 @@ class SetParagraphStyle(Command):
|
|
|
294
294
|
space_after_emu: Optional[int] = None
|
|
295
295
|
margin_left_emu: Optional[int] = None
|
|
296
296
|
indent_emu: Optional[int] = None
|
|
297
|
+
# Authored paragraph-level run defaults (python-pptx ``paragraph.font``).
|
|
298
|
+
# Keys are already wire-format camelCase (the Font style payload);
|
|
299
|
+
# the studio exports them as ``<a:pPr><a:defRPr …/></a:pPr>``.
|
|
300
|
+
default_run_style: Optional[dict] = None
|
|
297
301
|
|
|
298
302
|
@property
|
|
299
303
|
def command_type(self) -> str:
|
|
@@ -179,7 +179,7 @@ class Presentation:
|
|
|
179
179
|
from pptx.units import Inches
|
|
180
180
|
|
|
181
181
|
prs = Presentation(
|
|
182
|
-
|
|
182
|
+
asset_id="asset_3a9328bc-9c1c-4498-be8f-bda3883276f5",
|
|
183
183
|
base_url="https://api.pptx-studio.com",
|
|
184
184
|
api_key="sk_live_..."
|
|
185
185
|
)
|
|
@@ -192,41 +192,73 @@ class Presentation:
|
|
|
192
192
|
|
|
193
193
|
def __init__(
|
|
194
194
|
self,
|
|
195
|
-
deck_id: DeckId,
|
|
195
|
+
deck_id: Optional[DeckId] = None,
|
|
196
196
|
base_url: Optional[str] = None,
|
|
197
197
|
api_key: Optional[str] = None,
|
|
198
198
|
auto_refresh: bool = True,
|
|
199
|
+
*,
|
|
200
|
+
asset_id: Optional[DeckId] = None,
|
|
199
201
|
):
|
|
200
202
|
"""
|
|
201
203
|
Initialize a Presentation proxy.
|
|
202
204
|
|
|
203
205
|
Args:
|
|
204
|
-
deck_id:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
``Presentation.create(name=...)`` and reuse the
|
|
209
|
-
returned ``prs.deck_id`` for subsequent opens.
|
|
206
|
+
deck_id: Legacy alias for ``asset_id`` — the same identifier
|
|
207
|
+
string. Prefer ``asset_id``. Passing both is allowed
|
|
208
|
+
only when they match; differing values raise
|
|
209
|
+
``ValueError``.
|
|
210
210
|
base_url: Base URL of the API. If not provided, uses ATHENA_PPTX_BASE_URL
|
|
211
211
|
environment variable.
|
|
212
212
|
api_key: Optional API key for authentication. If not provided, uses
|
|
213
213
|
ATHENA_PPTX_API_KEY environment variable.
|
|
214
214
|
auto_refresh: Whether to automatically fetch snapshot on init
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
asset_id: ID of the presentation asset. MUST be ``asset_<uuid>``
|
|
216
|
+
for Athena assets or ``deck_<id>`` for legacy standalone
|
|
217
|
+
decks. Anything else (e.g. a presentation TITLE) raises
|
|
218
|
+
``ValueError`` before any HTTP request. To mint a new
|
|
219
|
+
asset, call ``Presentation.create(name=...)`` and reuse
|
|
220
|
+
the returned ``prs.asset_id`` for subsequent opens.
|
|
221
|
+
"""
|
|
222
|
+
if deck_id is not None and asset_id is not None and deck_id != asset_id:
|
|
217
223
|
raise ValueError(
|
|
218
|
-
|
|
224
|
+
"Presentation(asset_id=..., deck_id=...) — asset_id and "
|
|
225
|
+
"deck_id must match when both are provided (they are the "
|
|
226
|
+
"same identifier; deck_id is the legacy alias). Pass only "
|
|
227
|
+
"asset_id."
|
|
228
|
+
)
|
|
229
|
+
resolved_id = asset_id if asset_id is not None else deck_id
|
|
230
|
+
if resolved_id is None:
|
|
231
|
+
raise TypeError(
|
|
232
|
+
"Presentation() missing required argument 'asset_id' "
|
|
233
|
+
"(or its legacy alias 'deck_id')."
|
|
234
|
+
)
|
|
235
|
+
id_param = "deck_id" if asset_id is None else "asset_id"
|
|
236
|
+
if not resolved_id or not _DECK_ID_PATTERN.match(resolved_id):
|
|
237
|
+
if id_param == "asset_id":
|
|
238
|
+
raise ValueError(
|
|
239
|
+
f"Presentation(asset_id={resolved_id!r}) is not a valid "
|
|
240
|
+
f"asset id. Asset ids look like 'asset_<uuid>' "
|
|
241
|
+
f"(e.g., 'asset_3a9328bc-9c1c-4498-be8f-bda3883276f5'); "
|
|
242
|
+
f"legacy standalone decks use 'deck_<id>'. To open an "
|
|
243
|
+
f"existing presentation, pass its id — NOT its title. To "
|
|
244
|
+
f"create a new presentation titled {resolved_id!r}, call "
|
|
245
|
+
f"Presentation.create(name={resolved_id!r}) and reuse "
|
|
246
|
+
f"`prs.asset_id` on subsequent calls in the same "
|
|
247
|
+
f"conversation."
|
|
248
|
+
)
|
|
249
|
+
raise ValueError(
|
|
250
|
+
f"Presentation(deck_id={resolved_id!r}) is not a valid deck id. "
|
|
219
251
|
f"Deck ids look like 'asset_<uuid>' "
|
|
220
252
|
f"(e.g., 'asset_3a9328bc-9c1c-4498-be8f-bda3883276f5'); "
|
|
221
253
|
f"legacy standalone decks use 'deck_<id>'. To open an "
|
|
222
254
|
f"existing deck, pass its id — NOT its title. To create a "
|
|
223
|
-
f"new deck titled {
|
|
224
|
-
f"Presentation.create(name={
|
|
255
|
+
f"new deck titled {resolved_id!r}, call "
|
|
256
|
+
f"Presentation.create(name={resolved_id!r}) and reuse "
|
|
225
257
|
f"`prs.deck_id` on subsequent calls in the same conversation."
|
|
226
258
|
)
|
|
227
|
-
self._deck_id =
|
|
259
|
+
self._deck_id = resolved_id
|
|
228
260
|
self._client = Client(base_url=base_url, api_key=api_key)
|
|
229
|
-
self._buffer = CommandBuffer(self._client,
|
|
261
|
+
self._buffer = CommandBuffer(self._client, resolved_id)
|
|
230
262
|
self._snapshot: Optional[DeckSnapshot] = None
|
|
231
263
|
self._slides: Optional[Slides] = None
|
|
232
264
|
self._closed = False
|
|
@@ -265,7 +297,7 @@ class Presentation:
|
|
|
265
297
|
if auto_refresh:
|
|
266
298
|
from pptx import _ptc
|
|
267
299
|
|
|
268
|
-
call_id = _ptc.emit_begin("OpenPresentation", {"assetId":
|
|
300
|
+
call_id = _ptc.emit_begin("OpenPresentation", {"assetId": resolved_id})
|
|
269
301
|
try:
|
|
270
302
|
self.refresh()
|
|
271
303
|
except AuthenticationError as exc:
|
|
@@ -276,10 +308,10 @@ class Presentation:
|
|
|
276
308
|
is_error=True,
|
|
277
309
|
)
|
|
278
310
|
raise PermissionError(
|
|
279
|
-
f"Presentation(
|
|
311
|
+
f"Presentation({id_param}={resolved_id!r}) — access denied. "
|
|
280
312
|
f"The current user doesn't have permission to open this "
|
|
281
|
-
f"
|
|
282
|
-
f"and reachable from the current workspace."
|
|
313
|
+
f"presentation, or the id is invalid. Verify the id is "
|
|
314
|
+
f"correct and reachable from the current workspace."
|
|
283
315
|
) from exc
|
|
284
316
|
except RemoteError as exc:
|
|
285
317
|
_ptc.emit_end(
|
|
@@ -290,16 +322,18 @@ class Presentation:
|
|
|
290
322
|
)
|
|
291
323
|
if exc.status_code == 404:
|
|
292
324
|
raise ValueError(
|
|
293
|
-
f"Presentation(
|
|
294
|
-
f"To create a new
|
|
295
|
-
f"Presentation.create(name=...)
|
|
296
|
-
f"for subsequent opens in
|
|
325
|
+
f"Presentation({id_param}={resolved_id!r}) — "
|
|
326
|
+
f"presentation not found. To create a new "
|
|
327
|
+
f"presentation, call Presentation.create(name=...) "
|
|
328
|
+
f"and reuse `prs.asset_id` for subsequent opens in "
|
|
329
|
+
f"the same conversation."
|
|
297
330
|
) from exc
|
|
298
331
|
if exc.status_code in (401, 403):
|
|
299
332
|
raise PermissionError(
|
|
300
|
-
f"Presentation(
|
|
301
|
-
f"(HTTP {exc.status_code}). Verify the
|
|
302
|
-
f"that the current user has permission to
|
|
333
|
+
f"Presentation({id_param}={resolved_id!r}) — access "
|
|
334
|
+
f"denied (HTTP {exc.status_code}). Verify the asset "
|
|
335
|
+
f"id and that the current user has permission to "
|
|
336
|
+
f"open it."
|
|
303
337
|
) from exc
|
|
304
338
|
raise
|
|
305
339
|
except Exception as exc:
|
|
@@ -313,7 +347,7 @@ class Presentation:
|
|
|
313
347
|
_ptc.emit_end(
|
|
314
348
|
call_id=call_id,
|
|
315
349
|
tool_name="OpenPresentation",
|
|
316
|
-
result={"ok": True, "assetId":
|
|
350
|
+
result={"ok": True, "assetId": resolved_id},
|
|
317
351
|
is_error=False,
|
|
318
352
|
)
|
|
319
353
|
|
|
@@ -708,13 +742,14 @@ class Presentation:
|
|
|
708
742
|
description="Presentation.asset_id — Athena asset id alias for deck_id.",
|
|
709
743
|
)
|
|
710
744
|
def asset_id(self) -> DeckId:
|
|
711
|
-
"""
|
|
712
|
-
|
|
713
|
-
Athena hosts each presentation as an ``asset_<uuid>`` asset;
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
Athena-only ergonomic addition — not in upstream
|
|
745
|
+
"""Athena asset id of the presentation — preferred spelling.
|
|
746
|
+
|
|
747
|
+
Athena hosts each presentation as an ``asset_<uuid>`` asset; this
|
|
748
|
+
matches the URL pattern in Olympus, the GraphQL schema, and
|
|
749
|
+
agent-facing vocabulary. :attr:`deck_id` is the legacy alias and
|
|
750
|
+
returns the same string, as does the ``deck_id=`` constructor
|
|
751
|
+
keyword. Athena-only ergonomic addition — not in upstream
|
|
752
|
+
python-pptx.
|
|
718
753
|
"""
|
|
719
754
|
return self._deck_id
|
|
720
755
|
|