athena-python-pptx 0.14.1__tar.gz → 0.14.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/.gitignore +9 -0
  2. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/AGENTS.md +12 -6
  3. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/API_PARITY_REPORT.md +4 -2
  4. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/CHANGELOG.md +154 -0
  5. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/DEV-GUIDE.md +13 -5
  6. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/PKG-INFO +1 -1
  7. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/docs/API_PARITY_EXCEPTIONS.md +650 -26
  8. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/__init__.py +96 -28
  9. athena_python_pptx-0.14.3/pptx/_unsupported.py +408 -0
  10. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/batching.py +210 -26
  11. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/errors.py +11 -0
  12. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/chart.py +7 -2
  13. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/presentation.py +66 -25
  14. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/__init__.py +521 -84
  15. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/slides.py +75 -36
  16. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/text/__init__.py +154 -21
  17. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pyproject.toml +14 -1
  18. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/CLAUDE.md +0 -0
  19. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/PARITY_QUESTIONS.md +0 -0
  20. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/PUBLISHING.md +0 -0
  21. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/README.md +0 -0
  22. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/docs/athena-api.json +0 -0
  23. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/docs/athena-api.md +0 -0
  24. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/_athena_extension.py +0 -0
  25. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/_citations.py +0 -0
  26. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/_ptc.py +0 -0
  27. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/_references.py +0 -0
  28. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/action.py +0 -0
  29. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/__init__.py +0 -0
  30. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/axis.py +0 -0
  31. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/category.py +0 -0
  32. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/chart.py +0 -0
  33. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/data.py +0 -0
  34. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/datalabel.py +0 -0
  35. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/legend.py +0 -0
  36. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/marker.py +0 -0
  37. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/plot.py +0 -0
  38. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/point.py +0 -0
  39. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/series.py +0 -0
  40. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/chart/xlsx.py +0 -0
  41. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/client.py +0 -0
  42. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/commands.py +0 -0
  43. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/decorators.py +0 -0
  44. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/dml/__init__.py +0 -0
  45. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/dml/chtfmt.py +0 -0
  46. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/dml/color.py +0 -0
  47. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/dml/effect.py +0 -0
  48. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/dml/fill.py +0 -0
  49. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/dml/line.py +0 -0
  50. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/docgen.py +0 -0
  51. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/__init__.py +0 -0
  52. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/action.py +0 -0
  53. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/chart.py +0 -0
  54. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/dml.py +0 -0
  55. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/lang.py +0 -0
  56. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/shapes.py +0 -0
  57. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/enum/text.py +0 -0
  58. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/exc.py +0 -0
  59. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/kit.py +0 -0
  60. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/media.py +0 -0
  61. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/oxml/__init__.py +0 -0
  62. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/oxml/ns.py +0 -0
  63. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/package.py +0 -0
  64. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/__init__.py +0 -0
  65. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/_base.py +0 -0
  66. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/coreprops.py +0 -0
  67. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/embeddedpackage.py +0 -0
  68. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/image.py +0 -0
  69. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/media.py +0 -0
  70. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/presentation.py +0 -0
  71. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/parts/slide.py +0 -0
  72. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/autoshape.py +0 -0
  73. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/base.py +0 -0
  74. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/connector.py +0 -0
  75. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/freeform.py +0 -0
  76. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/graphfrm.py +0 -0
  77. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/group.py +0 -0
  78. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/picture.py +0 -0
  79. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/placeholder.py +0 -0
  80. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shapes/shapetree.py +0 -0
  81. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/shared.py +0 -0
  82. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/slide.py +0 -0
  83. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/spec.py +0 -0
  84. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/table.py +0 -0
  85. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/text/fonts.py +0 -0
  86. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/text/layout.py +0 -0
  87. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/text/text.py +0 -0
  88. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/types.py +0 -0
  89. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/typing.py +0 -0
  90. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/pptx/units.py +0 -0
  91. {athena_python_pptx-0.14.1 → athena_python_pptx-0.14.3}/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
