athena-python-pptx 0.14.0__tar.gz → 0.14.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/.gitignore +9 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/AGENTS.md +12 -6
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/API_PARITY_REPORT.md +4 -2
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/CHANGELOG.md +151 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/DEV-GUIDE.md +13 -5
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/PKG-INFO +1 -1
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/docs/API_PARITY_EXCEPTIONS.md +638 -26
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/__init__.py +96 -28
- athena_python_pptx-0.14.2/pptx/_unsupported.py +408 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/batching.py +210 -26
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/errors.py +11 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/chart.py +7 -2
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/presentation.py +93 -39
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/__init__.py +487 -82
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/slides.py +75 -36
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/__init__.py +154 -21
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pyproject.toml +14 -1
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/CLAUDE.md +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/PARITY_QUESTIONS.md +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/PUBLISHING.md +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/README.md +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/docs/athena-api.json +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/docs/athena-api.md +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_athena_extension.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_citations.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_ptc.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_references.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/action.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/__init__.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/axis.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/category.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/chart.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/data.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/datalabel.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/legend.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/marker.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/plot.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/point.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/series.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/xlsx.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/client.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/commands.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/decorators.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/__init__.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/chtfmt.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/color.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/effect.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/fill.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/line.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/docgen.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/__init__.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/action.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/chart.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/dml.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/lang.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/shapes.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/text.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/exc.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/kit.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/media.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/oxml/__init__.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/oxml/ns.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/package.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/__init__.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/_base.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/coreprops.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/embeddedpackage.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/image.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/media.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/presentation.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/slide.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/autoshape.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/base.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/connector.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/freeform.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/graphfrm.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/group.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/picture.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/placeholder.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/shapetree.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shared.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/slide.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/spec.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/table.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/fonts.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/layout.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/text.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/types.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/typing.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/units.py +0 -0
- {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/util.py +0 -0
|
@@ -28,8 +28,17 @@ wheels/
|
|
|
28
28
|
venv/
|
|
29
29
|
ENV/
|
|
30
30
|
|
|
31
|
+
# uv's lockfile for local `uv run` / `uv sync`. CI installs with pip
|
|
32
|
+
# (test-pptx-studio-sdk.yml) and the SDK ships as a library whose consumers
|
|
33
|
+
# resolve their own dependencies, so a committed lock would be read by
|
|
34
|
+
# nothing and go stale with every version bump.
|
|
35
|
+
uv.lock
|
|
36
|
+
|
|
31
37
|
# Testing
|
|
32
38
|
.pytest_cache/
|
|
39
|
+
# Per-checkout test caches, e.g. the stock python-pptx parity reference
|
|
40
|
+
# (tests/_stock_python_pptx.py).
|
|
41
|
+
.cache/
|
|
33
42
|
.coverage
|
|
34
43
|
htmlcov/
|
|
35
44
|
.tox/
|
|
@@ -99,16 +99,22 @@ If there is a genuine technical reason why a deviation from python-pptx is neces
|
|
|
99
99
|
## Development
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
-
#
|
|
103
|
-
|
|
102
|
+
# Full suite, the way CI selects it — uv installs the `dev` dependency group
|
|
103
|
+
# into the SDK's own .venv, so this never runs another interpreter's pytest
|
|
104
|
+
uv run pytest tests/ -q --ignore=tests/test_visual_comparison.py
|
|
104
105
|
|
|
105
|
-
#
|
|
106
|
-
|
|
106
|
+
# Specific test file
|
|
107
|
+
uv run pytest tests/test_sdk_features.py -x -q
|
|
107
108
|
|
|
108
|
-
#
|
|
109
|
-
python -m pytest tests/
|
|
109
|
+
# pip alternative (what CI does)
|
|
110
|
+
pip install -e ../../python-sdk/athena-references -e ".[dev]" && python -m pytest tests/ -x -q
|
|
110
111
|
```
|
|
111
112
|
|
|
113
|
+
Stock python-pptx for the parity tests is installed on first use into this
|
|
114
|
+
checkout's git-ignored `.cache/` (per pinned version × interpreter × platform;
|
|
115
|
+
`tests/_stock_python_pptx.py`) — never into a shared `/tmp` directory. A failed
|
|
116
|
+
install fails the parity tests rather than skipping them.
|
|
117
|
+
|
|
112
118
|
## Architecture
|
|
113
119
|
|
|
114
120
|
This is a **REST API client** that mimics the python-pptx API. Instead of manipulating XML directly, method calls are translated to commands sent to the PPTX Studio API server. Key differences from python-pptx internals:
|
|
@@ -159,6 +159,8 @@ See `docs/API_PARITY_EXCEPTIONS.md` for the full list. Highlights:
|
|
|
159
159
|
## How to Run
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
|
-
|
|
163
|
-
python -m pytest tests/test_python_pptx_api_parity.py -v -s
|
|
162
|
+
uv run pytest tests/test_python_pptx_api_parity.py -v -s
|
|
164
163
|
```
|
|
164
|
+
|
|
165
|
+
The stock python-pptx reference is installed on first use into this
|
|
166
|
+
checkout's git-ignored `.cache/` (`tests/_stock_python_pptx.py`).
|
|
@@ -2,6 +2,157 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `athena-python-pptx` are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.14.2 — `chart.value_axis` / `chart.category_axis` edit the axis python-pptx returns
|
|
6
|
+
|
|
7
|
+
Axis edits (`axis_title`, `visible`, `minimum_scale` / `maximum_scale` /
|
|
8
|
+
`major_unit` / `minor_unit` / `reverse_order`, gridlines and their line,
|
|
9
|
+
`tick_labels`, tick marks, `crosses` / `crosses_at`, `category_type`) now
|
|
10
|
+
change the axis python-pptx 1.0.2's `chart.category_axis` /
|
|
11
|
+
`chart.value_axis` return:
|
|
12
|
+
|
|
13
|
+
- On an XY chart (scatter, bubble) `value_axis` is the Y axis and
|
|
14
|
+
`category_axis` the X axis. Previously a `value_axis` edit changed the X
|
|
15
|
+
axis of an uploaded XY chart and was dropped on one made with `add_chart`,
|
|
16
|
+
and a `category_axis` edit was dropped on both.
|
|
17
|
+
- On an XY chart `category_axis` is a `ValueAxis`, as in python-pptx, so
|
|
18
|
+
`chart.category_axis.major_unit` / `crosses` / `crosses_at` reach the X
|
|
19
|
+
axis (a `CategoryAxis` has no such properties, so those writes did
|
|
20
|
+
nothing).
|
|
21
|
+
- **Behaviour change:** on a chart with two value axes (a combo with a
|
|
22
|
+
secondary axis) `value_axis` is the secondary axis, as in python-pptx;
|
|
23
|
+
previously it was the primary one. Scripts that styled the primary axis
|
|
24
|
+
through `value_axis` on such a chart now style the secondary axis.
|
|
25
|
+
- `axis_title.text_frame.text` reads the title back from the same axis.
|
|
26
|
+
- `chart.replace_data()` keeps the chart's axes (and a combo's other plots)
|
|
27
|
+
in the local mirror, as the server does, so later axis edits still address
|
|
28
|
+
the right axis and axis titles still read back.
|
|
29
|
+
- Each axis patch now carries `axisRef` — the kind of axis and its position,
|
|
30
|
+
e.g. `{"role": "value", "index": 1}` — next to `axisId`. Works with any
|
|
31
|
+
studio: one that predates `axisRef` drops the field and resolves `axisId`,
|
|
32
|
+
which the SDK now picks per chart (`val-axis-y` for an XY chart's Y axis
|
|
33
|
+
or a secondary value axis, `val-axis-x` for an XY chart's X axis) so it
|
|
34
|
+
names the same axis.
|
|
35
|
+
|
|
36
|
+
## 0.14.2 — inside Athena's executor a failed script leaves the deck untouched
|
|
37
|
+
|
|
38
|
+
Agent scripts crashed partway (an `AttributeError` on a chart adapter, an
|
|
39
|
+
`UnsupportedFeatureError` from a setter) after earlier edits had already been
|
|
40
|
+
sent by the 2 s idle auto-flush, by `add_slide()` flushing to hand back the
|
|
41
|
+
server's slide, or by `with prs.batch():` exits; the executor's
|
|
42
|
+
`pptx.discard_all()` could only drop what was still buffered, so decks were
|
|
43
|
+
left half-edited.
|
|
44
|
+
|
|
45
|
+
- With `ATHENA_PPTX_EXECUTOR_MODE=1` (set by agora's `execute_presentation_code`
|
|
46
|
+
for every run, alongside `ATHENA_PPTX_BASE_URL`) the SDK never sends on its
|
|
47
|
+
own: no idle auto-flush, no `atexit` flush, `with prs.batch():` keeps its
|
|
48
|
+
commands buffered on exit, and `add_slide()` / `Slides.insert()` /
|
|
49
|
+
`Slides.delete()` / `delete_slides()` / `group_shapes()` return local
|
|
50
|
+
proxies as they do inside a batch. Writes reach the deck at the run's final
|
|
51
|
+
`pptx.flush_all(strict=True)` — or are dropped by `pptx.discard_all()` when
|
|
52
|
+
the script raised.
|
|
53
|
+
- Sync points still commit what is buffered so far: `flush()`, `save()`,
|
|
54
|
+
`save_to_bytes()`, `refresh()`, `close()`, `render_slide()` /
|
|
55
|
+
`slide.render()`, a clean `with Presentation(...)` exit, `slide.clone()`,
|
|
56
|
+
`copy_slide_from()`, `slide.slide_layout = ...`, `apply_foreign_layout()`.
|
|
57
|
+
- `with Presentation(...) as prs:` discards the writes buffered inside the
|
|
58
|
+
block when the block raises (executor only; writes buffered before it stay
|
|
59
|
+
for the run's final flush); a `Presentation` the script drops keeps its
|
|
60
|
+
buffered writes for the run's final drain instead of flushing them at
|
|
61
|
+
garbage collection. A `CommandBuffer(auto_batch=False)` buffers too while
|
|
62
|
+
commits are deferred.
|
|
63
|
+
- A final flush larger than the per-request budget (24 MiB) is still sent as
|
|
64
|
+
several requests; when a later one fails, `FlushAllError` now says how many
|
|
65
|
+
commands earlier requests applied (`applied_before_failure` on the
|
|
66
|
+
exception). Commands in a later request that name a slide an earlier request
|
|
67
|
+
created by its `sld_…` client id (`clear_shapes()`, freeforms,
|
|
68
|
+
`clone_placeholder()`, and edits of the new slide's layout placeholders,
|
|
69
|
+
addressed as `__inherited__:<slide id>:…` until a refresh) now carry the
|
|
70
|
+
server id, which the server needs outside the creating request — in every
|
|
71
|
+
mode, also after a `flush()` / `render()` that did not refresh.
|
|
72
|
+
- Outside the executor (plain scripts, notebooks, Computer assets) behaviour
|
|
73
|
+
is unchanged. `CommandBuffer(defer_commits=...)` overrides the environment.
|
|
74
|
+
- Release note: needs the agora change that sets the variable (older agora
|
|
75
|
+
keeps today's eager flushing; older SDKs ignore the variable), so either
|
|
76
|
+
deploy order is safe.
|
|
77
|
+
|
|
78
|
+
## 0.14.2 — `replace_data()` drops a chart's old category hierarchy
|
|
79
|
+
|
|
80
|
+
- `chart.replace_data()` on a chart with multi-level categories (an imported
|
|
81
|
+
Office 2016 waterfall, for one) no longer leaves the old hierarchy in the
|
|
82
|
+
local mirror. The new categories are flat, and the server drops the old
|
|
83
|
+
hierarchy the same way, so `chart.plots[0].categories` reads back the
|
|
84
|
+
categories you passed.
|
|
85
|
+
|
|
86
|
+
## 0.14.2 — no silent no-ops: an unsupported call fails or warns at its own line
|
|
87
|
+
|
|
88
|
+
Scripts could build on a write that never reached the deck: a setter kept the
|
|
89
|
+
value on the local proxy and sent nothing, or a convenience method died with a
|
|
90
|
+
bare `AttributeError` from inside the SDK. `tests/test_no_silent_noops.py` now
|
|
91
|
+
probes every public setter and method of every deck-bound adapter (mocked
|
|
92
|
+
client) and fails CI on any that neither sends a command, warns, nor raises.
|
|
93
|
+
|
|
94
|
+
- python-pptx members this SDK has no equivalent for (XML `element`, OPC
|
|
95
|
+
`part`, `parent`, the chart-workbook builders, … on 109 classes) raise the
|
|
96
|
+
new `pptx.UnsupportedAttributeError`, naming the member and what to use
|
|
97
|
+
instead. It is both an `UnsupportedFeatureError` and an `AttributeError`, so
|
|
98
|
+
`hasattr()` and `getattr(obj, name, default)` behave as before.
|
|
99
|
+
- These writes now raise at the call instead of being dropped:
|
|
100
|
+
`UnsupportedFeatureError` for a non-zero `brightness` on a shape fill or line
|
|
101
|
+
colour or on a table-cell colour, `theme_color` on a table-cell fill or font
|
|
102
|
+
colour, `font.color.brightness` without a theme colour, `font.fill.background()`
|
|
103
|
+
(python-pptx's transparent text), `fill.solid()` / `fore_color.rgb` on a layout,
|
|
104
|
+
master or notes-page background (the notes-page background used to restyle the
|
|
105
|
+
*slide*), and `ChartWorkbook.update_from_xlsx_blob()`;
|
|
106
|
+
`UnsupportedAttributeError` for `shape.text = …` on a shape with no text frame;
|
|
107
|
+
`TypeError`, as in python-pptx, for `fill.pattern` outside a patterned fill and
|
|
108
|
+
`fill.gradient_angle` / `gradient_stops` outside a gradient fill.
|
|
109
|
+
- These now warn once per presentation (the value is kept locally): chart-font
|
|
110
|
+
properties the target's font patch cannot carry (for example
|
|
111
|
+
`chart.font.italic`, `legend.font.underline`, `data_label.font.name`, title
|
|
112
|
+
`language_id`), chart-title paragraph `alignment` and `word_wrap`,
|
|
113
|
+
`Legend.horz_offset` other than 0, `Legend.include_in_layout = True`,
|
|
114
|
+
`DataLabels.show_legend_key = True`, `TickLabels.offset` other than 100, a
|
|
115
|
+
`number_format_is_linked` that contradicts `number_format`,
|
|
116
|
+
`notes_slide.name`, and `add_paragraph()` / `add_run()` on a table cell that
|
|
117
|
+
already has text (a cell holds one paragraph with one run, so writing the
|
|
118
|
+
returned one replaces the text).
|
|
119
|
+
- These now reach the deck: `axis.has_title = False` removes the axis title
|
|
120
|
+
(`= True` alone still sends nothing — the studio exports no empty axis title —
|
|
121
|
+
and the title is sent with its text); clearing a chart title's text frame
|
|
122
|
+
sends the empty title; `data_label.text_frame.text = …`;
|
|
123
|
+
`paragraph.add_line_break()`; `text_frame.clear_formatting()` (hyperlinks are
|
|
124
|
+
kept; runs styled only by a font reference or a stored style field are reset
|
|
125
|
+
too); `run.copy_format_from()` (a styled destination is reset first, so none
|
|
126
|
+
of its own formatting survives); `text_frame.apply_style_to_all()` (unknown
|
|
127
|
+
keys raise `ValueError`); `shape.fill_solid()` and `shape.line_fill_none()`;
|
|
128
|
+
`shape.line.color = RGBColor(…)`.
|
|
129
|
+
- `run = paragraph.add_run(); run.font.bold = True` — a font or hyperlink write
|
|
130
|
+
before the run's text — sent a style for a run the server did not have yet,
|
|
131
|
+
so the whole batch failed at flush; the run is now created first.
|
|
132
|
+
- `prs.get_all_colors()`, `text_frame.get_styled_runs()` and
|
|
133
|
+
`get_formatting_summary()` no longer raise `AttributeError`.
|
|
134
|
+
- `Legend.include_in_layout` reads `False` by default: the studio never draws
|
|
135
|
+
the legend over the plot.
|
|
136
|
+
- The tables in `docs/API_PARITY_EXCEPTIONS.md` of unsupported members, of writes
|
|
137
|
+
that warn or raise, and of Athena extensions are generated by
|
|
138
|
+
`scripts/generate_parity_tables.py`; CI fails when they are stale.
|
|
139
|
+
|
|
140
|
+
Release note: scripts that made one of these calls may now stop with
|
|
141
|
+
`UnsupportedFeatureError` / `UnsupportedAttributeError`, or print a
|
|
142
|
+
`UserWarning`, where they used to continue on a write that never landed.
|
|
143
|
+
|
|
144
|
+
## 0.14.1 — no shutdown error from `Presentation.__del__`
|
|
145
|
+
|
|
146
|
+
- A `Presentation` still alive when the script's interpreter exits no longer
|
|
147
|
+
prints `Exception ignored in: <function Presentation.__del__>` /
|
|
148
|
+
`ImportError: sys.meta_path is None, Python is likely shutting down` to
|
|
149
|
+
stderr. `__del__` now does nothing once the interpreter is finalizing (the
|
|
150
|
+
atexit hook has already flushed pending commands by then), `atexit` is
|
|
151
|
+
imported at module level instead of inside `_release()`, and a finalizer
|
|
152
|
+
error is swallowed instead of printed. Garbage collection during normal
|
|
153
|
+
execution still flushes pending commands, unregisters the atexit hook and
|
|
154
|
+
closes the HTTP session.
|
|
155
|
+
|
|
5
156
|
## 0.14.0 — waterfall charts render and export as waterfalls
|
|
6
157
|
|
|
7
158
|
Requires a studio that authors `AddChart2016` waterfalls and accepts the
|
|
@@ -44,19 +44,27 @@ python scratch.py
|
|
|
44
44
|
### Run tests
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
-
# All tests
|
|
48
|
-
pytest
|
|
47
|
+
# All tests, the way CI selects them (uv installs the `dev` dependency group
|
|
48
|
+
# into the SDK's own .venv; a bare `pytest` may belong to another interpreter)
|
|
49
|
+
uv run pytest tests/ -q --ignore=tests/test_visual_comparison.py
|
|
49
50
|
|
|
50
51
|
# Single file
|
|
51
|
-
pytest tests/test_text.py -v
|
|
52
|
+
uv run pytest tests/test_text.py -v
|
|
52
53
|
|
|
53
54
|
# Single test
|
|
54
|
-
pytest tests/test_text.py::TestTextFrame::test_set_text -v
|
|
55
|
+
uv run pytest tests/test_text.py::TestTextFrame::test_set_text -v
|
|
55
56
|
|
|
56
57
|
# Integration tests (needs running API)
|
|
57
|
-
ATHENA_PPTX_BASE_URL=http://localhost:4000 pytest tests/test_smoke_integration.py -v
|
|
58
|
+
ATHENA_PPTX_BASE_URL=http://localhost:4000 uv run pytest tests/test_smoke_integration.py -v
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
The parity tests compare against stock python-pptx, which the first run
|
|
62
|
+
installs into this checkout's git-ignored `.cache/stock-python-pptx/<key>/`
|
|
63
|
+
(pinned version × interpreter × platform; see `tests/_stock_python_pptx.py`).
|
|
64
|
+
Each checkout has its own copy, concurrent runs install it once, and a failed
|
|
65
|
+
install fails those tests instead of skipping them. `uv.lock` is git-ignored:
|
|
66
|
+
CI installs with pip, so a committed lock would be read by nothing.
|
|
67
|
+
|
|
60
68
|
### Interactive REPL
|
|
61
69
|
|
|
62
70
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-pptx
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.2
|
|
4
4
|
Summary: Drop-in replacement for python-pptx that connects to PPTX Studio for real-time collaboration
|
|
5
5
|
Project-URL: Homepage, https://github.com/pptx-studio/python-sdk
|
|
6
6
|
Project-URL: Documentation, https://docs.pptx-studio.com/sdk/python
|