athena-python-pptx 0.1.59__tar.gz → 0.1.63__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.1.63/CHANGELOG.md +194 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/CLAUDE.md +8 -2
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/PKG-INFO +1 -1
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/__init__.py +3 -2
- athena_python_pptx-0.1.63/pptx/action.py +34 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/chart/__init__.py +3 -0
- athena_python_pptx-0.1.63/pptx/chart/category.py +17 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/commands.py +5 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/enum/shapes.py +196 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/presentation.py +14 -3
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/shapes.py +328 -17
- athena_python_pptx-0.1.63/pptx/slide.py +28 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/slides.py +100 -15
- athena_python_pptx-0.1.63/pptx/table.py +15 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pyproject.toml +1 -1
- athena_python_pptx-0.1.59/CHANGELOG.md +0 -48
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/.gitignore +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/API_PARITY_REPORT.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/DEV-GUIDE.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/PUBLISHING.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/README.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/docs/API_PARITY_EXCEPTIONS.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/docs/athena-api.json +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/docs/athena-api.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/parity-tests/.gitignore +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/parity-tests/GAP_REPORT.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/parity-tests/README.md +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/parity-tests/_harness.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/parity-tests/runner.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/batching.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/chart/data.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/client.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/decorators.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/dml/__init__.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/dml/color.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/docgen.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/enum/__init__.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/enum/action.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/enum/chart.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/enum/dml.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/enum/text.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/errors.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/text.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/typing.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/units.py +0 -0
- {athena_python_pptx-0.1.59 → athena_python_pptx-0.1.63}/pptx/util.py +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `athena-python-pptx` are documented in this file.
|
|
4
|
+
|
|
5
|
+
## 0.1.63
|
|
6
|
+
|
|
7
|
+
Theme-hierarchy parity round (originally targeted 0.1.62, but PyPI's 0.1.62
|
|
8
|
+
was claimed by an unrelated upload on 2026-05-06 — bumped to 0.1.63 to keep
|
|
9
|
+
the SDK and PyPI sequence in step). — closes the remaining python-pptx surface gaps
|
|
10
|
+
on `Presentation`, `SlideMaster`, `SlideLayout`, `Slides`, and `SlideShapes`
|
|
11
|
+
that the parity test was allow-listing as KNOWN_MISSING.
|
|
12
|
+
|
|
13
|
+
- **`Presentation.slide_master`** — shorthand for `slide_masters[0]`. Matches
|
|
14
|
+
python-pptx where most single-master decks reach for the canonical master
|
|
15
|
+
via this attribute. Raises `IndexError` if the snapshot has no masters.
|
|
16
|
+
- **`SlideMaster.shapes`** / **`SlideMaster.placeholders`** — return empty
|
|
17
|
+
read-only collections instead of raising `AttributeError`. The REST snapshot
|
|
18
|
+
doesn't materialize master-level shapes as first-class elements (they are
|
|
19
|
+
projected onto each slide during ingestion), but code that probes these
|
|
20
|
+
collections for parity now gets a sensible empty result.
|
|
21
|
+
- **`SlideLayout.shapes`** — same treatment as the master-level collection.
|
|
22
|
+
- **`SlideLayout.slide_master`** — backreference to the parent
|
|
23
|
+
`SlideMaster`. `_build_slide_masters` now threads the master onto each
|
|
24
|
+
child layout when constructing the theme hierarchy, and the backreference
|
|
25
|
+
is `None` only on the legacy hardcoded fallback layouts (used when no
|
|
26
|
+
snapshot is available).
|
|
27
|
+
- **`Slides.get(slide_id, default=None)`** — matches python-pptx's
|
|
28
|
+
dict-style lookup. Same backing store as `Slides.get_by_id`, with the
|
|
29
|
+
added optional default-return semantics.
|
|
30
|
+
- **`SlideShapes.add_group_shape(shapes=())`** — alias that materializes any
|
|
31
|
+
iterable to a list and forwards to `group_shapes(...)`. Consistent with
|
|
32
|
+
python-pptx's signature; the SDK still requires at least 2 shapes per
|
|
33
|
+
group (one `GroupShapes` command per call).
|
|
34
|
+
- **Parity test cleanup** — `tests/test_python_pptx_api_parity.py` no longer
|
|
35
|
+
allow-lists the items above under `KNOWN_MISSING`. Type mismatches for the
|
|
36
|
+
new `@property` accessors are documented in `KNOWN_TYPE_MISMATCHES`
|
|
37
|
+
(functionally identical to python-pptx's `lazyproperty`).
|
|
38
|
+
|
|
39
|
+
## 0.1.61
|
|
40
|
+
|
|
41
|
+
Parity round: structural module shims + python-pptx-aligned signatures.
|
|
42
|
+
|
|
43
|
+
- **Module shims** so user code written against python-pptx imports unchanged:
|
|
44
|
+
`pptx.slide` (Slide / Slides / SlideLayout / SlideMaster / SlideMasters /
|
|
45
|
+
SlideBackground / SlideLayouts), `pptx.table` (Table / `_Cell`), and
|
|
46
|
+
`pptx.action` (Hyperlink, ActionSetting placeholder).
|
|
47
|
+
- **`Presentation.save(file=...)`** — renamed kwarg from `path=` to match
|
|
48
|
+
python-pptx exactly. Positional callers (`prs.save("out.pptx")`) keep
|
|
49
|
+
working; keyword callers using `path=` must update.
|
|
50
|
+
- **`Slides.add_slide(slide_layout=...)`** — renamed kwarg from `layout=` to
|
|
51
|
+
match python-pptx. `add_blank_slide()` updated internally.
|
|
52
|
+
- **`Table.cell(row_idx, col_idx)`** and the `GraphicFrame.cell` shortcut —
|
|
53
|
+
renamed parameters from `(row, col)` to match python-pptx.
|
|
54
|
+
- **`SlideShapes.add_chart(x, y, cx, cy, ...)`** — renamed positional
|
|
55
|
+
parameters from `(left, top, width, height)` to match python-pptx (EMU
|
|
56
|
+
semantics unchanged).
|
|
57
|
+
- **`pptx.SlideMaster`** — added top-level export (was only available via
|
|
58
|
+
`pptx.slides.SlideMaster` previously).
|
|
59
|
+
- **Parity test coverage doubled** — `tests/test_python_pptx_api_parity.py`
|
|
60
|
+
now compares 17 user-facing classes against stock python-pptx, up from 8.
|
|
61
|
+
Newly covered: Presentation, Slide, Slides, SlideLayout, SlideMaster,
|
|
62
|
+
SlideShapes, Table, `_Cell`. All deviations documented as either
|
|
63
|
+
REST-internal omissions (`element` / `part`) or SDK-extension extras.
|
|
64
|
+
|
|
65
|
+
## 0.1.60
|
|
66
|
+
|
|
67
|
+
Round 5 fidelity push — close diffs the parity harness surfaces against
|
|
68
|
+
`lowes_template_v1.pptx` for `add_shape` / `add_textbox` / `add_picture` /
|
|
69
|
+
`add_connector` / `add_table`. End-to-end: SDK → command → applier → Y.Doc →
|
|
70
|
+
export-worker → OOXML.
|
|
71
|
+
|
|
72
|
+
- **python-pptx-style auto-naming for new shapes:**
|
|
73
|
+
`slide.shapes.add_shape(MSO_SHAPE.OVAL, …)` now writes
|
|
74
|
+
`<p:cNvPr name="Oval 2"/>` instead of `name="Shape qR6BLH"`. Mirrors
|
|
75
|
+
python-pptx's deterministic `"<basename> <count_after_add>"` contract so
|
|
76
|
+
AI agents can find shapes by predictable names. Same scheme for
|
|
77
|
+
`add_textbox` (`"TextBox 2"`), `add_picture` (`"Picture 2"`),
|
|
78
|
+
`add_table` (`"Table 2"`), and `add_connector`
|
|
79
|
+
(`"Straight Connector 5"` / `"Elbow Connector …"` / `"Curved Connector …"`).
|
|
80
|
+
New `MSO_SHAPE → basename` map covers all 156 standard auto-shape
|
|
81
|
+
display names (`"Right Triangle"`, `"Rounded Rectangle"`,
|
|
82
|
+
`"Left-Right Arrow"`, etc.). `name` field added to `AddShape` /
|
|
83
|
+
`AddTextBox` / `AddPicture` / `AddConnector` / `AddTable` commands and
|
|
84
|
+
schemas; the applier stores it on the Y.Doc element and the
|
|
85
|
+
export-worker plumbs it through `CreateElement.shapeName`.
|
|
86
|
+
- **Default `<p:txBody>` on `add_shape`:** SDK auto-shapes without text
|
|
87
|
+
now emit python-pptx's standard placeholder body
|
|
88
|
+
(`<a:bodyPr rtlCol="0" anchor="ctr"/><a:lstStyle/><a:p><a:pPr algn="ctr"/></a:p>`)
|
|
89
|
+
so PowerPoint's "click to type text" affordance survives an export.
|
|
90
|
+
Previously athena dropped `<p:txBody>` entirely when no text was set,
|
|
91
|
+
which made downstream PowerPoint editing harder.
|
|
92
|
+
- **Default `<a:bodyPr>` on `add_textbox`:** new SDK textboxes now write
|
|
93
|
+
`<a:bodyPr wrap="none"><a:spAutoFit/></a:bodyPr>` (python-pptx's
|
|
94
|
+
default — auto-shrink to text). Previously athena wrote `wrap="square"`
|
|
95
|
+
which kept the box at its declared size and wrapped instead. Override
|
|
96
|
+
via `text_frame.word_wrap = True` to restore square-wrap behaviour.
|
|
97
|
+
|
|
98
|
+
## 0.1.59
|
|
99
|
+
|
|
100
|
+
Multi-round fidelity push (PR #19786). All items below are end-to-end:
|
|
101
|
+
SDK setter → command → applier → Y.Doc → export-worker → OOXML.
|
|
102
|
+
|
|
103
|
+
- **Picture crop end-to-end (`Shape.crop_left/top/right/bottom`):**
|
|
104
|
+
exported `<p:pic>` now emits `<a:srcRect l/t/r/b="..."/>` inside
|
|
105
|
+
`<a:blipFill>` for both `slide.shapes.add_picture(...)` and
|
|
106
|
+
`placeholder.insert_picture(...)`. Crop fractions clamp to [0,1]
|
|
107
|
+
and convert to OOXML's 1000ths-of-percent.
|
|
108
|
+
- **Server-side `<c:title>` for SDK-authored charts (Gap 16
|
|
109
|
+
follow-up):** `chart.has_title = True; chart.chart_title.text_frame.text = '...'`
|
|
110
|
+
on a freshly-added chart now lands in the rendered chart XML. New
|
|
111
|
+
helper folds queued `ChartPatch` ops into the `ChartSpec` before
|
|
112
|
+
authoring (handles `SetChartTitle`, `SetLegendVisible`,
|
|
113
|
+
`UpdateSeriesValue/Name`, `UpdateCategoryLabel`, `SetSeriesColor`).
|
|
114
|
+
- **Scatter / bubble `replace_data` (Gap 18 follow-up):**
|
|
115
|
+
`chart.replace_data(XyChartData)` and `chart.replace_data(BubbleChartData)`
|
|
116
|
+
now mutate the rendered chart's `<c:xVal>` / `<c:yVal>` /
|
|
117
|
+
`<c:bubbleSize>` via two new patch ops (`UpdateSeriesXValue`,
|
|
118
|
+
`UpdateBubbleSize`).
|
|
119
|
+
- **Run strike + sub/superscript export:** `Run.font.strike = True`,
|
|
120
|
+
`Run.font.subscript = True`, `Run.font.superscript = True` now emit
|
|
121
|
+
`<a:rPr strike="sngStrike"/>` and `<a:rPr baseline="-25000|30000"/>`
|
|
122
|
+
on both SDK-authored runs and `SetTextRunStyle` patches. Schema also
|
|
123
|
+
accepts a raw `baseline: int|null` override.
|
|
124
|
+
- **Connector elementType end-to-end:**
|
|
125
|
+
`slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, ...)` now
|
|
126
|
+
renders on export as `<p:cxnSp prst="..."/>` with
|
|
127
|
+
`<a:headEnd>/<a:tailEnd>` arrow attributes when set. Previously
|
|
128
|
+
`add_connector` was silently dropped at export time.
|
|
129
|
+
- **`add_slide(layout)` routes via snapshot-resolved `layoutPath`:**
|
|
130
|
+
closes the SDK→server layout-index mismatch that broke
|
|
131
|
+
`placeholder.insert_picture()` on Picture-with-Caption slides. The
|
|
132
|
+
SDK's `slide_layouts` ordering comes from the master's
|
|
133
|
+
`<p:sldLayoutIdLst>`, which generally does NOT match the
|
|
134
|
+
`slideLayout{i+1}.xml` filename numbering the server fell back to.
|
|
135
|
+
`add_slide` now forwards `layout._path` (and `layout.name` as
|
|
136
|
+
fallback); the applier prefers `layoutPath > layoutName > layoutIndex`.
|
|
137
|
+
- **Theme/scheme color support for shape fill + line:**
|
|
138
|
+
`shape.fill.fore_color.theme_color = MSO_THEME_COLOR.ACCENT_1` and
|
|
139
|
+
`shape.line.color.theme_color = MSO_THEME_COLOR.HYPERLINK` round-trip
|
|
140
|
+
end-to-end as `<a:solidFill><a:schemeClr val="accent1"/></a:solidFill>`
|
|
141
|
+
in the exported OOXML. AI-driven brand templates can now keep
|
|
142
|
+
customers' theme colors instead of hard-coding sRGB. New
|
|
143
|
+
`SetShapeStyle` fields `fill_scheme_color` / `line_scheme_color`;
|
|
144
|
+
setting one clears the corresponding sRGB so each `<a:solidFill>`
|
|
145
|
+
slot has a single declaration.
|
|
146
|
+
- **`Picture.crop_*` on `SubstitutePlaceholder(picture)`:** crop
|
|
147
|
+
fields now flow through the substitute-placeholder applier path so
|
|
148
|
+
cropped images filling layout placeholders honour the crop on
|
|
149
|
+
export.
|
|
150
|
+
|
|
151
|
+
## 0.1.58
|
|
152
|
+
|
|
153
|
+
- **Gap 18 (scatter / bubble chart authoring):** `slide.shapes.add_chart(XL_CHART_TYPE.XY_SCATTER, ..., XyChartData(...))` and `BUBBLE` with `BubbleChartData(...)` now author end-to-end. Server-side: chart-ooxml emits `<c:scatterChart>` / `<c:bubbleChart>` with `<c:xVal>` / `<c:yVal>` (and `<c:bubbleSize>` for bubble), pairs of value axes (no category axis), and an embedded workbook with x/y/(size) columns per series. SDK: `_split_chart_type` accepts all five `XY_SCATTER*` variants plus `BUBBLE`; `add_chart` and `insert_chart` validate the chart_data shape against the chart-type family. `replace_data` for XY/bubble is still gated (needs new patch ops; see GAP_REPORT.md Gap 18).
|
|
154
|
+
- **Gap 9 (notesMaster export):** export-worker now bootstraps a `ppt/notesMasters/notesMaster1.xml` + companion `ppt/theme/themeN.xml` whenever speaker notes are added to a deck without a notesMaster. Generators are extracted into `apps/export-worker/src/notes-master.ts`. Bootstrap path also registers the `notesMaster` relationship in `presentation.xml.rels` and adds content-type overrides for the new parts. Matches python-pptx's behavior for the same flow.
|
|
155
|
+
|
|
156
|
+
## 0.1.57
|
|
157
|
+
|
|
158
|
+
- **Gap 13 (line.dash_style export):** SDK shapes / textboxes with `line.dash_style = MSO_LINE_DASH_STYLE.DASH` (or any preset) now emit `<a:prstDash val=...>` inside `<a:ln>` on export. Schema, ooxml-patch shape-creator, and export-worker plumbing all updated.
|
|
159
|
+
- **Gap 14 (table column widths / row heights):** `table.columns[i].width` and `table.rows[i].height` on SDK-created tables now emit explicit `<a:gridCol w=...>` and `<a:tr h=...>` instead of dividing evenly.
|
|
160
|
+
- **Gap 15 (cell.merge export):** `cell.merge(other)` on SDK-created tables now emits `gridSpan="..."` / `rowSpan="..."` on the merge origin and `hMerge="1"` / `vMerge="1"` on consumed cells.
|
|
161
|
+
- **Gap 11 (run.hyperlink export):** `run.hyperlink.address = "https://..."` on SDK-created textboxes now writes `<a:hlinkClick r:id="rId..."/>` plus a corresponding External hyperlink relationship in the slide's `.rels`. Hyperlinked runs auto-underline to match python-pptx visual default.
|
|
162
|
+
- **Gap 10 (group_shapes/.ungroup id mapping):** In immediate (non-batched) mode `slide.shapes.group_shapes(...)` now flushes the buffer and stamps the real server-assigned id onto the returned `Shape`, so `group.ungroup()` works in the same statement chain.
|
|
163
|
+
- **Presentation.slide_width / slide_height setters:** `prs.slide_width = Inches(13.333)` and `prs.slide_height = Inches(7.5)` now work for python-pptx parity (route through `set_slide_size`).
|
|
164
|
+
- **Font.strike / Font.subscript / Font.superscript:** new properties on `Run.font` for python-pptx parity. Persist via the existing run-style emission pipeline.
|
|
165
|
+
- **Picture crop (`Shape.crop_top` / `crop_bottom` / `crop_left` / `crop_right`):** image shapes now expose python-pptx-compatible crop fractions (0.0–1.0). Setters emit a new `SetPictureCrop` command that writes to the Y.Doc element via `updateElementCrop`.
|
|
166
|
+
- **Connector geometry (`Shape.begin_x` / `begin_y` / `end_x` / `end_y`):** connector shapes expose the python-pptx endpoint API. Setters update the bounding box + `flipH/flipV` accordingly.
|
|
167
|
+
- **GroupShape.shapes children traversal:** for `is_group` shapes, `shape.shapes` returns the slide-level Shape objects whose ids match the group's `childIds`, mirroring `python-pptx GroupShape.shapes`.
|
|
168
|
+
- New `SetPictureCrop` command on the SDK→server protocol; applier wired through `updateElementCrop`.
|
|
169
|
+
|
|
170
|
+
## 0.1.56
|
|
171
|
+
|
|
172
|
+
- **Charts: end-to-end author + edit support** for column / bar / line / area / pie / doughnut (including all stacked variants).
|
|
173
|
+
- `slide.shapes.add_chart(chart_type, left, top, width, height, chart_data)` now authors a fresh chart from scratch — previously raised `UnsupportedFeatureError`. Returns a `GraphicFrame` whose `.chart` property exposes the resulting `Chart` object. Supported types: `XL_CHART_TYPE.COLUMN_CLUSTERED`, `COLUMN_STACKED`, `COLUMN_STACKED_100`, `BAR_CLUSTERED`, `BAR_STACKED`, `BAR_STACKED_100`, `LINE`, `LINE_MARKERS`, `LINE_STACKED`, `LINE_STACKED_100`, `AREA`, `AREA_STACKED`, `AREA_STACKED_100`, `PIE`, `DOUGHNUT`. 3-D / scatter / bubble / radar / stock / surface / combo still raise `UnsupportedFeatureError` with a clearer message listing the supported set.
|
|
174
|
+
- `placeholder.insert_chart(chart_type, chart_data)` now wires up the same path — previously raised `NotImplementedError`.
|
|
175
|
+
- `Chart.replace_data(chart_data)` now emits `UpdateChartData` patches against an existing chart (ingested or just-authored) — previously raised `UnsupportedFeatureError`. Rewrites series values, series names, and category labels in place; embedded `.xlsx` workbook stays in sync.
|
|
176
|
+
- New `Chart.chart_title` setter and `Chart.has_legend` setter emit `SetChartTitle` and `SetLegendVisible` patches respectively.
|
|
177
|
+
- `CategoryChartData.add_series()` and `XyChartData` / `BubbleChartData` now capture data client-side instead of raising eagerly. (Authoring scatter / bubble charts still raises at `add_chart()` time.)
|
|
178
|
+
- `GraphicFrame` extended to host a `Chart` (in addition to `Table`), so `gf.chart` and `gf.has_chart` work.
|
|
179
|
+
|
|
180
|
+
## 0.1.55
|
|
181
|
+
|
|
182
|
+
- Internal: bumped version coordinated with the `Presentation.create()` server-side fix (#19270). No SDK API change.
|
|
183
|
+
|
|
184
|
+
## 0.1.54
|
|
185
|
+
|
|
186
|
+
- `RemoteError.__str__` now includes the HTTP status code (e.g., `[HTTP 400] Invalid request body: ...`) so the status is visible in tracebacks without unpacking `exc.status_code`.
|
|
187
|
+
|
|
188
|
+
## 0.1.39
|
|
189
|
+
|
|
190
|
+
- Added SDK support for `slide.shapes.add_table(...)` and table creation command wiring.
|
|
191
|
+
- Added `slide.notes_slide.notes_text_frame.text` compatibility adapter for python-pptx style notes access.
|
|
192
|
+
- Added support for auto-shape text frame access (`shape.text_frame`) to match python-pptx behavior.
|
|
193
|
+
- Added smoke/integration tests for table creation/cell updates, notes slide adapter, and shape text-frame regression.
|
|
194
|
+
- Updated README examples for notes slide adapter and auto-shape text support.
|
|
@@ -23,13 +23,19 @@ Before adding or modifying any API surface:
|
|
|
23
23
|
3. Confirm the method/property/parameter exists with the same name and signature
|
|
24
24
|
4. If it doesn't exist in python-pptx, **do not add it** without explicit user approval
|
|
25
25
|
|
|
26
|
-
### Current status (v0.1.
|
|
26
|
+
### Current status (v0.1.60)
|
|
27
27
|
|
|
28
28
|
**Core classes are at 1:1 parity** with python-pptx: FillFormat, LineFormat, RGBColor, ColorFormat, Font. All legacy convenience methods on these classes have been removed.
|
|
29
29
|
|
|
30
|
+
**Module path shims** mirror stock python-pptx layout: `pptx.slide`, `pptx.table`, `pptx.action`. User code written for python-pptx (`from pptx.table import _Cell`, `from pptx.slide import Slide`) imports unchanged.
|
|
31
|
+
|
|
32
|
+
**Structural classes** (Presentation, Slide, Slides, SlideLayout, SlideMaster, SlideShapes, Table, `_Cell`) are parity-tested with deviations documented:
|
|
33
|
+
- Stock-only members like `element` / `part` are intentionally absent (REST SDK has no XML/OPC parts).
|
|
34
|
+
- SDK-specific extras (e.g., `Table.add_row()`, `Slide.title_text`, `Presentation.from_url()`) are listed in the test allowlist.
|
|
35
|
+
|
|
30
36
|
**TextFrame, Paragraph, and Run** still have non-standard convenience extras (e.g., `to_dict()`, `word_count`, `capitalize()`, string helpers). These are candidates for removal in a future cleanup pass.
|
|
31
37
|
|
|
32
|
-
Run `python -m pytest tests/test_python_pptx_api_parity.py -v -s` to verify parity across
|
|
38
|
+
Run `python -m pytest tests/test_python_pptx_api_parity.py -v -s` to verify parity across 17 classes.
|
|
33
39
|
|
|
34
40
|
### Intentionally omitted (REST SDK limitations)
|
|
35
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-pptx
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.63
|
|
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
|
|
@@ -37,7 +37,7 @@ a clear message explaining what's not supported.
|
|
|
37
37
|
from .presentation import Presentation
|
|
38
38
|
|
|
39
39
|
# Slide classes
|
|
40
|
-
from .slides import Slide, Slides, SlideLayout, SlideLayouts, SlideMasters, SlideBackground
|
|
40
|
+
from .slides import Slide, Slides, SlideLayout, SlideLayouts, SlideMaster, SlideMasters, SlideBackground
|
|
41
41
|
|
|
42
42
|
# Shape classes
|
|
43
43
|
from .shapes import (
|
|
@@ -127,7 +127,7 @@ def flush_all() -> None:
|
|
|
127
127
|
_active_buffers[:] = alive
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
__version__ = "0.1.
|
|
130
|
+
__version__ = "0.1.63"
|
|
131
131
|
|
|
132
132
|
__all__ = [
|
|
133
133
|
# Main entry point
|
|
@@ -137,6 +137,7 @@ __all__ = [
|
|
|
137
137
|
"Slides",
|
|
138
138
|
"SlideLayout",
|
|
139
139
|
"SlideLayouts",
|
|
140
|
+
"SlideMaster",
|
|
140
141
|
"SlideMasters",
|
|
141
142
|
"SlideBackground",
|
|
142
143
|
# Shape classes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Stock python-pptx exposes hyperlink/action classes via ``pptx.action``.
|
|
2
|
+
|
|
3
|
+
Mirror that module path so user code written for python-pptx
|
|
4
|
+
(``from pptx.action import Hyperlink, ActionSetting``) keeps working.
|
|
5
|
+
|
|
6
|
+
``ActionSetting`` and the ``PP_ACTION`` enum are not yet implemented in this
|
|
7
|
+
REST SDK — only ``Hyperlink`` is functional. The placeholders are provided so
|
|
8
|
+
that imports succeed and ``isinstance``-style checks behave predictably; they
|
|
9
|
+
will raise ``UnsupportedFeatureError`` if instantiated.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from .errors import UnsupportedFeatureError
|
|
15
|
+
from .text import Hyperlink
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ActionSetting:
|
|
19
|
+
"""Placeholder for python-pptx's ``ActionSetting``.
|
|
20
|
+
|
|
21
|
+
Click-action support is not yet implemented in athena-python-pptx. The
|
|
22
|
+
class exists so that ``from pptx.action import ActionSetting`` succeeds;
|
|
23
|
+
instantiation raises :class:`pptx.errors.UnsupportedFeatureError`.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, *_args: object, **_kwargs: object) -> None:
|
|
27
|
+
raise UnsupportedFeatureError(
|
|
28
|
+
"ActionSetting is not implemented in athena-python-pptx. "
|
|
29
|
+
"Use Run.hyperlink for URL hyperlinks; click-actions to slides "
|
|
30
|
+
"are not yet supported.",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = ["ActionSetting", "Hyperlink"]
|
|
@@ -4,9 +4,12 @@ Chart module for python-pptx compatibility.
|
|
|
4
4
|
Provides chart-related classes (currently stubs for unimplemented features).
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from .category import Categories, Category
|
|
7
8
|
from .data import CategoryChartData, ChartData, XyChartData, BubbleChartData
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
11
|
+
"Categories",
|
|
12
|
+
"Category",
|
|
10
13
|
"CategoryChartData",
|
|
11
14
|
"ChartData",
|
|
12
15
|
"XyChartData",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Category sequence classes for python-pptx compatibility."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Category(str):
|
|
10
|
+
"""Single chart category label."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Categories(tuple[Category, ...]):
|
|
14
|
+
"""Read-only chart category label sequence."""
|
|
15
|
+
|
|
16
|
+
def __new__(cls, values: Iterable[Any] = ()) -> "Categories":
|
|
17
|
+
return super().__new__(cls, (Category(str(value)) for value in values))
|
|
@@ -71,6 +71,7 @@ class AddTextBox(Command):
|
|
|
71
71
|
font_size_pt: Optional[float] = None
|
|
72
72
|
bold: Optional[bool] = None
|
|
73
73
|
italic: Optional[bool] = None
|
|
74
|
+
name: Optional[str] = None
|
|
74
75
|
|
|
75
76
|
@property
|
|
76
77
|
def command_type(self) -> str:
|
|
@@ -325,6 +326,7 @@ class AddShape(Command):
|
|
|
325
326
|
line_color_hex: Optional[str] = None
|
|
326
327
|
line_width_emu: Optional[int] = None
|
|
327
328
|
text: Optional[str] = None
|
|
329
|
+
name: Optional[str] = None
|
|
328
330
|
|
|
329
331
|
@property
|
|
330
332
|
def command_type(self) -> str:
|
|
@@ -403,6 +405,7 @@ class AddPicture(Command):
|
|
|
403
405
|
w_emu: Optional[int] = None
|
|
404
406
|
h_emu: Optional[int] = None
|
|
405
407
|
client_id: Optional[str] = None
|
|
408
|
+
name: Optional[str] = None
|
|
406
409
|
|
|
407
410
|
@property
|
|
408
411
|
def command_type(self) -> str:
|
|
@@ -443,6 +446,7 @@ class AddTable(Command):
|
|
|
443
446
|
w_emu: int
|
|
444
447
|
h_emu: int
|
|
445
448
|
client_id: Optional[str] = None
|
|
449
|
+
name: Optional[str] = None
|
|
446
450
|
|
|
447
451
|
@property
|
|
448
452
|
def command_type(self) -> str:
|
|
@@ -1024,6 +1028,7 @@ class AddConnector(Command):
|
|
|
1024
1028
|
begin_arrow: Optional[str] = None
|
|
1025
1029
|
end_arrow: Optional[str] = None
|
|
1026
1030
|
client_id: Optional[str] = None
|
|
1031
|
+
name: Optional[str] = None
|
|
1027
1032
|
|
|
1028
1033
|
@property
|
|
1029
1034
|
def command_type(self) -> str:
|
|
@@ -576,3 +576,199 @@ class PP_PLACEHOLDER(IntEnum):
|
|
|
576
576
|
|
|
577
577
|
# Alias for compatibility
|
|
578
578
|
PP_PLACEHOLDER_TYPE = PP_PLACEHOLDER
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
# ---------------------------------------------------------------------------
|
|
582
|
+
# MSO_SHAPE → display "basename" used by python-pptx for default shape names.
|
|
583
|
+
# python-pptx names new auto-shapes as "<basename> <slide_shape_count_after>".
|
|
584
|
+
# We mirror that contract so AI agents can find shapes by predictable names.
|
|
585
|
+
# ---------------------------------------------------------------------------
|
|
586
|
+
_MSO_SHAPE_BASENAME = {
|
|
587
|
+
1: 'Rectangle',
|
|
588
|
+
2: 'Parallelogram',
|
|
589
|
+
3: 'Trapezoid',
|
|
590
|
+
4: 'Diamond',
|
|
591
|
+
5: 'Rounded Rectangle',
|
|
592
|
+
6: 'Octagon',
|
|
593
|
+
7: 'Isosceles Triangle',
|
|
594
|
+
8: 'Right Triangle',
|
|
595
|
+
9: 'Oval',
|
|
596
|
+
10: 'Hexagon',
|
|
597
|
+
11: 'Cross',
|
|
598
|
+
12: 'Regular Pentagon',
|
|
599
|
+
13: 'Can',
|
|
600
|
+
14: 'Cube',
|
|
601
|
+
15: 'Bevel',
|
|
602
|
+
16: 'Folded Corner',
|
|
603
|
+
17: 'Smiley Face',
|
|
604
|
+
18: 'Donut',
|
|
605
|
+
19: '"No" Symbol',
|
|
606
|
+
20: 'Block Arc',
|
|
607
|
+
21: 'Heart',
|
|
608
|
+
22: 'Lightning Bolt',
|
|
609
|
+
23: 'Sun',
|
|
610
|
+
24: 'Moon',
|
|
611
|
+
25: 'Arc',
|
|
612
|
+
26: 'Double Bracket',
|
|
613
|
+
27: 'Double Brace',
|
|
614
|
+
28: 'Plaque',
|
|
615
|
+
29: 'Left Bracket',
|
|
616
|
+
30: 'Right Bracket',
|
|
617
|
+
31: 'Left Brace',
|
|
618
|
+
32: 'Right Brace',
|
|
619
|
+
33: 'Right Arrow',
|
|
620
|
+
34: 'Left Arrow',
|
|
621
|
+
35: 'Up Arrow',
|
|
622
|
+
36: 'Down Arrow',
|
|
623
|
+
37: 'Left-Right Arrow',
|
|
624
|
+
38: 'Up-Down Arrow',
|
|
625
|
+
39: 'Quad Arrow',
|
|
626
|
+
40: 'Left-Right-Up Arrow',
|
|
627
|
+
41: 'Bent Arrow',
|
|
628
|
+
42: 'U-Turn Arrow',
|
|
629
|
+
43: 'Left-Up Arrow',
|
|
630
|
+
44: 'Bent-Up Arrow',
|
|
631
|
+
45: 'Curved Right Arrow',
|
|
632
|
+
46: 'Curved Left Arrow',
|
|
633
|
+
47: 'Curved Up Arrow',
|
|
634
|
+
48: 'Curved Down Arrow',
|
|
635
|
+
49: 'Striped Right Arrow',
|
|
636
|
+
50: 'Notched Right Arrow',
|
|
637
|
+
51: 'Pentagon',
|
|
638
|
+
52: 'Chevron',
|
|
639
|
+
53: 'Right Arrow Callout',
|
|
640
|
+
54: 'Left Arrow Callout',
|
|
641
|
+
55: 'Up Arrow Callout',
|
|
642
|
+
56: 'Down Arrow Callout',
|
|
643
|
+
57: 'Left-Right Arrow Callout',
|
|
644
|
+
58: 'Up-Down Arrow Callout',
|
|
645
|
+
59: 'Quad Arrow Callout',
|
|
646
|
+
60: 'Circular Arrow',
|
|
647
|
+
61: 'Process',
|
|
648
|
+
62: 'Alternate Process',
|
|
649
|
+
63: 'Decision',
|
|
650
|
+
64: 'Data',
|
|
651
|
+
65: 'Predefined Process',
|
|
652
|
+
66: 'Internal Storage',
|
|
653
|
+
67: 'Document',
|
|
654
|
+
68: 'Multidocument',
|
|
655
|
+
69: 'Terminator',
|
|
656
|
+
70: 'Preparation',
|
|
657
|
+
71: 'Manual Input',
|
|
658
|
+
72: 'Manual Operation',
|
|
659
|
+
73: 'Connector',
|
|
660
|
+
74: 'Off-page Connector',
|
|
661
|
+
75: 'Card',
|
|
662
|
+
76: 'Punched Tape',
|
|
663
|
+
77: 'Summing Junction',
|
|
664
|
+
78: 'Or',
|
|
665
|
+
79: 'Collate',
|
|
666
|
+
80: 'Sort',
|
|
667
|
+
81: 'Extract',
|
|
668
|
+
82: 'Merge',
|
|
669
|
+
83: 'Stored Data',
|
|
670
|
+
84: 'Delay',
|
|
671
|
+
85: 'Sequential Access Storage',
|
|
672
|
+
86: 'Magnetic Disk',
|
|
673
|
+
87: 'Direct Access Storage',
|
|
674
|
+
88: 'Display',
|
|
675
|
+
92: 'Explosion 1',
|
|
676
|
+
93: 'Explosion 2',
|
|
677
|
+
94: '4-Point Star',
|
|
678
|
+
95: '5-Point Star',
|
|
679
|
+
96: '8-Point Star',
|
|
680
|
+
97: '16-Point Star',
|
|
681
|
+
98: '24-Point Star',
|
|
682
|
+
99: '32-Point Star',
|
|
683
|
+
100: 'Up Ribbon',
|
|
684
|
+
101: 'Down Ribbon',
|
|
685
|
+
102: 'Curved Up Ribbon',
|
|
686
|
+
103: 'Curved Down Ribbon',
|
|
687
|
+
104: 'Vertical Scroll',
|
|
688
|
+
105: 'Horizontal Scroll',
|
|
689
|
+
106: 'Wave',
|
|
690
|
+
107: 'Double Wave',
|
|
691
|
+
108: 'Rectangular Callout',
|
|
692
|
+
109: 'Rounded Rectangular Callout',
|
|
693
|
+
110: 'Oval Callout',
|
|
694
|
+
111: 'Cloud Callout',
|
|
695
|
+
112: 'Line Callout 1',
|
|
696
|
+
113: 'Line Callout 2',
|
|
697
|
+
114: 'Line Callout 3',
|
|
698
|
+
115: 'Line Callout 4',
|
|
699
|
+
116: 'Line Callout 1 (Accent Bar)',
|
|
700
|
+
117: 'Line Callout 2 (Accent Bar)',
|
|
701
|
+
118: 'Line Callout 3 (Accent Bar)',
|
|
702
|
+
119: 'Line Callout 4 (Accent Bar)',
|
|
703
|
+
120: 'Line Callout 1 (No Border)',
|
|
704
|
+
121: 'Line Callout 2 (No Border)',
|
|
705
|
+
122: 'Line Callout 3 (No Border)',
|
|
706
|
+
123: 'Line Callout 4 (No Border)',
|
|
707
|
+
124: 'Line Callout 1 (Border and Accent Bar)',
|
|
708
|
+
125: 'Line Callout 2 (Border and Accent Bar)',
|
|
709
|
+
126: 'Line Callout 3 (Border and Accent Bar)',
|
|
710
|
+
127: 'Line Callout 4 (Border and Accent Bar)',
|
|
711
|
+
128: 'Action Button: Custom',
|
|
712
|
+
129: 'Action Button: Back or Previous',
|
|
713
|
+
130: 'Action Button: Forward or Next',
|
|
714
|
+
131: 'Action Button: Beginning',
|
|
715
|
+
132: 'Action Button: End',
|
|
716
|
+
133: 'Action Button: Home',
|
|
717
|
+
134: 'Action Button: Information',
|
|
718
|
+
135: 'Action Button: Return',
|
|
719
|
+
136: 'Action Button: Movie',
|
|
720
|
+
137: 'Action Button: Document',
|
|
721
|
+
138: 'Action Button: Sound',
|
|
722
|
+
139: 'Action Button: Help',
|
|
723
|
+
140: 'Bevel',
|
|
724
|
+
156: 'Frame',
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def mso_shape_basename(shape) -> str:
|
|
729
|
+
"""Return python-pptx-compatible display basename for an MSO_SHAPE.
|
|
730
|
+
|
|
731
|
+
Used by ``Shapes.add_shape()`` to mirror python-pptx's default naming
|
|
732
|
+
scheme: ``f"{basename} {slide_shape_count_after_add}"``.
|
|
733
|
+
"""
|
|
734
|
+
if isinstance(shape, str):
|
|
735
|
+
# Backend string ('rectangle', 'oval', etc.) — title-case fallback.
|
|
736
|
+
return shape.replace('_', ' ').title()
|
|
737
|
+
try:
|
|
738
|
+
value = int(shape)
|
|
739
|
+
except (TypeError, ValueError):
|
|
740
|
+
return 'Shape'
|
|
741
|
+
if value in _MSO_SHAPE_BASENAME:
|
|
742
|
+
return _MSO_SHAPE_BASENAME[value]
|
|
743
|
+
name = getattr(shape, 'name', None)
|
|
744
|
+
if name:
|
|
745
|
+
return name.replace('_', ' ').title()
|
|
746
|
+
return 'Shape'
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
_MSO_CONNECTOR_BASENAME = {
|
|
750
|
+
1: 'Straight Connector',
|
|
751
|
+
2: 'Elbow Connector',
|
|
752
|
+
3: 'Curved Connector',
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
def mso_connector_basename(connector) -> str:
|
|
757
|
+
"""python-pptx names new connectors as ``f"{basename} {slide_count_after}"``.
|
|
758
|
+
|
|
759
|
+
Mirrors python-pptx — ``MSO_CONNECTOR.STRAIGHT`` → ``"Straight Connector"``.
|
|
760
|
+
"""
|
|
761
|
+
if isinstance(connector, str):
|
|
762
|
+
normalized = connector.lower()
|
|
763
|
+
if normalized in {'straight', 'line'}:
|
|
764
|
+
return 'Straight Connector'
|
|
765
|
+
if normalized == 'elbow':
|
|
766
|
+
return 'Elbow Connector'
|
|
767
|
+
if normalized == 'curve':
|
|
768
|
+
return 'Curved Connector'
|
|
769
|
+
return connector.replace('_', ' ').title()
|
|
770
|
+
try:
|
|
771
|
+
value = int(connector)
|
|
772
|
+
except (TypeError, ValueError):
|
|
773
|
+
return 'Connector'
|
|
774
|
+
return _MSO_CONNECTOR_BASENAME.get(value, 'Connector')
|
|
@@ -513,6 +513,16 @@ class Presentation:
|
|
|
513
513
|
from .slides import _build_slide_masters
|
|
514
514
|
return _build_slide_masters(self)
|
|
515
515
|
|
|
516
|
+
@property
|
|
517
|
+
def slide_master(self):
|
|
518
|
+
"""The first :class:`SlideMaster` (python-pptx parity).
|
|
519
|
+
|
|
520
|
+
Stock python-pptx exposes ``Presentation.slide_master`` as a shortcut
|
|
521
|
+
for ``slide_masters[0]``. Raises ``IndexError`` if the deck has no
|
|
522
|
+
masters in its snapshot.
|
|
523
|
+
"""
|
|
524
|
+
return self.slide_masters[0]
|
|
525
|
+
|
|
516
526
|
def reorder_slides(self, new_order: list[int]) -> None:
|
|
517
527
|
"""
|
|
518
528
|
Reorder slides in the presentation.
|
|
@@ -1230,12 +1240,13 @@ class Presentation:
|
|
|
1230
1240
|
snapshot=self._snapshot,
|
|
1231
1241
|
)
|
|
1232
1242
|
|
|
1233
|
-
def save(self,
|
|
1243
|
+
def save(self, file: Union[str, Path]) -> None:
|
|
1234
1244
|
"""
|
|
1235
1245
|
Export the presentation and download to a local file.
|
|
1236
1246
|
|
|
1237
1247
|
Args:
|
|
1238
|
-
|
|
1248
|
+
file: Path where the PPTX file should be saved.
|
|
1249
|
+
Matches python-pptx's ``Presentation.save(file)`` signature.
|
|
1239
1250
|
"""
|
|
1240
1251
|
# Flush any pending commands first
|
|
1241
1252
|
self._buffer.flush()
|
|
@@ -1244,7 +1255,7 @@ class Presentation:
|
|
|
1244
1255
|
pptx_bytes = self._client.export_and_download(self._deck_id)
|
|
1245
1256
|
|
|
1246
1257
|
# Write to file
|
|
1247
|
-
path = Path(
|
|
1258
|
+
path = Path(file)
|
|
1248
1259
|
path.write_bytes(pptx_bytes)
|
|
1249
1260
|
|
|
1250
1261
|
def save_to_bytes(self) -> bytes:
|