athena-python-pptx 0.16.1__tar.gz → 0.17.1__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 (109) hide show
  1. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/AGENTS.md +29 -2
  2. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/API_PARITY_REPORT.md +1 -1
  3. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/CHANGELOG.md +161 -0
  4. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/PKG-INFO +2 -1
  5. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/docs/API_PARITY_EXCEPTIONS.md +375 -14
  6. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/__init__.py +2 -2
  7. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/_unsupported.py +94 -43
  8. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/batching.py +179 -4
  9. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/commands.py +3 -2
  10. athena_python_pptx-0.17.1/pptx/oxml/__init__.py +114 -0
  11. athena_python_pptx-0.17.1/pptx/oxml/_mirror.py +471 -0
  12. athena_python_pptx-0.17.1/pptx/oxml/_tbl.py +1160 -0
  13. athena_python_pptx-0.17.1/pptx/oxml/_txbody.py +1782 -0
  14. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/oxml/ns.py +8 -0
  15. athena_python_pptx-0.17.1/pptx/oxml/presentation.py +378 -0
  16. athena_python_pptx-0.17.1/pptx/oxml/shapes/__init__.py +68 -0
  17. athena_python_pptx-0.17.1/pptx/oxml/shapes/autoshape.py +75 -0
  18. athena_python_pptx-0.17.1/pptx/oxml/shapes/connector.py +31 -0
  19. athena_python_pptx-0.17.1/pptx/oxml/shapes/graphfrm.py +32 -0
  20. athena_python_pptx-0.17.1/pptx/oxml/shapes/groupshape.py +402 -0
  21. athena_python_pptx-0.17.1/pptx/oxml/shapes/picture.py +31 -0
  22. athena_python_pptx-0.17.1/pptx/oxml/shapes/shared.py +888 -0
  23. athena_python_pptx-0.17.1/pptx/oxml/simpletypes.py +727 -0
  24. athena_python_pptx-0.17.1/pptx/oxml/table.py +583 -0
  25. athena_python_pptx-0.17.1/pptx/oxml/text.py +652 -0
  26. athena_python_pptx-0.17.1/pptx/oxml/xmlchemy.py +716 -0
  27. athena_python_pptx-0.17.1/pptx/parts/presentation.py +54 -0
  28. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/presentation.py +24 -2
  29. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/__init__.py +861 -96
  30. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/slides.py +69 -4
  31. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/text/__init__.py +169 -2
  32. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/units.py +6 -0
  33. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pyproject.toml +5 -1
  34. athena_python_pptx-0.16.1/pptx/oxml/__init__.py +0 -43
  35. athena_python_pptx-0.16.1/pptx/parts/presentation.py +0 -15
  36. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/.gitignore +0 -0
  37. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/CLAUDE.md +0 -0
  38. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/DEV-GUIDE.md +0 -0
  39. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/PARITY_QUESTIONS.md +0 -0
  40. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/README.md +0 -0
  41. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/docs/athena-api.json +0 -0
  42. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/docs/athena-api.md +0 -0
  43. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/_athena_extension.py +0 -0
  44. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/_citations.py +0 -0
  45. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/_ptc.py +0 -0
  46. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/_references.py +0 -0
  47. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/action.py +0 -0
  48. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/__init__.py +0 -0
  49. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/axis.py +0 -0
  50. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/category.py +0 -0
  51. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/chart.py +0 -0
  52. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/data.py +0 -0
  53. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/datalabel.py +0 -0
  54. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/legend.py +0 -0
  55. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/marker.py +0 -0
  56. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/plot.py +0 -0
  57. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/point.py +0 -0
  58. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/series.py +0 -0
  59. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/chart/xlsx.py +0 -0
  60. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/client.py +0 -0
  61. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/decorators.py +0 -0
  62. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/dml/__init__.py +0 -0
  63. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/dml/chtfmt.py +0 -0
  64. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/dml/color.py +0 -0
  65. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/dml/effect.py +0 -0
  66. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/dml/fill.py +0 -0
  67. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/dml/line.py +0 -0
  68. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/docgen.py +0 -0
  69. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/__init__.py +0 -0
  70. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/action.py +0 -0
  71. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/chart.py +0 -0
  72. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/dml.py +0 -0
  73. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/lang.py +0 -0
  74. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/shapes.py +0 -0
  75. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/enum/text.py +0 -0
  76. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/errors.py +0 -0
  77. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/exc.py +0 -0
  78. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/generated/__init__.py +0 -0
  79. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/generated/autoshape_types.py +0 -0
  80. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/kit.py +0 -0
  81. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/media.py +0 -0
  82. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/package.py +0 -0
  83. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/__init__.py +0 -0
  84. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/_base.py +0 -0
  85. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/chart.py +0 -0
  86. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/coreprops.py +0 -0
  87. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/embeddedpackage.py +0 -0
  88. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/image.py +0 -0
  89. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/media.py +0 -0
  90. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/parts/slide.py +0 -0
  91. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/autoshape.py +0 -0
  92. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/base.py +0 -0
  93. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/connector.py +0 -0
  94. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/freeform.py +0 -0
  95. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/graphfrm.py +0 -0
  96. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/group.py +0 -0
  97. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/picture.py +0 -0
  98. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/placeholder.py +0 -0
  99. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shapes/shapetree.py +0 -0
  100. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/shared.py +0 -0
  101. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/slide.py +0 -0
  102. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/spec.py +0 -0
  103. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/table.py +0 -0
  104. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/text/fonts.py +0 -0
  105. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/text/layout.py +0 -0
  106. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/text/text.py +0 -0
  107. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/types.py +0 -0
  108. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/typing.py +0 -0
  109. {athena_python_pptx-0.16.1 → athena_python_pptx-0.17.1}/pptx/util.py +0 -0