- # Install in editable mode
103
- pip install -e ".[dev]"
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
- # Run tests
106
- python -m pytest tests/ -x -q
106
+ # Specific test file
107
+ uv run pytest tests/test_sdk_features.py -x -q
107
108
 
108
- # Run specific test file
109
- python -m pytest tests/test_sdk_features.py -x -q
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
- pip install python-pptx --target /tmp/pptx-standard
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,160 @@
2
2
 
3
3
  All notable changes to `athena-python-pptx` are documented in this file.
4
4
 
5
+ ## 0.14.3 — waterfall `connector_lines`
6
+
7
+ - New Athena extension `chart.plots[0].connector_lines = False` on a
8
+ `WaterfallPlot` hides the lines joining each bar to the next (Excel's
9
+ "Show connector lines"; `True` by default). Emits a
10
+ `SetWaterfallConnectorLines` chart patch; an SDK-authored waterfall then
11
+ exports one category per bar with its `gap_width`, and an uploaded Office
12
+ 2016 waterfall writes `connectorLines` into its own part. Reads back from
13
+ either.
14
+ - Studio-side (no SDK change, any SDK version): SDK-authored bar / column
15
+ series export `<c:invertIfNegative val="0"/>` by default, so negative bars
16
+ keep the series colour instead of rendering white in PowerPoint; an explicit
17
+ `series.invert_if_negative = True` still wins. `chart.replace_data()` on an
18
+ uploaded bar chart rebuilds its series the same way.
19
+
20
+ ## 0.14.2 — `chart.value_axis` / `chart.category_axis` edit the axis python-pptx returns
21
+
22
+ Axis edits (`axis_title`, `visible`, `minimum_scale` / `maximum_scale` /
23
+ `major_unit` / `minor_unit` / `reverse_order`, gridlines and their line,
24
+ `tick_labels`, tick marks, `crosses` / `crosses_at`, `category_type`) now
25
+ change the axis python-pptx 1.0.2's `chart.category_axis` /
26
+ `chart.value_axis` return:
27
+
28
+ - On an XY chart (scatter, bubble) `value_axis` is the Y axis and
29
+ `category_axis` the X axis. Previously a `value_axis` edit changed the X
30
+ axis of an uploaded XY chart and was dropped on one made with `add_chart`,
31
+ and a `category_axis` edit was dropped on both.
32
+ - On an XY chart `category_axis` is a `ValueAxis`, as in python-pptx, so
33
+ `chart.category_axis.major_unit` / `crosses` / `crosses_at` reach the X
34
+ axis (a `CategoryAxis` has no such properties, so those writes did
35
+ nothing).
36
+ - **Behaviour change:** on a chart with two value axes (a combo with a
37
+ secondary axis) `value_axis` is the secondary axis, as in python-pptx;
38
+ previously it was the primary one. Scripts that styled the primary axis
39
+ through `value_axis` on such a chart now style the secondary axis.
40
+ - `axis_title.text_frame.text` reads the title back from the same axis.
41
+ - `chart.replace_data()` keeps the chart's axes (and a combo's other plots)
42
+ in the local mirror, as the server does, so later axis edits still address
43
+ the right axis and axis titles still read back.
44
+ - Each axis patch now carries `axisRef` — the kind of axis and its position,
45
+ e.g. `{"role": "value", "index": 1}` — next to `axisId`. Works with any
46
+ studio: one that predates `axisRef` drops the field and resolves `axisId`,
47
+ which the SDK now picks per chart (`val-axis-y` for an XY chart's Y axis
48
+ or a secondary value axis, `val-axis-x` for an XY chart's X axis) so it
49
+ names the same axis.
50
+
51
+ ## 0.14.2 — inside Athena's executor a failed script leaves the deck untouched
52
+
53
+ Agent scripts crashed partway (an `AttributeError` on a chart adapter, an
54
+ `UnsupportedFeatureError` from a setter) after earlier edits had already been
55
+ sent by the 2 s idle auto-flush, by `add_slide()` flushing to hand back the
56
+ server's slide, or by `with prs.batch():` exits; the executor's
57
+ `pptx.discard_all()` could only drop what was still buffered, so decks were
58
+ left half-edited.
59
+
60
+ - With `ATHENA_PPTX_EXECUTOR_MODE=1` (set by agora's `execute_presentation_code`
61
+ for every run, alongside `ATHENA_PPTX_BASE_URL`) the SDK never sends on its
62
+ own: no idle auto-flush, no `atexit` flush, `with prs.batch():` keeps its
63
+ commands buffered on exit, and `add_slide()` / `Slides.insert()` /
64
+ `Slides.delete()` / `delete_slides()` / `group_shapes()` return local
65
+ proxies as they do inside a batch. Writes reach the deck at the run's final
66
+ `pptx.flush_all(strict=True)` — or are dropped by `pptx.discard_all()` when
67
+ the script raised.
68
+ - Sync points still commit what is buffered so far: `flush()`, `save()`,
69
+ `save_to_bytes()`, `refresh()`, `close()`, `render_slide()` /
70
+ `slide.render()`, a clean `with Presentation(...)` exit, `slide.clone()`,
71
+ `copy_slide_from()`, `slide.slide_layout = ...`, `apply_foreign_layout()`.
72
+ - `with Presentation(...) as prs:` discards the writes buffered inside the
73
+ block when the block raises (executor only; writes buffered before it stay
74
+ for the run's final flush); a `Presentation` the script drops keeps its
75
+ buffered writes for the run's final drain instead of flushing them at
76
+ garbage collection. A `CommandBuffer(auto_batch=False)` buffers too while
77
+ commits are deferred.
78
+ - A final flush larger than the per-request budget (24 MiB) is still sent as
79
+ several requests; when a later one fails, `FlushAllError` now says how many
80
+ commands earlier requests applied (`applied_before_failure` on the
81
+ exception). Commands in a later request that name a slide an earlier request
82
+ created by its `sld_…` client id (`clear_shapes()`, freeforms,
83
+ `clone_placeholder()`, and edits of the new slide's layout placeholders,
84
+ addressed as `__inherited__:<slide id>:…` until a refresh) now carry the
85
+ server id, which the server needs outside the creating request — in every
86
+ mode, also after a `flush()` / `render()` that did not refresh.
87
+ - Outside the executor (plain scripts, notebooks, Computer assets) behaviour
88
+ is unchanged. `CommandBuffer(defer_commits=...)` overrides the environment.
89
+ - Release note: needs the agora change that sets the variable (older agora
90
+ keeps today's eager flushing; older SDKs ignore the variable), so either
91
+ deploy order is safe.
92
+
93
+ ## 0.14.2 — `replace_data()` drops a chart's old category hierarchy
94
+
95
+ - `chart.replace_data()` on a chart with multi-level categories (an imported
96
+ Office 2016 waterfall, for one) no longer leaves the old hierarchy in the
97
+ local mirror. The new categories are flat, and the server drops the old
98
+ hierarchy the same way, so `chart.plots[0].categories` reads back the
99
+ categories you passed.
100
+
101
+ ## 0.14.2 — no silent no-ops: an unsupported call fails or warns at its own line
102
+
103
+ Scripts could build on a write that never reached the deck: a setter kept the
104
+ value on the local proxy and sent nothing, or a convenience method died with a
105
+ bare `AttributeError` from inside the SDK. `tests/test_no_silent_noops.py` now
106
+ probes every public setter and method of every deck-bound adapter (mocked
107
+ client) and fails CI on any that neither sends a command, warns, nor raises.
108
+
109
+ - python-pptx members this SDK has no equivalent for (XML `element`, OPC
110
+ `part`, `parent`, the chart-workbook builders, … on 109 classes) raise the
111
+ new `pptx.UnsupportedAttributeError`, naming the member and what to use
112
+ instead. It is both an `UnsupportedFeatureError` and an `AttributeError`, so
113
+ `hasattr()` and `getattr(obj, name, default)` behave as before.
114
+ - These writes now raise at the call instead of being dropped:
115
+ `UnsupportedFeatureError` for a non-zero `brightness` on a shape fill or line
116
+ colour or on a table-cell colour, `theme_color` on a table-cell fill or font
117
+ colour, `font.color.brightness` without a theme colour, `font.fill.background()`
118
+ (python-pptx's transparent text), `fill.solid()` / `fore_color.rgb` on a layout,
119
+ master or notes-page background (the notes-page background used to restyle the
120
+ *slide*), and `ChartWorkbook.update_from_xlsx_blob()`;
121
+ `UnsupportedAttributeError` for `shape.text = …` on a shape with no text frame;
122
+ `TypeError`, as in python-pptx, for `fill.pattern` outside a patterned fill and
123
+ `fill.gradient_angle` / `gradient_stops` outside a gradient fill.
124
+ - These now warn once per presentation (the value is kept locally): chart-font
125
+ properties the target's font patch cannot carry (for example
126
+ `chart.font.italic`, `legend.font.underline`, `data_label.font.name`, title
127
+ `language_id`), chart-title paragraph `alignment` and `word_wrap`,
128
+ `Legend.horz_offset` other than 0, `Legend.include_in_layout = True`,
129
+ `DataLabels.show_legend_key = True`, `TickLabels.offset` other than 100, a
130
+ `number_format_is_linked` that contradicts `number_format`,
131
+ `notes_slide.name`, and `add_paragraph()` / `add_run()` on a table cell that
132
+ already has text (a cell holds one paragraph with one run, so writing the
133
+ returned one replaces the text).
134
+ - These now reach the deck: `axis.has_title = False` removes the axis title
135
+ (`= True` alone still sends nothing — the studio exports no empty axis title —
136
+ and the title is sent with its text); clearing a chart title's text frame
137
+ sends the empty title; `data_label.text_frame.text = …`;
138
+ `paragraph.add_line_break()`; `text_frame.clear_formatting()` (hyperlinks are
139
+ kept; runs styled only by a font reference or a stored style field are reset
140
+ too); `run.copy_format_from()` (a styled destination is reset first, so none
141
+ of its own formatting survives); `text_frame.apply_style_to_all()` (unknown
142
+ keys raise `ValueError`); `shape.fill_solid()` and `shape.line_fill_none()`;
143
+ `shape.line.color = RGBColor(…)`.
144
+ - `run = paragraph.add_run(); run.font.bold = True` — a font or hyperlink write
145
+ before the run's text — sent a style for a run the server did not have yet,
146
+ so the whole batch failed at flush; the run is now created first.
147
+ - `prs.get_all_colors()`, `text_frame.get_styled_runs()` and
148
+ `get_formatting_summary()` no longer raise `AttributeError`.
149
+ - `Legend.include_in_layout` reads `False` by default: the studio never draws
150
+ the legend over the plot.
151
+ - The tables in `docs/API_PARITY_EXCEPTIONS.md` of unsupported members, of writes
152
+ that warn or raise, and of Athena extensions are generated by
153
+ `scripts/generate_parity_tables.py`; CI fails when they are stale.
154
+
155
+ Release note: scripts that made one of these calls may now stop with
156
+ `UnsupportedFeatureError` / `UnsupportedAttributeError`, or print a
157
+ `UserWarning`, where they used to continue on a write that never landed.
158
+
5
159
  ## 0.14.1 — no shutdown error from `Presentation.__del__`
6
160
 
7
161
  - A `Presentation` still alive when the script's interpreter exits no longer
@@ -44,19 +44,27 @@ python scratch.py
44
44
  ### Run tests
45
45
 
46
46
  ```bash
47
- # All tests
48
- pytest tests/ -v
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.1
3
+ Version: 0.14.3
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