athena-python-pptx 0.18.0__tar.gz → 0.20.0__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.18.0 → athena_python_pptx-0.20.0}/AGENTS.md +35 -7
- athena_python_pptx-0.20.0/API_PARITY_REPORT.md +220 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/CHANGELOG.md +217 -0
- athena_python_pptx-0.20.0/PARITY_QUESTIONS.md +112 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/PKG-INFO +10 -1
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/README.md +9 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/docs/API_PARITY_EXCEPTIONS.md +284 -60
- athena_python_pptx-0.20.0/docs/sdk_usage_census.json +15382 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/__init__.py +1 -1
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/_unsupported.py +1 -5
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/batching.py +20 -1
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/client.py +42 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/commands.py +175 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/lang.py +204 -41
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/simpletypes.py +3 -3
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/presentation.py +31 -12
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/__init__.py +1718 -286
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/slides.py +33 -1
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/text/__init__.py +165 -16
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/typing.py +6 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pyproject.toml +1 -1
- athena_python_pptx-0.18.0/API_PARITY_REPORT.md +0 -165
- athena_python_pptx-0.18.0/PARITY_QUESTIONS.md +0 -304
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/.gitignore +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/CLAUDE.md +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/DEV-GUIDE.md +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/docs/athena-api.json +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/docs/athena-api.md +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/_athena_extension.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/_citations.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/_ptc.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/_references.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/action.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/axis.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/category.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/chart.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/data.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/datalabel.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/legend.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/marker.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/plot.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/point.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/series.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/chart/xlsx.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/decorators.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/dml/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/dml/chtfmt.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/dml/color.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/dml/effect.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/dml/fill.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/dml/line.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/docgen.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/action.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/chart.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/dml.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/shapes.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/enum/text.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/errors.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/exc.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/generated/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/generated/autoshape_types.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/kit.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/media.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/_mirror.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/_tbl.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/_txbody.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/ns.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/presentation.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/autoshape.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/connector.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/graphfrm.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/groupshape.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/picture.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/shapes/shared.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/table.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/text.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/oxml/xmlchemy.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/package.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/__init__.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/_base.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/chart.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/coreprops.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/embeddedpackage.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/image.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/media.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/presentation.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/parts/slide.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/autoshape.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/base.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/connector.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/freeform.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/graphfrm.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/group.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/picture.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/placeholder.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shapes/shapetree.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/shared.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/slide.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/spec.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/table.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/text/fonts.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/text/layout.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/text/text.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/types.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/units.py +0 -0
- {athena_python_pptx-0.18.0 → athena_python_pptx-0.20.0}/pptx/util.py +0 -0
|
@@ -23,7 +23,17 @@ 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
|
|
26
|
+
### Current status
|
|
27
|
+
|
|
28
|
+
[`API_PARITY_REPORT.md`](API_PARITY_REPORT.md) is generated
|
|
29
|
+
(`scripts/generate_parity_report.py`; `tests/test_parity_report_generated.py`
|
|
30
|
+
fails CI when it is stale): it joins the usage census of agent code
|
|
31
|
+
(`docs/sdk_usage_census.json`, built by `scripts/pull_agent_traces.py` and
|
|
32
|
+
`scripts/sdk_usage_census.py`) with the surface audit and python-pptx's
|
|
33
|
+
surface, and ranks the gaps by usage, used and broken first.
|
|
34
|
+
[`PARITY_QUESTIONS.md`](PARITY_QUESTIONS.md) holds the open questions in that
|
|
35
|
+
order. After an SDK change that moves a member, regenerate both; refresh the
|
|
36
|
+
census (a new trace window) with the steps in `scripts/sdk_usage_census.py`.
|
|
27
37
|
|
|
28
38
|
**Full public-class parity reached** against python-pptx 1.0.2 across
|
|
29
39
|
three dimensions:
|
|
@@ -59,11 +69,27 @@ A small number of REST-SDK-specific departures are documented in
|
|
|
59
69
|
- **`_BaseSeriesData.index`** raises `NotImplementedError` (raising
|
|
60
70
|
rather than silently returning 0).
|
|
61
71
|
- **`Package.open()`** raises `UnsupportedFeatureError`.
|
|
62
|
-
- **Axis-title text frames**
|
|
63
|
-
into one title string in the
|
|
64
|
-
`UserWarning` when that drops styling
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
- **Axis-title text frames** have one font: their paragraphs and line
|
|
73
|
+
breaks are python-pptx's, but runs join into one title string in the
|
|
74
|
+
first run's font, with a one-time `UserWarning` when that drops styling,
|
|
75
|
+
and there is no `add_paragraph()`.
|
|
76
|
+
- **Chart and axis titles in lines** are written and read as python-pptx's
|
|
77
|
+
(a paragraph per `\n`, a line break per `\v`); a run's own text with
|
|
78
|
+
either character becomes a break in the deck (`docs/API_PARITY_EXCEPTIONS.md`).
|
|
79
|
+
- **Table-cell text frames** hold python-pptx's paragraphs and runs (sent as
|
|
80
|
+
`SetTableCell.paragraphs` once a cell holds more than one run); an empty
|
|
81
|
+
cell's paragraph still lists one empty run, a one-run cell's `paragraph.font`
|
|
82
|
+
is its run's font, and every underline style reads back as `True`
|
|
83
|
+
(`docs/API_PARITY_EXCEPTIONS.md`).
|
|
84
|
+
|
|
85
|
+
### Approved Athena extensions
|
|
86
|
+
|
|
87
|
+
Members python-pptx lacks that were added with explicit approval, each
|
|
88
|
+
documented in [`docs/API_PARITY_EXCEPTIONS.md`](docs/API_PARITY_EXCEPTIONS.md)
|
|
89
|
+
and listed in `tests/test_python_pptx_api_parity.py`'s allowlist:
|
|
90
|
+
`Shapes.add_linked_ole_object()`, `Shapes.add_linked_table()` and
|
|
91
|
+
`Shapes.add_linked_chart()` (approved 2026-10-20), the studio-linking
|
|
92
|
+
methods that bind an element to an Athena source.
|
|
67
93
|
|
|
68
94
|
### Intentionally omitted (REST SDK limitations)
|
|
69
95
|
|
|
@@ -73,7 +99,9 @@ local XML / OPC parts:
|
|
|
73
99
|
shape's element, for python-pptx's shape-tree recipes; see Architecture.)
|
|
74
100
|
- `TextFrame.part`, `Paragraph.part`, `Run.part` — package part access.
|
|
75
101
|
- `FillFormat.from_fill_parent()`, `ColorFormat.from_colorchoice_parent()` — internal XML constructors.
|
|
76
|
-
|
|
102
|
+
|
|
103
|
+
`Font.fill` exists: `solid()` and `fore_color` drive `font.color`; gradient,
|
|
104
|
+
pattern and no-fill text raise `UnsupportedFeatureError`.
|
|
77
105
|
|
|
78
106
|
`Font.language_id` IS now implemented (returns `MSO_LANGUAGE_ID` enum, accepts enum or int LCID) — added in v0.1.68.
|
|
79
107
|
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# API parity report: athena-python-pptx and python-pptx 1.0.2
|
|
2
|
+
|
|
3
|
+
<!-- Generated by scripts/generate_parity_report.py; do not edit by hand. tests/test_parity_report_generated.py fails CI when it is stale. -->
|
|
4
|
+
|
|
5
|
+
Which python-pptx members agent code calls, what the SDK does with each, and the gaps
|
|
6
|
+
ranked by that usage: used and broken first. Three inputs are joined per member:
|
|
7
|
+
|
|
8
|
+
1. **What agents call** — `docs/sdk_usage_census.json`, built by `scripts/sdk_usage_census.py`
|
|
9
|
+
from the Python agents sent to Athena's code tools in production traces: static analysis
|
|
10
|
+
(the code is parsed, never run) types each expression against the SDK's classes and records
|
|
11
|
+
the member it reaches.
|
|
12
|
+
2. **What the SDK does** — the surface audit (`tests/_surface_audit.py`) probes every public
|
|
13
|
+
setter and method on a mocked deck: it sends a command, warns once, raises, or sends nothing;
|
|
14
|
+
`pptx/_unsupported.py` lists members the SDK refuses.
|
|
15
|
+
3. **What python-pptx has** — python-pptx 1.0.2's public members (the parity tests'
|
|
16
|
+
reference).
|
|
17
|
+
|
|
18
|
+
Counts are customer **workspaces | sessions | tool calls** in the window; Athena staff traffic is
|
|
19
|
+
counted apart and excluded. A count from fewer than three workspaces reads `<3`.
|
|
20
|
+
|
|
21
|
+
## Census
|
|
22
|
+
|
|
23
|
+
- Source: LangSmith agora-production traces; the 60 days from 2026-08-16 to 2026-10-14 (55 of them with SDK code).
|
|
24
|
+
- Customers: 6335 tool calls with SDK code, 290 sessions, 10 workspaces; computer_asset_exec <3; eval 109; execute <3; execute_presentation_code 5992; run_python_code 121; write_file 26.
|
|
25
|
+
- Athena staff: 1335 tool calls, 101 sessions, 5 workspaces. Left out of both: the calls with neither a signed-in user nor a workspace (no workspace is behind them, so their count is withheld).
|
|
26
|
+
- Members used by customers: 743. How the analysis typed them: downcast 17, heuristic 7, static 660, unknown 59 (`unknown`: no SDK class defines the member).
|
|
27
|
+
|
|
28
|
+
## Used and broken, ranked by usage
|
|
29
|
+
|
|
30
|
+
Members customers' agents called whose call the SDK does not carry out as python-pptx does today.
|
|
31
|
+
Ranked by customer workspaces, then sessions.
|
|
32
|
+
|
|
33
|
+
| # | python-pptx member | SDK class | op | Workspaces | Sessions | Tool calls | What the SDK does |
|
|
34
|
+
|---|---|---|---|---|---|---|---|
|
|
35
|
+
| 1 | `Shapes._spTree` | `Shapes` | get | <3 | <3 | <3 | raises: in 4 of 8 probed contexts; athena-python-pptx mirrors a slide's own shape tree, slide.shapes._spTree; a notes slide's, layout's or master's is not mirrored. |
|
|
36
|
+
| 2 | `Chart._chartSpace` | `Chart` | get | <3 | <3 | <3 | raises: python-pptx XML internal: Use the Chart API: chart.plots, chart.series, chart.replace_data(chart_data), chart.has_title / chart.chart_title, chart.value_axis, chart.category_axis. |
|
|
37
|
+
| 3 | `Slide._element` | `Slide` | get | <3 | <3 | <3 | raises: python-pptx XML internal: Use the object's own properties and methods. |
|
|
38
|
+
| 4 | `Subshape.part` | `Paragraph` | get | <3 | <3 | <3 | raises: athena-python-pptx edits a deck that lives on the PPTX Studio server, so there are no OPC package parts or relationships to reach. |
|
|
39
|
+
| 5 | `_ChartSeries._element` | `_ChartSeries` | get | <3 | <3 | <3 | raises: python-pptx XML internal: Use the object's own properties and methods. |
|
|
40
|
+
| 6 | `ColorFormat.brightness` | `_FillColorFormat` | set | <3 | <3 | <3 | raises: Shape fills carry no brightness in the Athena studio (SetShapeStyle has no lumMod / lumOff). |
|
|
41
|
+
|
|
42
|
+
## Known structural gaps, measured
|
|
43
|
+
|
|
44
|
+
The gaps the fidelity plan names (`../docs/plans/2026-10-14-powerpoint-fidelity-program.md` §5.3),
|
|
45
|
+
with the members that reach each. A gap no customer reached in the window is not prioritized.
|
|
46
|
+
|
|
47
|
+
| Gap | Members | Workspaces | Sessions | What the SDK does |
|
|
48
|
+
|---|---|---|---|---|
|
|
49
|
+
| A table cell holds several paragraphs and runs | `_TableCellParagraph.add_run`, `_TableCellTextFrame.add_paragraph` | 3 | 23 | Closed: add_run() / add_paragraph() add a run / paragraph, each run keeps its own font, and the cell's text body is sent run by run (SetTableCell.paragraphs) |
|
|
50
|
+
| Table-cell paragraph spacing and level | `_TableCellParagraph.line_spacing`, `_TableCellParagraph.space_before`, `_TableCellParagraph.space_after`, `_TableCellParagraph.level` | <3 | <3 | Closed: sent with the cell's paragraphs (SetTableCell.paragraphs) |
|
|
51
|
+
| Table-cell word wrap and auto-size | `_TableCellTextFrame.word_wrap`, `_TableCellTextFrame.auto_size` | 3 | 19 | word_wrap = True, the value every measured use sets, is how a cell lays out its text and is accepted; False, None and auto_size warn once and keep the value locally |
|
|
52
|
+
| Table-cell colours are sRGB only; no table-cell hyperlink writes (no customer use) | `_TableCellFontColorFormat.theme_color`, `_TableCellFillColorFormat.theme_color`, `_TableCellHyperlink.address` | 0 | 0 | raise UnsupportedFeatureError |
|
|
53
|
+
| 3-D, stock and surface charts (no customer use) | `XL_CHART_TYPE.THREE_D_*`, `XL_CHART_TYPE.STOCK_*`, `XL_CHART_TYPE.SURFACE*` | 0 | 0 | add_chart() raises UnsupportedFeatureError |
|
|
54
|
+
| Gradient and pattern text (Font.fill) (no customer use) | `_FontFillFormat.gradient`, `_FontFillFormat.patterned` | 0 | 0 | raise UnsupportedFeatureError |
|
|
55
|
+
| Notes-slide shapes and placeholders (no customer use) | `NotesSlide.shapes`, `NotesSlide.placeholders`, `NotesSlide.notes_placeholder` | 0 | 0 | empty collections; notes text works through notes_slide.notes_text_frame |
|
|
56
|
+
| Layout and master backgrounds are read-only (no customer use) | `_LayoutBackgroundFill.solid`, `_LayoutBackgroundColor.rgb` | 0 | 0 | raise UnsupportedFeatureError |
|
|
57
|
+
|
|
58
|
+
Workspaces and sessions are the largest count of any one member of the gap (a lower bound on the
|
|
59
|
+
gap's reach).
|
|
60
|
+
|
|
61
|
+
## Members agents call that python-pptx does not have
|
|
62
|
+
|
|
63
|
+
Not parity gaps: python-pptx raises `AttributeError` for these too. They show where agents guess
|
|
64
|
+
at an API, which the skill text and error messages can steer.
|
|
65
|
+
|
|
66
|
+
| Member | op | Workspaces | Sessions | Tool calls |
|
|
67
|
+
|---|---|---|---|---|
|
|
68
|
+
| `Slides.move` | call | <3 | <3 | <3 |
|
|
69
|
+
| `_TableCellTextFrame.replace_text` | call | <3 | <3 | <3 |
|
|
70
|
+
| `Client.timeout` | get | <3 | <3 | <3 |
|
|
71
|
+
| `DeckSnapshot.get` | call | <3 | <3 | <3 |
|
|
72
|
+
| `Shapes.remove` | call | <3 | <3 | <3 |
|
|
73
|
+
| `ActionSetting.tooltip` | set | <3 | <3 | <3 |
|
|
74
|
+
| `CategoryAxis.major_unit` | set | <3 | <3 | <3 |
|
|
75
|
+
| `DataLabel._dLbl` | get | <3 | <3 | <3 |
|
|
76
|
+
| `DataLabel.number_format_is_linked` | set | <3 | <3 | <3 |
|
|
77
|
+
| `LineFormat._get_or_add_ln` | call | <3 | <3 | <3 |
|
|
78
|
+
| `Picture.insert_picture` | get | <3 | <3 | <3 |
|
|
79
|
+
| `Presentation._buffer` | get | <3 | <3 | <3 |
|
|
80
|
+
| `Presentation.move_slide` | call | <3 | <3 | <3 |
|
|
81
|
+
| `Region.split` | call | <3 | <3 | <3 |
|
|
82
|
+
| `Run._parent` | get | <3 | <3 | <3 |
|
|
83
|
+
| `Shape._placeholder` | get | <3 | <3 | <3 |
|
|
84
|
+
| `Shape._properties` | get | <3 | <3 | <3 |
|
|
85
|
+
| `Shape._shape_id` | get | <3 | <3 | <3 |
|
|
86
|
+
| `Shape.id` | get | <3 | <3 | <3 |
|
|
87
|
+
| `Shape.margin_bottom` | set | <3 | <3 | <3 |
|
|
88
|
+
| `Shape.margin_left` | set | <3 | <3 | <3 |
|
|
89
|
+
| `Shape.margin_right` | set | <3 | <3 | <3 |
|
|
90
|
+
| `Shape.margin_top` | set | <3 | <3 | <3 |
|
|
91
|
+
| `Shape.vertical_anchor` | set | <3 | <3 | <3 |
|
|
92
|
+
| `Shapes.clear_shapes` | call | <3 | <3 | <3 |
|
|
93
|
+
| … 15 more | | | | |
|
|
94
|
+
|
|
95
|
+
Agent code also reached names neither the SDK nor python-pptx has (attributes it hung on SDK
|
|
96
|
+
objects, made-up enum members) on `Client`, `MSO_SHAPE`, `Paragraph`, `Shape`, `SlideLayout`, `Slides`, `_ChartSeries`. The census
|
|
97
|
+
records them only as `?`: a name the code invents may say anything.
|
|
98
|
+
|
|
99
|
+
## Most-used members (top 60)
|
|
100
|
+
|
|
101
|
+
| # | python-pptx member | SDK class | op | Workspaces | Sessions | Tool calls | Status |
|
|
102
|
+
|---|---|---|---|---|---|---|---|
|
|
103
|
+
| 1 | `Slide.shapes` | `Slide` | get | 10 | 256 | 4644 | works |
|
|
104
|
+
| 2 | `Presentation.slides` | `Presentation` | get | 10 | 255 | 5245 | works |
|
|
105
|
+
| 3 | `Presentation()` | `Presentation` | new | 10 | 244 | 4960 | works |
|
|
106
|
+
| 4 | `Presentation.slide_layouts` | `Presentation` | get | 10 | 185 | 881 | works |
|
|
107
|
+
| 5 | `Shape.text_frame` | `Shape` | get | 9 | 247 | 3053 | works |
|
|
108
|
+
| 6 | `TextFrame.paragraphs` | `TextFrame` | get | 9 | 227 | 2220 | works |
|
|
109
|
+
| 7 | `Pt()` | `Pt` | new | 9 | 224 | 2237 | works |
|
|
110
|
+
| 8 | `Font.color` | `Font` | get | 9 | 216 | 1733 | works |
|
|
111
|
+
| 9 | `_Run.font` | `Run` | get | 9 | 215 | 1954 | works |
|
|
112
|
+
| 10 | `Font.size` | `Font` | set | 9 | 213 | 1897 | works |
|
|
113
|
+
| 11 | `ColorFormat.rgb` | `_FontColorFormat` | set | 9 | 213 | 1636 | works |
|
|
114
|
+
| 12 | `Inches()` | `Inches` | new | 9 | 212 | 2179 | works |
|
|
115
|
+
| 13 | `Font.bold` | `Font` | set | 9 | 211 | 1594 | works |
|
|
116
|
+
| 14 | `RGBColor()` | `RGBColor` | new | 9 | 210 | 2071 | works |
|
|
117
|
+
| 15 | `TextFrame.word_wrap` | `TextFrame` | set | 9 | 205 | 1427 | works |
|
|
118
|
+
| 16 | `_BaseGroupShapes.add_textbox` | `Shapes` | call | 9 | 201 | 1263 | works |
|
|
119
|
+
| 17 | `_Paragraph.alignment` | `Paragraph` | set | 9 | 179 | 1057 | works |
|
|
120
|
+
| 18 | `TextFrame.add_paragraph` | `TextFrame` | call | 9 | 179 | 940 | works |
|
|
121
|
+
| 19 | `_BaseSlide.name` | `SlideLayout` | get | 9 | 172 | 464 | works |
|
|
122
|
+
| 20 | `_Run.text` | `Run` | set | 9 | 169 | 1047 | works |
|
|
123
|
+
| 21 | `BaseShape.shape_type` | `Shape` | get | 9 | 168 | 433 | works |
|
|
124
|
+
| 22 | `Presentation.batch` | `Presentation` | call | 9 | 159 | 2190 | works (Athena extension) |
|
|
125
|
+
| 23 | `TextFrame.margin_left` | `TextFrame` | set | 9 | 153 | 800 | works |
|
|
126
|
+
| 24 | `_Paragraph.add_run` | `Paragraph` | call | 9 | 151 | 805 | works |
|
|
127
|
+
| 25 | `TextFrame.margin_right` | `TextFrame` | set | 9 | 149 | 709 | works |
|
|
128
|
+
| 26 | `Presentation.slide_width` | `Presentation` | get | 9 | 148 | 401 | works |
|
|
129
|
+
| 27 | `TextFrame.margin_top` | `TextFrame` | set | 9 | 147 | 691 | works |
|
|
130
|
+
| 28 | `Presentation.slide_height` | `Presentation` | get | 9 | 142 | 316 | works |
|
|
131
|
+
| 29 | `Font.name` | `Font` | set | 9 | 132 | 816 | works |
|
|
132
|
+
| 30 | `TextFrame.margin_bottom` | `TextFrame` | set | 9 | 131 | 607 | works |
|
|
133
|
+
| 31 | `FillFormat.fore_color` | `FillFormat` | get | 8 | 201 | 1373 | works |
|
|
134
|
+
| 32 | `Shape.fill` | `Shape` | get | 8 | 200 | 1263 | works |
|
|
135
|
+
| 33 | `FillFormat.solid` | `FillFormat` | call | 8 | 199 | 1302 | works |
|
|
136
|
+
| 34 | `ColorFormat.rgb` | `_FillColorFormat` | set | 8 | 199 | 1301 | works |
|
|
137
|
+
| 35 | `Shape.line` | `Shape` | get | 8 | 194 | 1228 | works |
|
|
138
|
+
| 36 | `_BaseGroupShapes.add_shape` | `Shapes` | call | 8 | 189 | 1160 | works |
|
|
139
|
+
| 37 | `BaseShape.name` | `Shape` | get | 8 | 186 | 1454 | works |
|
|
140
|
+
| 38 | `FillFormat.background` | `LineFillFormat` | call | 8 | 181 | 1031 | works |
|
|
141
|
+
| 39 | `LineFormat.fill` | `LineFormat` | get | 8 | 181 | 1033 | works |
|
|
142
|
+
| 40 | `PP_PARAGRAPH_ALIGNMENT.CENTER` | `PP_PARAGRAPH_ALIGNMENT` | enum | 8 | 162 | 856 | works |
|
|
143
|
+
| 41 | `LineFormat.color` | `LineFormat` | get | 8 | 157 | 822 | works |
|
|
144
|
+
| 42 | `ColorFormat.rgb` | `_LineColorFormat` | set | 8 | 155 | 809 | works |
|
|
145
|
+
| 43 | `LineFormat.width` | `LineFormat` | set | 8 | 150 | 752 | works |
|
|
146
|
+
| 44 | `Font.italic` | `Font` | set | 8 | 138 | 808 | works |
|
|
147
|
+
| 45 | `Slides.add_slide` | `Slides` | call | 8 | 134 | 718 | works |
|
|
148
|
+
| 46 | `_Paragraph.space_after` | `Paragraph` | set | 8 | 115 | 448 | works |
|
|
149
|
+
| 47 | `PP_PARAGRAPH_ALIGNMENT.RIGHT` | `PP_PARAGRAPH_ALIGNMENT` | enum | 8 | 114 | 493 | works |
|
|
150
|
+
| 48 | `Slide.validate` | `Slide` | call | 8 | 114 | 999 | works (Athena extension) |
|
|
151
|
+
| 49 | `Presentation.flush` | `Presentation` | call | 8 | 106 | 1176 | works (Athena extension) |
|
|
152
|
+
| 50 | `Presentation.slide_size_inches` | `Presentation` | get | 8 | 75 | 151 | works (Athena extension) |
|
|
153
|
+
| 51 | `_Paragraph.runs` | `Paragraph` | get | 7 | 176 | 1601 | works |
|
|
154
|
+
| 52 | `MSO_VERTICAL_ANCHOR.MIDDLE` | `MSO_ANCHOR` | enum | 7 | 170 | 971 | works |
|
|
155
|
+
| 53 | `TextFrame.vertical_anchor` | `TextFrame` | set | 7 | 164 | 868 | works |
|
|
156
|
+
| 54 | `MSO_AUTO_SHAPE_TYPE.RECTANGLE` | `MSO_SHAPE` | enum | 7 | 136 | 704 | works |
|
|
157
|
+
| 55 | `PP_PARAGRAPH_ALIGNMENT.LEFT` | `PP_PARAGRAPH_ALIGNMENT` | enum | 7 | 122 | 490 | works |
|
|
158
|
+
| 56 | `BaseShape.top` | `Shape` | set | 7 | 118 | 408 | works |
|
|
159
|
+
| 57 | `Table.cell` | `Table` | call | 7 | 112 | 738 | works |
|
|
160
|
+
| 58 | `_Cell.text_frame` | `TableCell` | get | 7 | 109 | 650 | works |
|
|
161
|
+
| 59 | `Table.columns` | `Table` | get | 7 | 108 | 509 | works |
|
|
162
|
+
| 60 | `TextFrame.paragraphs` | `_TableCellTextFrame` | get | 7 | 108 | 636 | works |
|
|
163
|
+
|
|
164
|
+
## Errors in customers' tool results
|
|
165
|
+
|
|
166
|
+
API-name-only signals from the tool results: the exception class, a missing attribute of an SDK
|
|
167
|
+
class, the feature tag of the SDK's unsupported errors. **Now** checks the SDK as it is today.
|
|
168
|
+
|
|
169
|
+
| Signal | Workspaces | Tool calls | Now |
|
|
170
|
+
|---|---|---|---|
|
|
171
|
+
| `UnsupportedFeatureError` | 8 | 70 | |
|
|
172
|
+
| `UnsupportedFeatureError:[Shape._element]` | 6 | 28 | fixed (the member exists now) |
|
|
173
|
+
| `UnsupportedFeatureError:[cell font.name]` | 6 | 34 | no longer raised |
|
|
174
|
+
| `ValueError` | 6 | 66 | |
|
|
175
|
+
| `AttributeError` | 5 | 83 | |
|
|
176
|
+
| `RemoteError` | 5 | 47 | |
|
|
177
|
+
| `TypeError` | 5 | 35 | |
|
|
178
|
+
| `BatchPartialError` | 4 | 13 | |
|
|
179
|
+
| `ConnectionError` | 4 | 39 | |
|
|
180
|
+
| `FlushAllError` | 4 | 19 | |
|
|
181
|
+
| `MaxRetryError` | 4 | 39 | |
|
|
182
|
+
| `NameError` | 4 | 21 | |
|
|
183
|
+
| `ReadTimeoutError` | 4 | 39 | |
|
|
184
|
+
| `TimeoutError` | 4 | 39 | |
|
|
185
|
+
| `AttributeError:Slides._sldIdLst` | 3 | 10 | fixed (the member exists now) |
|
|
186
|
+
| `FileNotFoundError` | 3 | 4 | |
|
|
187
|
+
| `ImportError` | 3 | 16 | |
|
|
188
|
+
| `IndexError` | 3 | 9 | |
|
|
189
|
+
| `KeyError` | 3 | 5 | |
|
|
190
|
+
| `ModuleNotFoundError` | 3 | 6 | |
|
|
191
|
+
| `UnsupportedFeatureError:[cell font.italic]` | 3 | 7 | no longer raised |
|
|
192
|
+
| `AttributeError:AxisTitleTextFrame.paragraphs` | <3 | <3 | fixed (the member exists now) |
|
|
193
|
+
| `AttributeError:BackgroundFill.type` | <3 | <3 | fixed (the member exists now) |
|
|
194
|
+
| `AttributeError:Run._r` | <3 | <3 | fixed (the member exists now) |
|
|
195
|
+
| `AttributeError:Shapes._spTree` | <3 | <3 | fixed (the member exists now) |
|
|
196
|
+
|
|
197
|
+
## Class-level parity with python-pptx 1.0.2
|
|
198
|
+
|
|
199
|
+
- 150 python-pptx classes are compared member by member (`tests/test_python_pptx_api_parity.py`); their public members missing from the SDK: 432 allowlisted (XML elements, OPC parts, chart-workbook builders, listed in `docs/API_PARITY_EXCEPTIONS.md`), 0 unexpected.
|
|
200
|
+
- Writes the SDK refuses or ignores, member by member: the generated `unsupported-writes` table in
|
|
201
|
+
`docs/API_PARITY_EXCEPTIONS.md`.
|
|
202
|
+
|
|
203
|
+
## Method and privacy
|
|
204
|
+
|
|
205
|
+
- `scripts/pull_agent_traces.py` reads the code-tool runs of the production LangSmith project
|
|
206
|
+
(read-only) into a local directory; `scripts/sdk_usage_census.py extract` turns each run's code
|
|
207
|
+
into events and `aggregate` counts them. Raw traces and event files never leave the operator's
|
|
208
|
+
machine.
|
|
209
|
+
- An event is a class, member and parameter name, an enum member name, or `True` / `False` / `None`
|
|
210
|
+
assigned to a property: no literal, no string, no name the agent's code defines. Workspace and
|
|
211
|
+
session ids are hashed with a per-run salt and dropped after counting.
|
|
212
|
+
- Population: a call is Athena staff when its signed-in user is staff, or when it ran without a
|
|
213
|
+
signed-in user (an automation) in a workspace where staff made most signed-in calls.
|
|
214
|
+
- Typing: each expression is typed from the SDK's annotations (and, where a member has none, from
|
|
215
|
+
the classes its `return` statements build). A member only a subclass defines is credited to that
|
|
216
|
+
subclass (`downcast`); a name the code never assigns is typed by agents' naming convention
|
|
217
|
+
(`prs`, `slide`, `tf`, `cell`: the executor keeps globals between calls) (`heuristic`).
|
|
218
|
+
- Limits: code that reaches the SDK through objects the analysis cannot type (dict lookups, helper
|
|
219
|
+
classes) is not counted, so counts are lower bounds; customer VPC environments keep their own
|
|
220
|
+
traces and are not in this census.
|
|
@@ -2,6 +2,223 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `athena-python-pptx` are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.20.0 — an older deck opens by its deck id
|
|
6
|
+
|
|
7
|
+
- `Presentation(asset_id=...)` asks pptx-studio once, at open, for the id it
|
|
8
|
+
names the deck by (`GET /decks/by-asset/<asset id>/deck-id`, #46493). An older deck's
|
|
9
|
+
room, and every `/decks/...` route, name it by its deck id rather than its
|
|
10
|
+
asset id (#46319). Every snapshot, command, export and render then uses that
|
|
11
|
+
id, so code run against an older deck edits the deck instead of an empty
|
|
12
|
+
room.
|
|
13
|
+
- `prs.asset_id` (and its `deck_id` alias) stays the id you passed, and tool
|
|
14
|
+
cards still name the asset.
|
|
15
|
+
- Only a successful answer for this asset is used. Any other outcome keeps the
|
|
16
|
+
asset id, as every open did before, with one `pptx.client` log record: a
|
|
17
|
+
studio without the route, a refusal, an error or a timeout.
|
|
18
|
+
- `copy_slide_from(source)` names an older source deck by its deck id too.
|
|
19
|
+
- Needs a pptx-studio with `GET /decks/by-asset/:assetId/deck-id` (#46590) and agora's
|
|
20
|
+
lookup (#46588); against an older studio, nothing changes.
|
|
21
|
+
|
|
22
|
+
## 0.20.0 — slides hide from the slide show, as PowerPoint's Hide Slide does
|
|
23
|
+
|
|
24
|
+
- `slide.hidden = True` hides a slide from the slide show and keeps it in the
|
|
25
|
+
deck; the saved deck writes `<p:sld show="0">`. `slide.hidden = False` shows
|
|
26
|
+
it again. `slide.hidden` reads an uploaded slide's flag. Not in python-pptx:
|
|
27
|
+
`docs/API_PARITY_EXCEPTIONS.md`, "`Slide.hidden`".
|
|
28
|
+
- Needs a studio with `SetSlideHidden`; an older one rejects the command.
|
|
29
|
+
|
|
30
|
+
## 0.19.0 — `Shapes.add_linked_chart()`: a chart whose data is a source range
|
|
31
|
+
|
|
32
|
+
- `slide.shapes.add_linked_chart(chart_type, source, anchor, left=, top=,
|
|
33
|
+
width=, height=, series_in='columns', freshness_mode='athena_refreshable',
|
|
34
|
+
version_policy=None, title=None, name=None)` sends `AddLinkedChart`. The
|
|
35
|
+
studio reads the range and writes the chart's categories and series from
|
|
36
|
+
it, and the chart refreshes from the range as a linked table does.
|
|
37
|
+
- An Athena studio-linking extension with no python-pptx counterpart, beside
|
|
38
|
+
`add_linked_table()` and `add_linked_ole_object()`, added with explicit
|
|
39
|
+
approval (2026-10-20). See `docs/API_PARITY_EXCEPTIONS.md`.
|
|
40
|
+
- Column, bar, line, area, pie and doughnut chart types; a scatter, bubble,
|
|
41
|
+
radar or Office 2016 type raises `UnsupportedFeatureError`.
|
|
42
|
+
- Needs a studio with `AddLinkedChart` in its command schema (pptx-studio
|
|
43
|
+
since the linked-chart refresh work, #45113).
|
|
44
|
+
|
|
45
|
+
## 0.19.0 — a text write keeps what someone typed after the SDK read the box
|
|
46
|
+
|
|
47
|
+
- A write of a box's text (`text_frame.text`, `paragraph.text`,
|
|
48
|
+
`text_frame.clear()`, `add_paragraph()`, a hyperlink change, …) sends
|
|
49
|
+
`SetText` with `baseText`: the frame's text as the SDK read it from the
|
|
50
|
+
snapshot, or last wrote it (python-pptx's `text_frame.text`, a line break
|
|
51
|
+
as `\v`). A run's (`run.text`, `run.upper()`, …) sends `SetRunText` with the
|
|
52
|
+
run's. When the box holds other text, someone changed it between the read
|
|
53
|
+
and the write (an executor run sends its writes when the script ends), and
|
|
54
|
+
the studio merges three ways: the SDK's change goes onto the box's current
|
|
55
|
+
text, so their typing is kept, and a word they deleted stays deleted. Where
|
|
56
|
+
both changed the same words, both versions are kept, theirs first (#44914,
|
|
57
|
+
PR 8).
|
|
58
|
+
- No public API changes, and python-pptx code reads and writes as before:
|
|
59
|
+
`text_frame.text` reads what the SDK wrote until the deck is read again.
|
|
60
|
+
- After `text_frame.replace_text()` (the studio replaces in the text it finds)
|
|
61
|
+
the frame's next write sends no base, until the frame is read again or
|
|
62
|
+
written whole.
|
|
63
|
+
- Needs a studio that merges (pptx-studio with `baseText` in its command
|
|
64
|
+
schema). An older studio drops the field, as it drops any key it does not
|
|
65
|
+
declare, and writes the text over the box as before.
|
|
66
|
+
|
|
67
|
+
## 0.19.0 — chart and axis titles in lines read and write as python-pptx's
|
|
68
|
+
|
|
69
|
+
- `chart.chart_title.text_frame` and `axis.axis_title.text_frame` hold
|
|
70
|
+
python-pptx's paragraphs and line breaks. `text_frame.text = "a\nb\vc"` is a
|
|
71
|
+
paragraph per `\n`-separated line and a line break for each `\v`, and the
|
|
72
|
+
saved deck gets them so (it got one run whose text held the line feeds);
|
|
73
|
+
`paragraph.text = "a\nb"` makes both line breaks. `text_frame.text` reads
|
|
74
|
+
`\n` between paragraphs and `\v` for a line break, `paragraphs` one per
|
|
75
|
+
line (an axis title had one), and a paragraph's `runs` leave its line breaks
|
|
76
|
+
out. An uploaded deck's titles read the same way, where a line break
|
|
77
|
+
(`a:br`) read as `\n`.
|
|
78
|
+
- A chart title's runs styled across its lines (`paragraphs[i].runs[j].font`,
|
|
79
|
+
a paragraph from `add_paragraph()`) reach the deck in their fonts, each in
|
|
80
|
+
its paragraph: `SetChartTitleRich` carries each paragraph break as a run of
|
|
81
|
+
`"\n"` and each line break as a run of `"\v"`. They were sent as one
|
|
82
|
+
paragraph, the paragraphs run together.
|
|
83
|
+
- Departures (a run's own text with a line feed, the font kept on replaced
|
|
84
|
+
text, trimmed chart titles, fields read as runs):
|
|
85
|
+
`docs/API_PARITY_EXCEPTIONS.md`, "Chart and axis titles in lines".
|
|
86
|
+
- Needs a studio that writes titles in lines; an older one writes each
|
|
87
|
+
paragraph break as a line feed inside a run, and a line break as a vertical
|
|
88
|
+
tab the deck's XML cannot hold.
|
|
89
|
+
|
|
90
|
+
## 0.19.0 — a chart point's own line reads and writes as python-pptx's
|
|
91
|
+
|
|
92
|
+
- `series.points[i].format.line` is the point's own line
|
|
93
|
+
(`c:dPt/c:spPr/a:ln`): the outline of its bar, slice or bubble, or on a
|
|
94
|
+
line or XY series the segment ending at it. `color.rgb`, `width`,
|
|
95
|
+
`dash_style`, `fill.background()` and `fill.solid()` emit a `SetPointLine`
|
|
96
|
+
chart patch for that point (the series' line is left alone), which the deck
|
|
97
|
+
gets as python-pptx writes it; they warned that they were ignored and sent
|
|
98
|
+
nothing. Reads return the point's own line as the uploaded file has it (an
|
|
99
|
+
uploaded slice's white separator reads `MSO_FILL.SOLID`, `FFFFFF`, its
|
|
100
|
+
width; a point repeating its series' line reads that copy), and `width`
|
|
101
|
+
reads `0` for a point without one, as python-pptx's does. `color.theme_color` still warns. Departures:
|
|
102
|
+
`docs/API_PARITY_EXCEPTIONS.md`.
|
|
103
|
+
- Needs a studio with the `SetPointLine` op; an older one rejects the batch
|
|
104
|
+
that carries it.
|
|
105
|
+
|
|
106
|
+
## 0.19.0 — converting an uploaded file keeps its id
|
|
107
|
+
|
|
108
|
+
- Opening an uploaded `.pptx`/`.potx` that was never converted says to convert
|
|
109
|
+
it with `convert_powerpoint_to_athena_presentation` and then open the same id:
|
|
110
|
+
the upload itself becomes the presentation. It said the conversion creates a
|
|
111
|
+
NEW presentation with its own id, which Athena's conversion no longer does.
|
|
112
|
+
|
|
113
|
+
## 0.19.0 — a chart's date axis reads as python-pptx's `DateAxis`
|
|
114
|
+
|
|
115
|
+
- `chart.category_axis` on an uploaded chart whose category axis is a
|
|
116
|
+
`c:dateAx` (and that has no `c:catAx`) is a `DateAxis`, as in python-pptx
|
|
117
|
+
1.0.2, its `category_type` `XL_CATEGORY_TYPE.TIME_SCALE`. It was a
|
|
118
|
+
`CategoryAxis` that read nothing of the axis (the studio did not read date
|
|
119
|
+
axes): its number format, its link to the source, its title, gridlines,
|
|
120
|
+
reversal and visibility now read back as the deck has them. Its edits reach
|
|
121
|
+
the date axis, as before.
|
|
122
|
+
- `CategoryAxis.category_type` reads `XL_CATEGORY_TYPE.CATEGORY_SCALE`, as
|
|
123
|
+
python-pptx's does; it read `None`. After the SDK's own setter it reads the
|
|
124
|
+
type written.
|
|
125
|
+
- Needs a studio that reads date axes (`AxisSpec.dateAxis`); with an older one
|
|
126
|
+
`category_axis` stays a `CategoryAxis`.
|
|
127
|
+
|
|
128
|
+
## 0.19.0 — table cells hold paragraphs and runs
|
|
129
|
+
|
|
130
|
+
- A table cell's text frame holds python-pptx's paragraphs and runs.
|
|
131
|
+
`cell.text_frame.add_paragraph()` and `paragraph.add_run()` add a paragraph or
|
|
132
|
+
a run, where they handed back the cell's single paragraph or run with a
|
|
133
|
+
warning: a "**Label:** value" cell built from two runs kept only the last
|
|
134
|
+
run's text, in its formatting, and a cell built paragraph by paragraph kept
|
|
135
|
+
only its last line. These were the most-used gaps in customers' agent code
|
|
136
|
+
(`API_PARITY_REPORT.md`). Each run has its own `font` (`bold`, `italic`,
|
|
137
|
+
`size`, `name`, `color.rgb`, and now `underline`, which raised), each
|
|
138
|
+
paragraph its own `alignment`, `level`, `line_spacing`, `space_before` and
|
|
139
|
+
`space_after` (which warned and were dropped) and `font` (its run defaults,
|
|
140
|
+
`a:defRPr`), and `text_frame.clear()` / `paragraph.clear()` keep the
|
|
141
|
+
paragraph's properties, as in python-pptx. Assigning `None` removes a run's
|
|
142
|
+
or paragraph's own value, and a run added with no text stays an empty run. A
|
|
143
|
+
cell holding more than one run is sent whole (`SetTableCell.paragraphs`, with
|
|
144
|
+
its text beside it for a studio that predates the field). On an uploaded
|
|
145
|
+
table a kept run keeps its own run properties, a kept paragraph its own
|
|
146
|
+
paragraph properties and end mark, and the export writes only what the edit
|
|
147
|
+
changed; an uploaded cell's runs read their own family, italic and underline.
|
|
148
|
+
Needs a studio with `SetTableCell.paragraphs`; an older one applies the
|
|
149
|
+
cell's text alone.
|
|
150
|
+
- `cell.text_frame.word_wrap = True` no longer warns: a table cell's text wraps
|
|
151
|
+
at the cell width in PowerPoint and the studio, which is what `True` asks for.
|
|
152
|
+
- `run.text = …` on a table cell holding one run keeps the run, as python-pptx's
|
|
153
|
+
does: only its text changes, and the font, size, colour, bold, italic and link
|
|
154
|
+
it had in the uploaded deck (or that this session wrote) stay, with its
|
|
155
|
+
paragraph's alignment. It retyped the cell (`cell.text`), which dropped every
|
|
156
|
+
property read from the deck: a template's styled cell came out plain. The
|
|
157
|
+
stored run is kept (`SetTableCell.paragraphs`, `keep`) and only its text is
|
|
158
|
+
sent; a studio that predates the field applies the text beside it, which
|
|
159
|
+
keeps the run's style. An uploaded cell's empty run (a template's run
|
|
160
|
+
formatted for text to come) is a run too: given text (or an underline) it
|
|
161
|
+
keeps its properties and link, and `paragraph.add_run()` adds a run after it,
|
|
162
|
+
as python-pptx's does, where the text went in a new, plain run (or in that
|
|
163
|
+
run). On a table the SDK built, the font written on an empty cell's run in an
|
|
164
|
+
earlier session goes with the text it is given, as this session's does.
|
|
165
|
+
- On a table the SDK built, a run's bold or colour written before a family, an
|
|
166
|
+
alignment or italic reads back after a refresh: it read `None`, while the
|
|
167
|
+
studio drew it and the downloaded deck had it.
|
|
168
|
+
- With a studio from this release, `font.bold = False` (`italic`, `underline`)
|
|
169
|
+
on a table the SDK built reaches the downloaded deck as `b="0"` (`i="0"`,
|
|
170
|
+
`u="none"`), as python-pptx writes it: PowerPoint drew a header run turned off
|
|
171
|
+
in the table style's bold, where the studio drew it regular. The fix is in the
|
|
172
|
+
studio's export, so it applies to every SDK version once the studio deploys;
|
|
173
|
+
an uploaded table already wrote them.
|
|
174
|
+
`tests/test_table_cell_formatting_parity.py` compares both with stock
|
|
175
|
+
python-pptx in session, after a refresh and in the exported XML.
|
|
176
|
+
- `shape.shadow.inherit = False` on a shape without a shadow of its own gives
|
|
177
|
+
it python-pptx's empty effect list (`<a:effectLst/>` in the downloaded
|
|
178
|
+
deck), so it casts no shadow, the one its theme effect style casts included,
|
|
179
|
+
and `inherit` reads `False` after the deck is reopened. It changed nothing
|
|
180
|
+
before: PowerPoint kept showing the style's shadow (an Office 2007 theme's
|
|
181
|
+
second effect style, which `add_shape()` names), and a reopened deck read
|
|
182
|
+
`True`. `inherit = True` removes the shape's own shadow and that list, and
|
|
183
|
+
`shadow.visible = False` on a shape with a shadow of its own keeps its
|
|
184
|
+
effect list, emptied, as deleting the `<a:outerShdw>` would (the export
|
|
185
|
+
dropped the list, and PowerPoint drew the style's shadow in its place). An
|
|
186
|
+
uploaded shape with an effect list of its own, an empty `<a:effectLst/>`
|
|
187
|
+
included, reads `inherit == False` as in python-pptx (it read `True`), and
|
|
188
|
+
`inherit = True` removes that list, every effect in it, from the downloaded
|
|
189
|
+
deck. A shadow the shape takes from its style is not reported as its own:
|
|
190
|
+
`inherit` stays `True` and `visible` `False`. Needs a studio with
|
|
191
|
+
`SetShapeShadow.inherit`; an older one ignores the field.
|
|
192
|
+
- `line.dash_style` on an uploaded line reads its `<a:prstDash>` as python-pptx
|
|
193
|
+
1.0.2 does: `sysDash` is `SQUARE_DOT`, `sysDot` `ROUND_DOT`, `lgDashDotDot`
|
|
194
|
+
`DASH_DOT_DOT` and an explicit `solid` `SOLID`, where the studio's import read
|
|
195
|
+
`sysDash` as `DASH`, `sysDashDot` as `DASH_DOT` and `lgDashDotDot` as no dash
|
|
196
|
+
at all (on a connector, `LONG_DASH_DOT`), and a shape's explicit `solid` as
|
|
197
|
+
`None`. The presets python-pptx has no member for (`dot`, `sysDashDot`,
|
|
198
|
+
`sysDashDotDot`) raise its `ValueError`, as python-pptx does. A dash set in
|
|
199
|
+
the session reads back as the preset the studio writes for it. Needs a
|
|
200
|
+
studio that sends `strokeDashPreset`; with an older one, and for decks
|
|
201
|
+
imported before it, `dash_style` reads as before.
|
|
202
|
+
- `font.fill.type` reads a run's own fill as python-pptx does:
|
|
203
|
+
`MSO_FILL.GRADIENT` for gradient text (`<a:gradFill>`), `MSO_FILL.BACKGROUND`
|
|
204
|
+
for hollow text (`<a:noFill/>`), `MSO_FILL.SOLID` for a colour of its own and
|
|
205
|
+
`None` when the run inherits its fill. It read `None` unless `fill.solid()`
|
|
206
|
+
had been called in the session, and then the string `"solid"`. Setting
|
|
207
|
+
`font.color` on gradient or hollow text makes it solid, as in python-pptx.
|
|
208
|
+
The glyph outline (`<a:ln>`, which python-pptx has no API for) and a
|
|
209
|
+
gradient are carried unchanged when the SDK resends a run. Needs a studio
|
|
210
|
+
that stores `textFill`; with an older one, and for decks imported before it
|
|
211
|
+
until they are imported again, `fill.type` reads as before.
|
|
212
|
+
- `font.fill.background()` makes the text of a run or a paragraph's font
|
|
213
|
+
transparent (`<a:noFill/>`), as python-pptx does: hollow glyphs, drawn by
|
|
214
|
+
their outline alone (an uploaded run's `<a:ln>`) and invisible without one.
|
|
215
|
+
It raised. A colour set afterwards fills the text again, within one batch
|
|
216
|
+
too. Table and chart text still raise. `font.fill.solid()` on hollow or
|
|
217
|
+
gradient text removes that fill, and the text takes the colour it inherits
|
|
218
|
+
until a colour is set (python-pptx writes a colourless `<a:solidFill/>`); it
|
|
219
|
+
only recorded the fill type. Needs a studio that stores `textFill`; an older
|
|
220
|
+
one ignores the writes.
|
|
221
|
+
|
|
5
222
|
## 0.18.0 — chart.chart_area / chart.plot_area format and add_chart(name=)
|
|
6
223
|
|
|
7
224
|
- `chart.chart_area.format` and `chart.plot_area.format` now round-trip fill
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# athena-python-pptx ↔ python-pptx — open parity questions
|
|
2
|
+
|
|
3
|
+
The open gaps and design questions between this SDK and python-pptx 1.0.2, in the order to work
|
|
4
|
+
them: by how much customers' agent code uses each. Sister documents:
|
|
5
|
+
|
|
6
|
+
- [`API_PARITY_REPORT.md`](API_PARITY_REPORT.md) — generated: the usage census joined with the
|
|
7
|
+
surface audit, with every used member's status.
|
|
8
|
+
- [`docs/API_PARITY_EXCEPTIONS.md`](docs/API_PARITY_EXCEPTIONS.md) — the deviations this SDK makes on
|
|
9
|
+
purpose, and the generated tables of the members it refuses or ignores.
|
|
10
|
+
|
|
11
|
+
Usage comes from `docs/sdk_usage_census.json` (`scripts/sdk_usage_census.py`): the customer
|
|
12
|
+
workspaces and sessions whose agent code reached a member in the census window. A gap no customer
|
|
13
|
+
reached stays listed, unscheduled, until a later census moves it.
|
|
14
|
+
|
|
15
|
+
## Used and broken today
|
|
16
|
+
|
|
17
|
+
<!-- BEGIN GENERATED: ranked-gaps (scripts/generate_parity_report.py; do not edit by hand) -->
|
|
18
|
+
Census: the 60 days from 2026-08-16 to 2026-10-14; counts are customer workspaces | sessions (`<3`: fewer than three workspaces). Full table: `API_PARITY_REPORT.md`.
|
|
19
|
+
|
|
20
|
+
| # | Member | SDK class | Workspaces | Sessions | What the SDK does |
|
|
21
|
+
|---|---|---|---|---|---|
|
|
22
|
+
| 1 | `Shapes._spTree` | `Shapes` | <3 | <3 | raises: in 4 of 8 probed contexts; athena-python-pptx mirrors a slide's own shape tree, slide.shapes._spTree; a notes slide's, layout's or master's is not mirrored. |
|
|
23
|
+
| 2 | `Chart._chartSpace` | `Chart` | <3 | <3 | raises: python-pptx XML internal: Use the Chart API: chart.plots, chart.series, chart.replace_data(chart_data), chart.has_title / chart.chart_title, chart.value_axis, chart.category_axis. |
|
|
24
|
+
| 3 | `Slide._element` | `Slide` | <3 | <3 | raises: python-pptx XML internal: Use the object's own properties and methods. |
|
|
25
|
+
| 4 | `Subshape.part` | `Paragraph` | <3 | <3 | raises: athena-python-pptx edits a deck that lives on the PPTX Studio server, so there are no OPC package parts or relationships to reach. |
|
|
26
|
+
| 5 | `_ChartSeries._element` | `_ChartSeries` | <3 | <3 | raises: python-pptx XML internal: Use the object's own properties and methods. |
|
|
27
|
+
| 6 | `ColorFormat.brightness` | `_FillColorFormat` | <3 | <3 | raises: Shape fills carry no brightness in the Athena studio (SetShapeStyle has no lumMod / lumOff). |
|
|
28
|
+
<!-- END GENERATED: ranked-gaps -->
|
|
29
|
+
|
|
30
|
+
## Open, in usage order
|
|
31
|
+
|
|
32
|
+
### python-pptx XML internals agents reach
|
|
33
|
+
|
|
34
|
+
In the table above, each from fewer than three workspaces: `chart._chartSpace`, `slide._element`,
|
|
35
|
+
`series._element`, `paragraph.part`, and `shapes._spTree` where it is not a slide's own (a notes
|
|
36
|
+
slide's, layout's or master's shapes; the census cannot tell which the agents read). Each raises
|
|
37
|
+
`UnsupportedAttributeError` naming the supported API. The text, table and shape-tree mirrors (0.17)
|
|
38
|
+
cover the recipes customers used most.
|
|
39
|
+
|
|
40
|
+
**Question:** mirror a chart's or a slide's XML only when a census shows a recipe at three or
|
|
41
|
+
more workspaces.
|
|
42
|
+
|
|
43
|
+
### Shape fill brightness
|
|
44
|
+
|
|
45
|
+
In the table above, from fewer than three workspaces: `shape.fill.fore_color.brightness = …` raises; the
|
|
46
|
+
studio's fill model has no `lumMod` / `lumOff`.
|
|
47
|
+
|
|
48
|
+
### Members agents invent
|
|
49
|
+
|
|
50
|
+
Not parity gaps (python-pptx raises too), listed in the report's "Members agents call that
|
|
51
|
+
python-pptx does not have": a misspelt `MSO_SHAPE.ISOCELES_TRIANGLE`, `prs.slides.move(…)` /
|
|
52
|
+
`prs.move_slide(…)`, `shapes.remove(…)`, `category_axis.major_unit`, and agents setting the SDK's
|
|
53
|
+
private HTTP client timeouts (`Client.timeout`, and attributes of their own on `Client`), which
|
|
54
|
+
matches the read timeouts in the error signals.
|
|
55
|
+
|
|
56
|
+
**Question:** which of these deserve a teaching `AttributeError` (as `GUESSED_MEMBERS` gives the
|
|
57
|
+
slide-move guesses) or a line in the skill text.
|
|
58
|
+
|
|
59
|
+
## Open, with no customer use in the window (unscheduled)
|
|
60
|
+
|
|
61
|
+
- **3-D, stock and surface charts** — `add_chart()` raises `UnsupportedFeatureError`. The deck
|
|
62
|
+
census found no stock chart in any customer deck either.
|
|
63
|
+
- **Gradient and pattern text** (`Font.fill.gradient()` / `.patterned()`) — raise.
|
|
64
|
+
- **Notes-slide shapes and placeholders** (`NotesSlide.shapes`, `.placeholders`,
|
|
65
|
+
`.notes_placeholder`) — empty collections; `notes_slide.notes_text_frame` carries notes text.
|
|
66
|
+
`prs.notes_master` raises.
|
|
67
|
+
- **Layout and master backgrounds** — read-only; `fill.solid()` and colour writes raise.
|
|
68
|
+
- **Table-cell theme colours and hyperlinks** — `font.color.theme_color`,
|
|
69
|
+
`fill.fore_color.theme_color` and `run.hyperlink.address` on a cell raise; a cell's link is
|
|
70
|
+
cell-wide (`SetTableCell.hyperlinkUrl`), so every run of a cell reads it.
|
|
71
|
+
- **Table-cell `word_wrap = False` / `None` and `auto_size`** — warn once and keep the value
|
|
72
|
+
locally. `word_wrap = True` is accepted (see Resolved).
|
|
73
|
+
- **`MSO_LANGUAGE_ID.MIXED`** — exists for parity; nothing returns it (runs are addressed one at a
|
|
74
|
+
time).
|
|
75
|
+
- **`Area3DPlot`** behaves as `AreaPlot`.
|
|
76
|
+
- **`XyChartData.add_series(name, values)`** — signature drift from python-pptx's
|
|
77
|
+
`add_series(name, number_format=None)` (documented in `docs/API_PARITY_EXCEPTIONS.md`).
|
|
78
|
+
|
|
79
|
+
## Resolved since the 2026-05 report
|
|
80
|
+
|
|
81
|
+
- **Table cells hold python-pptx's paragraphs and runs** — the census's most-used gap
|
|
82
|
+
(`_TableCellParagraph.add_run` 3 workspaces / 23 sessions, `_TableCellTextFrame.add_paragraph`
|
|
83
|
+
3 / 10). `add_run()` and `add_paragraph()` add a run or paragraph, each run keeps its own font
|
|
84
|
+
(`underline` included), a paragraph carries its own alignment, level, line spacing and space
|
|
85
|
+
before / after, and `clear()` keeps the first paragraph's properties, as in python-pptx. The SDK
|
|
86
|
+
sends the cell's whole text body (`SetTableCell.paragraphs`); an uploaded cell's runs keep their
|
|
87
|
+
own XML and take only what an edit wrote. `tests/test_table_cell_runs_parity.py` compares the
|
|
88
|
+
reads with stock python-pptx in session, after a refresh and in the exported deck.
|
|
89
|
+
- **`cell.text_frame.word_wrap = True`** (3 workspaces / 19 sessions) is accepted without a
|
|
90
|
+
warning: PowerPoint and the studio wrap a cell's text at the cell width, which is what `True`
|
|
91
|
+
asks for. It reads back in the session; after a refresh it reads `None` (python-pptx reads the
|
|
92
|
+
`wrap="square"` it wrote).
|
|
93
|
+
- **Chart patch ops** — axis scale, gridlines, tick-label and legend fonts, legend position, chart
|
|
94
|
+
font and style, data labels, markers, series and point fills, trendlines and error bars all
|
|
95
|
+
round-trip through chart patches. What the studio still ignores is the warn-once list in
|
|
96
|
+
`docs/API_PARITY_EXCEPTIONS.md` (`unsupported-writes`).
|
|
97
|
+
- **`Legend.include_in_layout`** — `False`, the value customers' agents assign, is what the studio
|
|
98
|
+
draws; only `True` (legend over the plot) warns.
|
|
99
|
+
- **`Picture.image`** returns python-pptx's `Image` with the picture's bytes (0.18.0).
|
|
100
|
+
- **`GroupShape.shapes`** lists the group's member shapes.
|
|
101
|
+
- **Detached chart fonts** (`chart.font`, `legend.font`, `tick_labels.font`) send their changes.
|
|
102
|
+
- **XML recipes** — `prs.slides._sldIdLst`, `shape._element` / `_spTree`, `text_frame._txBody`,
|
|
103
|
+
`paragraph._p`, `run._r`, `table._tbl`, `cell._tc` are real mirrors (0.17.0); the census's error
|
|
104
|
+
signals show customers hitting the old refusals (`Shapes._spTree`, `Slides._sldIdLst`,
|
|
105
|
+
`Run._r`) before that release.
|
|
106
|
+
- **Table-cell font name and italic** — supported; the `UnsupportedFeatureError` customers hit for
|
|
107
|
+
`[cell font.name]` / `[cell font.italic]` earlier in the window no longer exists.
|
|
108
|
+
- **`Connector.begin_connect(None)`** raises instead of recording an empty endpoint.
|
|
109
|
+
- **Private-class naming** — python-pptx's public names (`AxisTitle`, `TickLabels`, `Legend`, …) are
|
|
110
|
+
the SDK's (Tranche B); `_Paragraph`, `_Run`, `_Cell` import from their python-pptx paths.
|
|
111
|
+
- **`Font.language_id`** (with `MSO_LANGUAGE_ID.NONE` removing the language) and the language-tag
|
|
112
|
+
mapping, **`shape.click_action`** returning `ActionSetting`, and **core properties** — done.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-pptx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
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
|
|
@@ -869,6 +869,15 @@ print(info['wsUrl'])
|
|
|
869
869
|
print(info['authToken'])
|
|
870
870
|
```
|
|
871
871
|
|
|
872
|
+
People can edit a deck while your code runs, and your writes reach it when
|
|
873
|
+
the batch is sent (in Athena's executor, when the script ends). A write of a
|
|
874
|
+
box's text (`text_frame.text`, `paragraph.text`, `run.text`, …) names the text
|
|
875
|
+
it replaces as the SDK read it, so the studio keeps what someone typed into
|
|
876
|
+
the box after that read: your change goes onto the box's current text. Where
|
|
877
|
+
you both changed the same words, both versions stay, theirs first. Nothing
|
|
878
|
+
changes in the code you write; `text_frame.text` reads what you wrote until
|
|
879
|
+
the deck is read again (`prs.refresh()`).
|
|
880
|
+
|
|
872
881
|
---
|
|
873
882
|
|
|
874
883
|
## Supported Features
|