@@ -69,7 +69,8 @@ A small number of REST-SDK-specific departures are documented in
69
69
 
70
70
  These standard python-pptx members are omitted because a REST SDK has no
71
71
  local XML / OPC parts:
72
- - `Shape.element`, `Shape.part` — XML element access.
72
+ - `Shape.part` — package part access. (`Shape.element` is implemented: the
73
+ shape's element, for python-pptx's shape-tree recipes; see Architecture.)
73
74
  - `TextFrame.part`, `Paragraph.part`, `Run.part` — package part access.
74
75
  - `FillFormat.from_fill_parent()`, `ColorFormat.from_colorchoice_parent()` — internal XML constructors.
75
76
  - `Font.fill` — Font fill format (uncommon; use `font.color` for text color).
@@ -119,7 +120,33 @@ install fails the parity tests rather than skipping them.
119
120
 
120
121
  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:
121
122
 
122
- - No `_element`, `_sp`, `_spTree` XML attributes — these don't exist
123
+ - No local OOXML package. python-pptx's private XML attributes (`_tbl`,
124
+ `_tc`, `series._element`, ...) raise `UnsupportedAttributeError` naming the
125
+ supported API (`pptx/_unsupported.py`), except where common recipes map onto
126
+ commands. Those edits are held until complete
127
+ (`CommandBuffer.hold_pending_edit`):
128
+ - `prs.slides._sldIdLst` and `prs.part.drop_rel()` for slide delete and move
129
+ (`pptx/oxml/presentation.py`, tests in `tests/test_sldidlst_compat.py`);
130
+ - `shape._element` / `shape.element` (with `_sp`, `_pic`, `_graphicFrame`,
131
+ `_grpSp`) and `slide.shapes._spTree` for shape delete, z-order and
132
+ duplicate, sent as `DeleteShape`, `SetShapeZOrder` and `CloneShape`
133
+ (`pptx/oxml/shapes/`, tests in `tests/test_shape_element_compat.py`);
134
+ - a shape's text: `text_frame._txBody` / `_bodyPr`, `paragraph._p`,
135
+ `run._r` and `font._rPr` are real lxml elements of python-pptx's classes
136
+ (`pptx/oxml/text.py` on the ported `xmlchemy` / `simpletypes`), a mirror of
137
+ the deck that is diffed into the public API's commands when the change
138
+ resolves. `pptx/oxml/_mirror.py` holds the reusable lifecycle (hold,
139
+ resolve, in-place re-sync, late edits, the access hooks that resolve before
140
+ a public call), `pptx/oxml/_txbody.py` the text translation; tests in
141
+ `tests/test_text_xml_compat.py`. A change no command carries raises
142
+ naming the XML path; never drop one silently.
143
+ - a table: `table._tbl`, `cell._tc`, `row._tr` and `column._gridCol`
144
+ (`pptx/oxml/table.py`), the same kind of mirror (`pptx/oxml/_tbl.py`):
145
+ cell borders, margins, anchor and fill, row heights, column widths, the
146
+ look flags, and row / column removal. It captures rows by their
147
+ serialization and re-syncs only the cells an API write touched, so a
148
+ recipe that walks a large table cell by cell stays linear; tests in
149
+ `tests/test_table_xml_compat.py`.
123
150
  - Operations are batched via `with prs.batch():` context manager
