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.
Files changed (91) hide show
  1. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/.gitignore +9 -0
  2. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/AGENTS.md +12 -6
  3. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/API_PARITY_REPORT.md +4 -2
  4. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/CHANGELOG.md +151 -0
  5. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/DEV-GUIDE.md +13 -5
  6. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/PKG-INFO +1 -1
  7. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/docs/API_PARITY_EXCEPTIONS.md +638 -26
  8. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/__init__.py +96 -28
  9. athena_python_pptx-0.14.2/pptx/_unsupported.py +408 -0
  10. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/batching.py +210 -26
  11. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/errors.py +11 -0
  12. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/chart.py +7 -2
  13. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/presentation.py +93 -39
  14. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/__init__.py +487 -82
  15. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/slides.py +75 -36
  16. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/__init__.py +154 -21
  17. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pyproject.toml +14 -1
  18. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/CLAUDE.md +0 -0
  19. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/PARITY_QUESTIONS.md +0 -0
  20. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/PUBLISHING.md +0 -0
  21. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/README.md +0 -0
  22. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/docs/athena-api.json +0 -0
  23. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/docs/athena-api.md +0 -0
  24. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_athena_extension.py +0 -0
  25. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_citations.py +0 -0
  26. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_ptc.py +0 -0
  27. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/_references.py +0 -0
  28. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/action.py +0 -0
  29. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/__init__.py +0 -0
  30. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/axis.py +0 -0
  31. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/category.py +0 -0
  32. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/chart.py +0 -0
  33. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/data.py +0 -0
  34. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/datalabel.py +0 -0
  35. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/legend.py +0 -0
  36. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/marker.py +0 -0
  37. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/plot.py +0 -0
  38. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/point.py +0 -0
  39. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/series.py +0 -0
  40. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/chart/xlsx.py +0 -0
  41. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/client.py +0 -0
  42. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/commands.py +0 -0
  43. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/decorators.py +0 -0
  44. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/__init__.py +0 -0
  45. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/chtfmt.py +0 -0
  46. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/color.py +0 -0
  47. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/effect.py +0 -0
  48. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/fill.py +0 -0
  49. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/dml/line.py +0 -0
  50. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/docgen.py +0 -0
  51. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/__init__.py +0 -0
  52. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/action.py +0 -0
  53. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/chart.py +0 -0
  54. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/dml.py +0 -0
  55. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/lang.py +0 -0
  56. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/shapes.py +0 -0
  57. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/enum/text.py +0 -0
  58. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/exc.py +0 -0
  59. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/kit.py +0 -0
  60. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/media.py +0 -0
  61. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/oxml/__init__.py +0 -0
  62. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/oxml/ns.py +0 -0
  63. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/package.py +0 -0
  64. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/__init__.py +0 -0
  65. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/_base.py +0 -0
  66. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/coreprops.py +0 -0
  67. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/embeddedpackage.py +0 -0
  68. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/image.py +0 -0
  69. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/media.py +0 -0
  70. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/presentation.py +0 -0
  71. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/parts/slide.py +0 -0
  72. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/autoshape.py +0 -0
  73. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/base.py +0 -0
  74. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/connector.py +0 -0
  75. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/freeform.py +0 -0
  76. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/graphfrm.py +0 -0
  77. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/group.py +0 -0
  78. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/picture.py +0 -0
  79. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/placeholder.py +0 -0
  80. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shapes/shapetree.py +0 -0
  81. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/shared.py +0 -0
  82. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/slide.py +0 -0
  83. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/spec.py +0 -0
  84. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/table.py +0 -0
  85. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/fonts.py +0 -0
  86. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/layout.py +0 -0
  87. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/text/text.py +0 -0
  88. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/types.py +0 -0
  89. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/typing.py +0 -0
  90. {athena_python_pptx-0.14.0 → athena_python_pptx-0.14.2}/pptx/units.py +0 -0
  91. {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
- # 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,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 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.0
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