124
151
  - State is synchronized via the PPTX Studio collaboration layer (Yjs)
125
152
  - **Programmatic Tool Calling (PTC):** every `CommandBuffer.add` and
@@ -91,7 +91,7 @@ via Tranches R–W:
91
91
  | SlideLayout | PASS (+`index`) |
92
92
  | SlideMaster | PASS |
93
93
  | SlideShapes | PASS (+SDK REST helpers; `add_connector` accepts optional `begin_shape`/`end_shape`) |
94
- | Table | PASS (+SDK REST helpers + inherited `Shape.element` proxy) |
94
+ | Table | PASS (+SDK REST helpers + inherited `Shape.element`, its frame's `p:graphicFrame` element) |
95
95
  | `_Cell` | PASS (+SDK REST helpers) |
96
96
  | PlaceholderFormat | PASS (+convenience type-check booleans + custom-prompt/transform fields) |
97
97
  | Chart | PASS (`category_axis` / `value_axis` / `legend` / `font` / `chart_style` added in v0.1.68; replaces `UnsupportedFeatureError` stubs) |
@@ -2,6 +2,167 @@
2
2
 
3
3
  All notable changes to `athena-python-pptx` are documented in this file.
4
4
 
5
+ ## 0.17.1
6
+
7
+ - `Table.table_style` returns the table's style GUID, the text of
8
+ `table._tbl`'s `a:tableStyleId`, which python-pptx code reads: an uploaded
9
+ table's from the deck, or the one `add_table()` / `add_linked_table()` gave a
10
+ table the SDK added; None for a table without one. It returned None for every
11
+ table (it read a snapshot field the studio never sends). It stays read-only:
12
+ the studio has no command that changes an existing table's style.
13
+ - `shape._element.txBody` (and `shape._sp.txBody`) of an autoshape, text box
14
+ or placeholder is its text frame's `_txBody`, the same lxml element
15
+ (`sp.txBody is shape.text_frame._txBody`, as in python-pptx), and
16
+ `sp.get_or_add_txBody()` returns it; it raised `UnsupportedAttributeError`.
17
+ Edits through it are held and sent as the text's commands, as edits through
18
+ `text_frame._txBody` are, so python-pptx's "for every shape, rewrite each
19
+ `a:t` of `shape._element.txBody`" recipe works. As in python-pptx it is None
20
+ for a picture's, graphic frame's, connector's or group's element, and for a
21
+ shape without a text frame.
22
+ - A table cell's cleared edge (`cell.border_left.no_border()`, or
23
+ `SetCellBorders` with `None`) reads in `cell._tc` as an `a:lnX` holding
24
+ `a:noFill` on every table, as the studio now stores and exports it (an
25
+ uploaded table's read as no `a:lnX`, which in PowerPoint is the table
26
+ style's line). A width or dash written after `no_border()` keeps the edge
27
+ without a line, as it does an `a:noFill` an XML recipe wrote, and an RGB
28
+ color draws it again (it sent a width-only edge). Removing a cell's `a:lnX`
29
+ through `cell._tc` raises
30
+ `UnsupportedAttributeError`: in python-pptx that hands the edge back to the
31
+ table style, which no studio command does (it cleared the edge before). For
32
+ no line, keep the `a:lnX` and give it an `a:noFill`, or call `no_border()`.
33
+
34
+ ## 0.17.0
35
+
36
+ - python-pptx's slide delete and move recipes work. They edit
37
+ `prs.slides._sldIdLst`, which raised `UnsupportedAttributeError`; it is now
38
+ python-pptx's `p:sldIdLst` (`pptx.oxml.presentation.CT_SlideIdList`):
39
+ - `xml_slides.remove(entry)` (or `del xml_slides[i]`) deletes the slide;
40
+ - re-inserting it (`insert`, `append`, `addnext`, `addprevious`) moves it
41
+ instead, and inserting a slide that is still in the list moves it, as
42
+ lxml does;
43
+ - `prs.part.drop_rel(entry.rId)` then `del` deletes the slide once;
44
+ - an entry's `id` is its slide's `slide_id`, as in python-pptx, so the
45
+ recipe that finds a slide's entry by comparing the two works.
46
+
47
+ Edits are held until the change is complete, then sent as deletes and one
48
+ reorder, so a move never deletes its slide first and "remove every slide,
49
+ then append them in a new order" is a single reorder. Anything that reads
50
+ slide positions sends an open change first (the next command, `prs.slides`,
51
+ a slide's index, a flush, a refresh, a `batch()` or `with prs:` block), so
52
+ later commands address slides by their new positions; a block that raises
53
+ drops the edits made inside it. The bulk-delete guard applies. Other lxml
54
+ members, and other OPC members of `prs.part`, raise naming the supported
55
+ API.
56
+ - A slide proxy held from before its slide was deleted (`prs.slides.delete`
57
+ or the recipes above) raises `ConflictError` on an edit. It wrote to the
58
+ slide that had taken its position.
59
+ - python-pptx's shape delete, z-order and duplicate recipes work.
60
+ `shape._element` and python-pptx's public `shape.element` (both raised or
61
+ answered with a delete-only proxy) are the shape's element, with python-pptx's
62
+ `_sp`, `_pic`, `_graphicFrame` and `_grpSp` aliases, and
63
+ `slide.shapes._spTree` is the slide's shape tree (`pptx.oxml.shapes`:
64
+ `CT_Shape`, `CT_Picture`, `CT_GraphicalObjectFrame`, `CT_Connector`,
65
+ `CT_GroupShape`). As in python-pptx, `group.shapes._spTree` is the group's
66
+ element and the tree holds `p:nvGrpSpPr` and `p:grpSpPr` first.
67
+ - `sp.getparent().remove(sp)` (or `spTree.remove(el)`, `del spTree[i]`)
68
+ deletes the shape or placeholder;
69
+ - putting it back (`insert`, `append`, `addnext`, `addprevious`), or
70
+ inserting a shape that is still in the tree, moves it, as lxml does, sent
71
+ as the fewest `SetShapeZOrder` steps that give exactly the new order:
72
+ `spTree.insert(2, el)` sends it to the back;
73
+ - inserting `copy.deepcopy(shape._element)` into any slide's tree clones the
74
+ shape there in place (`CloneShape` with no offset), and a copy never
75
+ inserted sends nothing.
76
+
77
+ Edits are held until the change is complete, as for `_sldIdLst`, so a move
78
+ never deletes its shape; the next command, a flush or refresh, and reading
79
+ `slide.shapes` send them (clones, then deletes, then z-order). An element's
80
+ tag, parent and siblings, and its `p:cNvPr` id and name, read as in
81
+ python-pptx. Other lxml members, property-level XML (`spPr`, `txBody`),
82
+ moving a shape to another slide or group, and copies changed after
83
+ `deepcopy()` raise naming the supported API.
84
+ - A held XML-recipe change that completes sends its commands without forcing
85
+ the other held change, so a shape move made during a slide move no longer
86
+ turns the slide's removal into a delete, and a clone lands on the slide it
87
+ named.
88
+ - A group member's `bring_to_front()` / `send_to_back()` / `bring_forward()` /
89
+ `send_backward()` and `delete()` update `group.shapes`, which kept the old
90
+ order and the deleted member until a refresh.
91
+ - python-pptx's text XML recipes work: bullets on and off, numbered lists,
92
+ hanging indents, superscript, letter spacing, line spacing and autofit
93
+ through XML. `text_frame._txBody` / `_bodyPr`, `paragraph._p` / `_pPr` /
94
+ `_defRPr`, `run._r` / `_rPr` and `font._rPr` raised
95
+ `UnsupportedAttributeError`; they are now real lxml elements of
96
+ python-pptx's classes (`pptx.oxml.text`), one tree per text frame built from
97
+ the deck, with python-pptx's helpers (`get_or_add_pPr()`,
98
+ `insert_element_before()`, `pPr.lvl`, ...). What a recipe changes is sent
99
+ through the public API's setters:
100
+ - `a:pPr` as `SetParagraphStyle` (indents, level, alignment, spacing, bullet
101
+ color and the `•` / `◦` / `▪`, `a:buNone` and `arabicPeriod` bullets),
102
+ its `a:defRPr` as `paragraph.font`;
103
+ - `a:rPr` as `SetRunStyle` (bold, italic, underline, size, strike,
104
+ baseline, caps, spacing, language, color, highlight, typeface);
105
+ - `a:bodyPr` as `SetTextFrameProperties` (wrap, anchor, insets, autofit);
106
+ - run text, appended runs, breaks and paragraphs, and trailing removals
107
+ through the existing text commands.
108
+
109
+ The change is sent before the next command, explicit flush, refresh or API
110
+ call on that text (never by the idle timer), so a later API write wins and
111
+ reads see the edit; like `_sldIdLst`'s, a `prs.batch()` or `with prs:` block
112
+ that raises drops the changes made inside it. Requested again, the tree shows
113
+ the deck's state in place. Anything no command carries raises
114
+ `UnsupportedAttributeError` naming the XML path and the supported API, and
115
+ is dropped. `pptx.oxml.parse_xml` and `pptx.oxml.xmlchemy.OxmlElement` build
116
+ real elements instead of raising. `lxml` is now a dependency.
117
+ - `str()` of a length (`Emu`, `Pt`, ...) and f-strings give the EMU integer, as
118
+ in python-pptx, so `pPr.set("marL", str(Emu(Pt(18))))` writes `"228600"`
119
+ (it wrote `"Emu(228600)"`); `repr()` is unchanged.
120
+ - A run's color writes merged into one command drop an earlier brightness the
121
+ later write does not set, as the studio does when it applies them one by
122
+ one: `font.color.brightness = 0.4` then `font.color.brightness = 0` (or a
123
+ new `theme_color`) sent the 0.4.
124
+ - python-pptx's table XML recipes work: cell borders, row heights, column
125
+ widths, the look flags, a cell's margins, anchor and fill through XML, and
126
+ the row and column delete recipes. `table._tbl`, `cell._tc`, `row._tr` and
127
+ `column._gridCol` raised `UnsupportedAttributeError`; they are now real lxml
128
+ elements of python-pptx's classes (`pptx.oxml.table`), one tree per table
129
+ built from the deck. What a recipe changes is sent through the public API's
130
+ setters:
131
+ - a cell's `a:lnL` / `a:lnR` / `a:lnT` / `a:lnB` as one `SetCellBorders`
132
+ (`w`, an RGB or no line, the dash), which `cell.border_*` then reads (a
133
+ later `width` or `dash_style` write keeps the no line or theme color);
134
+ - `marL` / `marR` / `marT` / `marB` as `SetCellMargins`, `anchor` as
135
+ `SetCellAnchor`, an RGB `a:solidFill` or `a:noFill` as `SetTableCell`;
136
+ - `a:tr/@h` as `SetRowHeight`, `a:gridCol/@w` as `SetColWidth`, the
137
+ `a:tblPr` flags as one `SetTableLook`;
138
+ - a removed `a:tr` as `DeleteTableRow`, a removed column (its `a:gridCol`
139
+ and its `a:tc` in every row) as `DeleteTableColumn`.
140
+
141
+ The studio cannot change an existing table's style, so an edit to
142
+ `a:tableStyleId` raises `UnsupportedAttributeError` naming what can change;
143
+ so do merges, a cell's text, rows or columns added through XML and the
144
+ other properties no command carries. A border's `cap` is ignored with a
145
+ warning. Walking the table (`table.cell(r, c)`, `row.cells`) does not send a
146
+ pending change, so a recipe that edits every cell sends its edits together.
147
+ - `table.delete_row(i)` / `table.delete_column(i)` drop the row's height (the
148
+ column's width) locally, as the server does: the rows below kept reading
149
+ the height of the row after them.
150
+ - A row or column proxy (`table.rows[i]`, `table.columns[i]`) kept across
151
+ rows or columns inserted or removed before it keeps meaning its own row or
152
+ column, as python-pptx's does: it read and wrote the one that took its
153
+ index. One whose row or column was removed raises `IndexError`.
154
+ - A chart data label's `text_frame` has python-pptx's `paragraphs`: one
155
+ paragraph over the label's text whose runs and font are the label's, so
156
+ `point.data_label.text_frame.paragraphs[0].runs[0].font.bold = True` and
157
+ `paragraphs[0].text = ...` work. `paragraphs` raised `AttributeError`.
158
+ - Table cell text frames have python-pptx's `vertical_anchor` and margins
159
+ (they write the cell's own), and `word_wrap` / `auto_size`; cell paragraphs
160
+ have `line_spacing`, `space_before`, `space_after` and `level`. A
161
+ one-paragraph cell cannot apply those last members, so a write warns once
162
+ per presentation and keeps the value for read-after-write, instead of
163
+ raising `AttributeError`, which production scripts hit. An uploaded cell's
164
+ paragraph reads its own level and spacing (`a:pPr`), as python-pptx does.
165
+
5
166
  ## 0.16.1 — `Presentation.upload()` and `create(pptx=...)` report the deck they create
6
167
 
7
168
  - `Presentation.upload()` emits the `CreatePresentation` tool-call event with
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: athena-python-pptx
3
- Version: 0.16.1
3
+ Version: 0.17.1
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
@@ -22,6 +22,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.10
24
24
  Requires-Dist: athena-references>=0.1.0
25
+ Requires-Dist: lxml>=4.9.0
25
26
  Requires-Dist: requests>=2.28.0
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: mypy>=1.0; extra == 'dev'