openpyxl2 3.2.0b2__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 (215) hide show
  1. openpyxl2-3.2.0b2/AUTHORS.rst +98 -0
  2. openpyxl2-3.2.0b2/LICENCE.rst +23 -0
  3. openpyxl2-3.2.0b2/MANIFEST.in +8 -0
  4. openpyxl2-3.2.0b2/PKG-INFO +125 -0
  5. openpyxl2-3.2.0b2/PUBLISHING.md +139 -0
  6. openpyxl2-3.2.0b2/README.md +244 -0
  7. openpyxl2-3.2.0b2/README.pypi.md +83 -0
  8. openpyxl2-3.2.0b2/README.ru.md +244 -0
  9. openpyxl2-3.2.0b2/openpyxl/__init__.py +19 -0
  10. openpyxl2-3.2.0b2/openpyxl/_constants.py +12 -0
  11. openpyxl2-3.2.0b2/openpyxl/cell/__init__.py +5 -0
  12. openpyxl2-3.2.0b2/openpyxl/cell/_writer.py +121 -0
  13. openpyxl2-3.2.0b2/openpyxl/cell/cell.py +417 -0
  14. openpyxl2-3.2.0b2/openpyxl/cell/coordinate.py +32 -0
  15. openpyxl2-3.2.0b2/openpyxl/cell/read_only.py +98 -0
  16. openpyxl2-3.2.0b2/openpyxl/cell/rich_text.py +188 -0
  17. openpyxl2-3.2.0b2/openpyxl/cell/text.py +168 -0
  18. openpyxl2-3.2.0b2/openpyxl/chart/_3d.py +85 -0
  19. openpyxl2-3.2.0b2/openpyxl/chart/__init__.py +20 -0
  20. openpyxl2-3.2.0b2/openpyxl/chart/_chart.py +194 -0
  21. openpyxl2-3.2.0b2/openpyxl/chart/area_chart.py +81 -0
  22. openpyxl2-3.2.0b2/openpyxl/chart/axis.py +389 -0
  23. openpyxl2-3.2.0b2/openpyxl/chart/bar_chart.py +144 -0
  24. openpyxl2-3.2.0b2/openpyxl/chart/bubble_chart.py +61 -0
  25. openpyxl2-3.2.0b2/openpyxl/chart/chartspace.py +194 -0
  26. openpyxl2-3.2.0b2/openpyxl/chart/data_source.py +175 -0
  27. openpyxl2-3.2.0b2/openpyxl/chart/descriptors.py +34 -0
  28. openpyxl2-3.2.0b2/openpyxl/chart/error_bar.py +69 -0
  29. openpyxl2-3.2.0b2/openpyxl/chart/label.py +134 -0
  30. openpyxl2-3.2.0b2/openpyxl/chart/layout.py +68 -0
  31. openpyxl2-3.2.0b2/openpyxl/chart/legend.py +58 -0
  32. openpyxl2-3.2.0b2/openpyxl/chart/line_chart.py +129 -0
  33. openpyxl2-3.2.0b2/openpyxl/chart/marker.py +89 -0
  34. openpyxl2-3.2.0b2/openpyxl/chart/picture.py +38 -0
  35. openpyxl2-3.2.0b2/openpyxl/chart/pie_chart.py +136 -0
  36. openpyxl2-3.2.0b2/openpyxl/chart/pivot.py +52 -0
  37. openpyxl2-3.2.0b2/openpyxl/chart/plotarea.py +151 -0
  38. openpyxl2-3.2.0b2/openpyxl/chart/print_settings.py +46 -0
  39. openpyxl2-3.2.0b2/openpyxl/chart/radar_chart.py +44 -0
  40. openpyxl2-3.2.0b2/openpyxl/chart/reader.py +28 -0
  41. openpyxl2-3.2.0b2/openpyxl/chart/reference.py +101 -0
  42. openpyxl2-3.2.0b2/openpyxl/chart/scatter_chart.py +53 -0
  43. openpyxl2-3.2.0b2/openpyxl/chart/series.py +239 -0
  44. openpyxl2-3.2.0b2/openpyxl/chart/series_factory.py +47 -0
  45. openpyxl2-3.2.0b2/openpyxl/chart/shapes.py +103 -0
  46. openpyxl2-3.2.0b2/openpyxl/chart/stock_chart.py +46 -0
  47. openpyxl2-3.2.0b2/openpyxl/chart/surface_chart.py +85 -0
  48. openpyxl2-3.2.0b2/openpyxl/chart/text.py +57 -0
  49. openpyxl2-3.2.0b2/openpyxl/chart/title.py +70 -0
  50. openpyxl2-3.2.0b2/openpyxl/chart/trendline.py +110 -0
  51. openpyxl2-3.2.0b2/openpyxl/chart/updown_bars.py +20 -0
  52. openpyxl2-3.2.0b2/openpyxl/chartsheet/__init__.py +2 -0
  53. openpyxl2-3.2.0b2/openpyxl/chartsheet/chartsheet.py +109 -0
  54. openpyxl2-3.2.0b2/openpyxl/chartsheet/custom.py +50 -0
  55. openpyxl2-3.2.0b2/openpyxl/chartsheet/properties.py +19 -0
  56. openpyxl2-3.2.0b2/openpyxl/chartsheet/protection.py +44 -0
  57. openpyxl2-3.2.0b2/openpyxl/chartsheet/publish.py +63 -0
  58. openpyxl2-3.2.0b2/openpyxl/chartsheet/relation.py +95 -0
  59. openpyxl2-3.2.0b2/openpyxl/chartsheet/views.py +42 -0
  60. openpyxl2-3.2.0b2/openpyxl/comments/__init__.py +2 -0
  61. openpyxl2-3.2.0b2/openpyxl/comments/author.py +13 -0
  62. openpyxl2-3.2.0b2/openpyxl/comments/comment_sheet.py +188 -0
  63. openpyxl2-3.2.0b2/openpyxl/comments/comments.py +56 -0
  64. openpyxl2-3.2.0b2/openpyxl/comments/shape_writer.py +111 -0
  65. openpyxl2-3.2.0b2/openpyxl/compat/__init__.py +49 -0
  66. openpyxl2-3.2.0b2/openpyxl/compat/abc.py +9 -0
  67. openpyxl2-3.2.0b2/openpyxl/compat/numbers.py +43 -0
  68. openpyxl2-3.2.0b2/openpyxl/compat/product.py +16 -0
  69. openpyxl2-3.2.0b2/openpyxl/compat/singleton.py +38 -0
  70. openpyxl2-3.2.0b2/openpyxl/compat/strings.py +24 -0
  71. openpyxl2-3.2.0b2/openpyxl/connection/__init__.py +1 -0
  72. openpyxl2-3.2.0b2/openpyxl/connection/connections.py +449 -0
  73. openpyxl2-3.2.0b2/openpyxl/descriptors/__init__.py +55 -0
  74. openpyxl2-3.2.0b2/openpyxl/descriptors/base.py +249 -0
  75. openpyxl2-3.2.0b2/openpyxl/descriptors/container.py +34 -0
  76. openpyxl2-3.2.0b2/openpyxl/descriptors/excel.py +101 -0
  77. openpyxl2-3.2.0b2/openpyxl/descriptors/namespace.py +11 -0
  78. openpyxl2-3.2.0b2/openpyxl/descriptors/nested.py +116 -0
  79. openpyxl2-3.2.0b2/openpyxl/descriptors/sequence.py +127 -0
  80. openpyxl2-3.2.0b2/openpyxl/descriptors/serialisable.py +208 -0
  81. openpyxl2-3.2.0b2/openpyxl/descriptors/slots.py +18 -0
  82. openpyxl2-3.2.0b2/openpyxl/drawing/__init__.py +2 -0
  83. openpyxl2-3.2.0b2/openpyxl/drawing/anchor.py +166 -0
  84. openpyxl2-3.2.0b2/openpyxl/drawing/colors.py +681 -0
  85. openpyxl2-3.2.0b2/openpyxl/drawing/connector.py +121 -0
  86. openpyxl2-3.2.0b2/openpyxl/drawing/drawing.py +78 -0
  87. openpyxl2-3.2.0b2/openpyxl/drawing/effect.py +364 -0
  88. openpyxl2-3.2.0b2/openpyxl/drawing/fill.py +456 -0
  89. openpyxl2-3.2.0b2/openpyxl/drawing/geometry.py +764 -0
  90. openpyxl2-3.2.0b2/openpyxl/drawing/graphic.py +140 -0
  91. openpyxl2-3.2.0b2/openpyxl/drawing/image.py +95 -0
  92. openpyxl2-3.2.0b2/openpyxl/drawing/legacy.py +24 -0
  93. openpyxl2-3.2.0b2/openpyxl/drawing/line.py +142 -0
  94. openpyxl2-3.2.0b2/openpyxl/drawing/picture.py +126 -0
  95. openpyxl2-3.2.0b2/openpyxl/drawing/properties.py +158 -0
  96. openpyxl2-3.2.0b2/openpyxl/drawing/relation.py +13 -0
  97. openpyxl2-3.2.0b2/openpyxl/drawing/spreadsheet_drawing.py +269 -0
  98. openpyxl2-3.2.0b2/openpyxl/drawing/text.py +767 -0
  99. openpyxl2-3.2.0b2/openpyxl/drawing/xdr.py +30 -0
  100. openpyxl2-3.2.0b2/openpyxl/formatting/__init__.py +2 -0
  101. openpyxl2-3.2.0b2/openpyxl/formatting/formatting.py +95 -0
  102. openpyxl2-3.2.0b2/openpyxl/formatting/rule.py +383 -0
  103. openpyxl2-3.2.0b2/openpyxl/formula/__init__.py +2 -0
  104. openpyxl2-3.2.0b2/openpyxl/formula/structural.py +178 -0
  105. openpyxl2-3.2.0b2/openpyxl/formula/tokenizer.py +444 -0
  106. openpyxl2-3.2.0b2/openpyxl/formula/translate.py +179 -0
  107. openpyxl2-3.2.0b2/openpyxl/packaging/__init__.py +3 -0
  108. openpyxl2-3.2.0b2/openpyxl/packaging/core.py +120 -0
  109. openpyxl2-3.2.0b2/openpyxl/packaging/custom.py +236 -0
  110. openpyxl2-3.2.0b2/openpyxl/packaging/extended.py +131 -0
  111. openpyxl2-3.2.0b2/openpyxl/packaging/interface.py +49 -0
  112. openpyxl2-3.2.0b2/openpyxl/packaging/manifest.py +196 -0
  113. openpyxl2-3.2.0b2/openpyxl/packaging/relationship.py +148 -0
  114. openpyxl2-3.2.0b2/openpyxl/packaging/workbook.py +189 -0
  115. openpyxl2-3.2.0b2/openpyxl/pivot/__init__.py +1 -0
  116. openpyxl2-3.2.0b2/openpyxl/pivot/cache.py +920 -0
  117. openpyxl2-3.2.0b2/openpyxl/pivot/fields.py +280 -0
  118. openpyxl2-3.2.0b2/openpyxl/pivot/record.py +81 -0
  119. openpyxl2-3.2.0b2/openpyxl/pivot/table.py +1297 -0
  120. openpyxl2-3.2.0b2/openpyxl/reader/__init__.py +1 -0
  121. openpyxl2-3.2.0b2/openpyxl/reader/drawings.py +73 -0
  122. openpyxl2-3.2.0b2/openpyxl/reader/excel.py +486 -0
  123. openpyxl2-3.2.0b2/openpyxl/reader/strings.py +38 -0
  124. openpyxl2-3.2.0b2/openpyxl/reader/workbook.py +121 -0
  125. openpyxl2-3.2.0b2/openpyxl/styles/__init__.py +15 -0
  126. openpyxl2-3.2.0b2/openpyxl/styles/alignment.py +79 -0
  127. openpyxl2-3.2.0b2/openpyxl/styles/borders.py +133 -0
  128. openpyxl2-3.2.0b2/openpyxl/styles/builtins.py +1362 -0
  129. openpyxl2-3.2.0b2/openpyxl/styles/cell_style.py +203 -0
  130. openpyxl2-3.2.0b2/openpyxl/styles/colors.py +213 -0
  131. openpyxl2-3.2.0b2/openpyxl/styles/differential.py +81 -0
  132. openpyxl2-3.2.0b2/openpyxl/styles/fills.py +228 -0
  133. openpyxl2-3.2.0b2/openpyxl/styles/fonts.py +146 -0
  134. openpyxl2-3.2.0b2/openpyxl/styles/named_styles.py +255 -0
  135. openpyxl2-3.2.0b2/openpyxl/styles/numbers.py +181 -0
  136. openpyxl2-3.2.0b2/openpyxl/styles/protection.py +17 -0
  137. openpyxl2-3.2.0b2/openpyxl/styles/proxy.py +56 -0
  138. openpyxl2-3.2.0b2/openpyxl/styles/styleable.py +217 -0
  139. openpyxl2-3.2.0b2/openpyxl/styles/stylesheet.py +263 -0
  140. openpyxl2-3.2.0b2/openpyxl/styles/table.py +100 -0
  141. openpyxl2-3.2.0b2/openpyxl/utils/__init__.py +12 -0
  142. openpyxl2-3.2.0b2/openpyxl/utils/bound_dictionary.py +24 -0
  143. openpyxl2-3.2.0b2/openpyxl/utils/cell.py +229 -0
  144. openpyxl2-3.2.0b2/openpyxl/utils/dataframe.py +73 -0
  145. openpyxl2-3.2.0b2/openpyxl/utils/datetime.py +147 -0
  146. openpyxl2-3.2.0b2/openpyxl/utils/escape.py +37 -0
  147. openpyxl2-3.2.0b2/openpyxl/utils/exceptions.py +56 -0
  148. openpyxl2-3.2.0b2/openpyxl/utils/formulas.py +380 -0
  149. openpyxl2-3.2.0b2/openpyxl/utils/indexed_list.py +49 -0
  150. openpyxl2-3.2.0b2/openpyxl/utils/inference.py +66 -0
  151. openpyxl2-3.2.0b2/openpyxl/utils/protection.py +22 -0
  152. openpyxl2-3.2.0b2/openpyxl/utils/units.py +116 -0
  153. openpyxl2-3.2.0b2/openpyxl/volatile/__init__.py +1 -0
  154. openpyxl2-3.2.0b2/openpyxl/volatile/volatile.py +79 -0
  155. openpyxl2-3.2.0b2/openpyxl/workbook/__init__.py +2 -0
  156. openpyxl2-3.2.0b2/openpyxl/workbook/_writer.py +185 -0
  157. openpyxl2-3.2.0b2/openpyxl/workbook/child.py +147 -0
  158. openpyxl2-3.2.0b2/openpyxl/workbook/defined_name.py +185 -0
  159. openpyxl2-3.2.0b2/openpyxl/workbook/external_link/__init__.py +2 -0
  160. openpyxl2-3.2.0b2/openpyxl/workbook/external_link/external.py +135 -0
  161. openpyxl2-3.2.0b2/openpyxl/workbook/external_reference.py +11 -0
  162. openpyxl2-3.2.0b2/openpyxl/workbook/function_group.py +24 -0
  163. openpyxl2-3.2.0b2/openpyxl/workbook/properties.py +143 -0
  164. openpyxl2-3.2.0b2/openpyxl/workbook/protection.py +180 -0
  165. openpyxl2-3.2.0b2/openpyxl/workbook/smart_tags.py +37 -0
  166. openpyxl2-3.2.0b2/openpyxl/workbook/views.py +142 -0
  167. openpyxl2-3.2.0b2/openpyxl/workbook/web.py +98 -0
  168. openpyxl2-3.2.0b2/openpyxl/workbook/workbook.py +428 -0
  169. openpyxl2-3.2.0b2/openpyxl/worksheet/__init__.py +1 -0
  170. openpyxl2-3.2.0b2/openpyxl/worksheet/_read_only.py +173 -0
  171. openpyxl2-3.2.0b2/openpyxl/worksheet/_reader.py +482 -0
  172. openpyxl2-3.2.0b2/openpyxl/worksheet/_structural_edit.py +428 -0
  173. openpyxl2-3.2.0b2/openpyxl/worksheet/_write_only.py +138 -0
  174. openpyxl2-3.2.0b2/openpyxl/worksheet/_writer.py +384 -0
  175. openpyxl2-3.2.0b2/openpyxl/worksheet/cell_range.py +497 -0
  176. openpyxl2-3.2.0b2/openpyxl/worksheet/cell_watch.py +23 -0
  177. openpyxl2-3.2.0b2/openpyxl/worksheet/controls.py +323 -0
  178. openpyxl2-3.2.0b2/openpyxl/worksheet/copier.py +57 -0
  179. openpyxl2-3.2.0b2/openpyxl/worksheet/custom.py +23 -0
  180. openpyxl2-3.2.0b2/openpyxl/worksheet/datavalidation.py +211 -0
  181. openpyxl2-3.2.0b2/openpyxl/worksheet/dimensions.py +315 -0
  182. openpyxl2-3.2.0b2/openpyxl/worksheet/drawing.py +11 -0
  183. openpyxl2-3.2.0b2/openpyxl/worksheet/errors.py +74 -0
  184. openpyxl2-3.2.0b2/openpyxl/worksheet/filters.py +558 -0
  185. openpyxl2-3.2.0b2/openpyxl/worksheet/formula.py +47 -0
  186. openpyxl2-3.2.0b2/openpyxl/worksheet/header_footer.py +260 -0
  187. openpyxl2-3.2.0b2/openpyxl/worksheet/hyperlink.py +40 -0
  188. openpyxl2-3.2.0b2/openpyxl/worksheet/merge.py +150 -0
  189. openpyxl2-3.2.0b2/openpyxl/worksheet/ole.py +126 -0
  190. openpyxl2-3.2.0b2/openpyxl/worksheet/page.py +167 -0
  191. openpyxl2-3.2.0b2/openpyxl/worksheet/pagebreak.py +75 -0
  192. openpyxl2-3.2.0b2/openpyxl/worksheet/picture.py +8 -0
  193. openpyxl2-3.2.0b2/openpyxl/worksheet/print_settings.py +165 -0
  194. openpyxl2-3.2.0b2/openpyxl/worksheet/properties.py +91 -0
  195. openpyxl2-3.2.0b2/openpyxl/worksheet/protection.py +148 -0
  196. openpyxl2-3.2.0b2/openpyxl/worksheet/related.py +13 -0
  197. openpyxl2-3.2.0b2/openpyxl/worksheet/scenario.py +80 -0
  198. openpyxl2-3.2.0b2/openpyxl/worksheet/smart_tag.py +57 -0
  199. openpyxl2-3.2.0b2/openpyxl/worksheet/table.py +360 -0
  200. openpyxl2-3.2.0b2/openpyxl/worksheet/views.py +145 -0
  201. openpyxl2-3.2.0b2/openpyxl/worksheet/worksheet.py +982 -0
  202. openpyxl2-3.2.0b2/openpyxl/writer/__init__.py +1 -0
  203. openpyxl2-3.2.0b2/openpyxl/writer/excel.py +351 -0
  204. openpyxl2-3.2.0b2/openpyxl/writer/theme.py +310 -0
  205. openpyxl2-3.2.0b2/openpyxl/xml/__init__.py +44 -0
  206. openpyxl2-3.2.0b2/openpyxl/xml/constants.py +121 -0
  207. openpyxl2-3.2.0b2/openpyxl/xml/functions.py +80 -0
  208. openpyxl2-3.2.0b2/openpyxl2.egg-info/PKG-INFO +125 -0
  209. openpyxl2-3.2.0b2/openpyxl2.egg-info/SOURCES.txt +214 -0
  210. openpyxl2-3.2.0b2/openpyxl2.egg-info/dependency_links.txt +1 -0
  211. openpyxl2-3.2.0b2/openpyxl2.egg-info/requires.txt +1 -0
  212. openpyxl2-3.2.0b2/openpyxl2.egg-info/top_level.txt +1 -0
  213. openpyxl2-3.2.0b2/pyproject.toml +3 -0
  214. openpyxl2-3.2.0b2/setup.cfg +7 -0
  215. openpyxl2-3.2.0b2/setup.py +98 -0
@@ -0,0 +1,98 @@
1
+ This project was started by Eric Gazoni. In 2013 Charlie Clark became
2
+ co-maintainer of the project.
3
+
4
+ It was initially *heavily* inspired by the PHPExcel library:
5
+ http://www.phpexcel.net/
6
+
7
+ Thanks to all those who participate in the project (in alphabetical order):
8
+
9
+ * aceMueller
10
+ * Adam Lofts
11
+ * Adam Morris
12
+ * Alessandro Cucci
13
+ * Alex Gronholm
14
+ * Alexander Ley
15
+ * Alexandre Fayolle
16
+ * Amin Mirzaee
17
+ * Anders Chrigstrom
18
+ * Anthony Hayward
19
+ * Bernt R. Brenna
20
+ * Brent Hoover
21
+ * Brice Gelineau
22
+ * ccoacley
23
+ * Chi Ho Kwok
24
+ * Cory Kramer
25
+ * Day Barr
26
+ * Detlef Lannert
27
+ * Dieter Vandenbussche
28
+ * Dmitriy Chernyshov
29
+ * Dominik Geldmacher
30
+ * Don Freeman
31
+ * Eirikur Fannar Torfason
32
+ * Elias Rabel
33
+ * Eric Chlebek
34
+ * Eric Gazoni
35
+ * Eric Hurkman
36
+ * Etienne Desautels
37
+ * Felipe Ochoa
38
+ * Felix Siebeneicker
39
+ * Fumito Hamamura
40
+ * Gabi Nagy
41
+ * Gar Thompson
42
+ * Gerald Van Huffelen
43
+ * Greg Lehmann
44
+ * Heikki Junes
45
+ * Israel Barth Rubio
46
+ * Jacob Middag
47
+ * James Smagala
48
+ * JarekPS
49
+ * Jean Pierre Huart
50
+ * Jeff Holman
51
+ * John Woltman IV
52
+ * Jonathan Peirce
53
+ * Joseph Tate
54
+ * Josh Haywood
55
+ * Jun Omae
56
+ * Kay Webber
57
+ * Khchine Hamza
58
+ * Klaus Bremer
59
+ * Koert van der Veer
60
+ * Laurent Laporte
61
+ * Laurent Vasseur
62
+ * Liam Sanders
63
+ * Maarten De Paepe
64
+ * Magnus Schieder
65
+ * Mark Gemmill
66
+ * Marko Loparic
67
+ * Masato Yoshida
68
+ * Max Bolingbroke
69
+ * Nicholas Laver
70
+ * Nis Martensen
71
+ * Paul Joyce
72
+ * Paul Van Der Linden
73
+ * Philip Roche
74
+ * Primoz Godec
75
+ * ramn_se
76
+ * René Neumann
77
+ * Rick Rankin
78
+ * Samuel Loretan
79
+ * Sergey Pikhovkin
80
+ * Shekhar Gyanwali
81
+ * Shibukawa Yoshiki
82
+ * Stefan Behnel
83
+ * Stephane Bard
84
+ * Stephen Rauch
85
+ * Sven Burk
86
+ * Ted Pollari
87
+ * Thomas Nygards
88
+ * Victor Korobkovsky
89
+ * Waldemar Osuch
90
+ * Wojciech Rola
91
+ * Wolfgane Scherer
92
+ * Yaroslav Halchenko
93
+ * Yash Jhunjhunwala
94
+ * Yingjie Lan
95
+ * Leetao
96
+
97
+ Project logo designed by Eric Gazoni, font by claudeserieux
98
+ (http://www.dafont.com/profile.php?user=337503)
@@ -0,0 +1,23 @@
1
+ This software is under the MIT Licence
2
+ ======================================
3
+
4
+ Copyright (c) 2010 openpyxl
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a
7
+ copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included
15
+ in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ prune openpyxl/sample
2
+ prune openpyxl/benchmarks
3
+ prune openpyxl/develop
4
+ prune scratchpad
5
+ prune doc
6
+
7
+ include *.rst
8
+ include README.md README.ru.md README.pypi.md PUBLISHING.md
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: openpyxl2
3
+ Version: 3.2.0b2
4
+ Summary: An openpyxl fork with formula-aware row/column edits and merged-cell formatting fixes
5
+ Home-page: https://github.com/EugeneRymarev/openpyxl2
6
+ Author: See AUTHORS
7
+ Maintainer: EugeneRymarev
8
+ License: MIT
9
+ Project-URL: Documentation, https://github.com/EugeneRymarev/openpyxl2#readme
10
+ Project-URL: Source, https://github.com/EugeneRymarev/openpyxl2
11
+ Project-URL: Tracker, https://github.com/EugeneRymarev/openpyxl2/issues
12
+ Project-URL: Upstream, https://foss.heptapod.net/openpyxl/openpyxl
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Operating System :: MacOS :: MacOS X
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Requires-Python: >=3.8
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENCE.rst
29
+ Requires-Dist: et_xmlfile
30
+ Dynamic: author
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: license
36
+ Dynamic: license-file
37
+ Dynamic: maintainer
38
+ Dynamic: project-url
39
+ Dynamic: requires-dist
40
+ Dynamic: requires-python
41
+ Dynamic: summary
42
+
43
+ # openpyxl2
44
+
45
+ **openpyxl2** is an independent fork of
46
+ [openpyxl](https://foss.heptapod.net/openpyxl/openpyxl), a Python library for
47
+ reading and writing Excel `.xlsx`, `.xlsm`, `.xltx` and `.xltm` files.
48
+ It is based on the upstream 3.2 development line. This release is **3.2.0b2**
49
+ (beta), for Python **3.8 or newer**.
50
+
51
+ The distribution is named **openpyxl2**; the Python import remains **openpyxl**.
52
+ This fork is maintained separately from the original openpyxl project.
53
+
54
+ ## Installation
55
+
56
+ Use a fresh virtual environment:
57
+
58
+ ```sh
59
+ python -m venv .venv
60
+ # Windows: .venv\Scripts\activate
61
+ # Linux/macOS: source .venv/bin/activate
62
+ python -m pip install openpyxl2==3.2.0b2
63
+ ```
64
+
65
+ Do not install `openpyxl` and `openpyxl2` together: they provide the same
66
+ `openpyxl` module and can overwrite each other's files. `pip install openpyxl`
67
+ installs the original project. Dependencies that explicitly require the
68
+ `openpyxl` distribution are **not** satisfied by `openpyxl2`; check your
69
+ application's dependencies before switching.
70
+
71
+ ## What this fork changes
72
+
73
+ - Inserting and deleting rows or columns updates supported formula references,
74
+ merged ranges, defined names, print areas and row/column dimensions.
75
+ - Partially deleted merged ranges retain the surviving anchor's value and style.
76
+ - Styles assigned after merging propagate through the merged range, with borders
77
+ applied to its perimeter. Stored merged-cell styles survive save/load cycles.
78
+ - Additional fixes cover named styles, XML backends and workbook resource handling.
79
+
80
+ ```python
81
+ from openpyxl import Workbook, load_workbook
82
+
83
+ workbook = Workbook()
84
+ sheet = workbook.active
85
+ sheet.append(["Item", "Units"])
86
+ sheet.append(["Pens", 10])
87
+ sheet.append(["Folders", 20])
88
+ sheet["B4"] = "=SUM(B2:B3)"
89
+ sheet.insert_rows(2)
90
+ assert sheet["B5"].value == "=SUM(B3:B4)"
91
+ workbook.save("sales.xlsx")
92
+
93
+ restored = load_workbook("sales.xlsx")
94
+ assert restored.active["B5"].value == "=SUM(B3:B4)"
95
+ restored.close()
96
+ ```
97
+
98
+ ## Scope and compatibility
99
+
100
+ Structural edits support direct A1 formula references, including absolute and
101
+ mixed references. They do not update every Excel feature: tables, charts, pivot
102
+ tables, validation, conditional formatting and external workbook references
103
+ require separate handling. Unsupported formula constructs can raise
104
+ `FormulaTranslationError` before an edit is applied. The library rewrites
105
+ formulas but does not calculate their results.
106
+
107
+ The fork removes the `openpyxl.open` alias; use `openpyxl.load_workbook`.
108
+ Named-style equality compares names. See the
109
+ [full feature list and limitations](https://github.com/EugeneRymarev/openpyxl2#readme)
110
+ and the [Russian README](https://github.com/EugeneRymarev/openpyxl2/blob/master/README.ru.md)
111
+ for details and examples.
112
+
113
+ `et_xmlfile` is a required dependency and is installed automatically. Optional
114
+ packages include `lxml` for XML processing, `Pillow` for images and `defusedxml`
115
+ for XML protection when handling untrusted files.
116
+
117
+ ## Project and license
118
+
119
+ - [Source and examples](https://github.com/EugeneRymarev/openpyxl2)
120
+ - [Report a fork issue](https://github.com/EugeneRymarev/openpyxl2/issues)
121
+ - [Upstream API documentation](https://openpyxl.pages.heptapod.net/openpyxl/)
122
+ - [Original authors and contributors](https://github.com/EugeneRymarev/openpyxl2/blob/master/AUTHORS.rst)
123
+
124
+ Distributed under the MIT license. The original openpyxl copyright notice and
125
+ license are preserved in `LICENCE.rst` in the distribution.
@@ -0,0 +1,139 @@
1
+ # Публикация openpyxl2 в PyPI
2
+
3
+ Подготовлены следующие данные:
4
+
5
+ | Поле | Значение |
6
+ | --- | --- |
7
+ | Имя дистрибутива в PyPI | `openpyxl2` |
8
+ | Версия | `3.2.0b2` — бета, сохранена из исходного кода |
9
+ | Импорт в Python | `import openpyxl` |
10
+ | Python | `>=3.8` |
11
+ | Лицензия | MIT, исходный текст в `LICENCE.rst` |
12
+ | Авторы | `See AUTHORS`, оригинальный список в `AUTHORS.rst` |
13
+ | Сопровождающий форка | `EugeneRymarev` — имя владельца GitHub-репозитория |
14
+ | Репозиторий | https://github.com/EugeneRymarev/openpyxl2 |
15
+ | Трекер | https://github.com/EugeneRymarev/openpyxl2/issues |
16
+ | Описание для страницы PyPI | `README.pypi.md` |
17
+ | Обязательная зависимость | `et_xmlfile` |
18
+ | Форматы | универсальный wheel `py3-none-any` и sdist `.tar.gz` |
19
+
20
+ Публичный email сопровождающего не указан. Адреса авторов upstream не
21
+ используются как контакты поддержки форка. Владельцем проекта на PyPI станет
22
+ аккаунт, который выполнит первую разрешённую загрузку; поле `maintainer` не
23
+ выдаёт права доступа.
24
+
25
+ На 10 октября 2026 года страница https://pypi.org/project/openpyxl2/ возвращала
26
+ 404. Это не резервирует имя: окончательно его доступность проверяется при
27
+ первой загрузке, включая ограничения PyPI на зарезервированные имена.
28
+
29
+ ## 1. Собрать и проверить
30
+
31
+ Команды ниже рассчитаны на PowerShell из корня репозитория. Для сборки удобно
32
+ использовать Python 3.12 или новее, независимо от минимальной версии библиотеки.
33
+
34
+ ```powershell
35
+ py -3.12 -m venv .tox/pypi-build
36
+ $publishPython = '.\.tox\pypi-build\Scripts\python.exe'
37
+ & $publishPython -m pip install --upgrade pip build twine
38
+ & $publishPython -m build --outdir dist/pypi
39
+ & $publishPython -m twine check --strict dist/pypi/openpyxl2-3.2.0b2*
40
+ ```
41
+
42
+ Результат:
43
+
44
+ - `dist/pypi/openpyxl2-3.2.0b2-py3-none-any.whl`
45
+ - `dist/pypi/openpyxl2-3.2.0b2.tar.gz`
46
+
47
+ Проверка установленного wheel отдельно от исходников и необязательных зависимостей:
48
+
49
+ ```powershell
50
+ py -3.12 -m venv .tox/pypi-smoke
51
+ $smokePython = '.\.tox\pypi-smoke\Scripts\python.exe'
52
+ & $smokePython -m pip install dist/pypi/openpyxl2-3.2.0b2-py3-none-any.whl
53
+ & $smokePython -m pip check
54
+ & $smokePython -I .github/scripts/check_wheel.py
55
+ ```
56
+
57
+ Если эти окружения уже использовались для других пакетов, создайте новые с
58
+ другими именами. Не устанавливайте оригинальный `openpyxl` в окружение форка:
59
+ файлы модулей пересекаются. Требования сторонних библиотек вида `openpyxl>=...`
60
+ не удовлетворяются пакетом `openpyxl2`, и pip может дополнительно поставить
61
+ оригинал. Учитывайте это при выборе зависимостей приложения.
62
+
63
+ ## 2. Проверить загрузку через TestPyPI
64
+
65
+ Зарегистрируйте отдельный аккаунт на https://test.pypi.org/account/register/,
66
+ подтвердите email, настройте двухфакторную аутентификацию и создайте API token.
67
+ Для первой загрузки нового проекта нужен токен с областью `Entire account`.
68
+ TestPyPI и PyPI имеют отдельные аккаунты и токены.
69
+
70
+ ```powershell
71
+ & $publishPython -m twine upload --repository testpypi --username __token__ dist/pypi/openpyxl2-3.2.0b2*
72
+ ```
73
+
74
+ При запросе пароля вставьте токен целиком, включая префикс `pypi-`.
75
+ Не записывайте токен в исходники или команду. Откройте страницу
76
+ https://test.pypi.org/project/openpyxl2/3.2.0b2/ и проверьте описание и ссылки.
77
+
78
+ Проверка установки с TestPyPI в новом окружении:
79
+
80
+ ```powershell
81
+ py -3.12 -m venv .tox/testpypi-smoke
82
+ $testPython = '.\.tox\testpypi-smoke\Scripts\python.exe'
83
+ & $testPython -m pip install --index-url https://pypi.org/simple et_xmlfile
84
+ & $testPython -m pip install --index-url https://test.pypi.org/simple/ --no-deps openpyxl2==3.2.0b2
85
+ & $testPython -m pip check
86
+ & $testPython -I .github/scripts/check_wheel.py
87
+ ```
88
+
89
+ Зависимость ставится отдельно из основного PyPI: в TestPyPI её может не быть.
90
+
91
+ ## 3. Опубликовать в основном PyPI
92
+
93
+ Создайте аккаунт на https://pypi.org/account/register/, подтвердите email,
94
+ настройте 2FA и создайте API token с областью `Entire account` для первой
95
+ публикации. Используйте токен основного PyPI, а не TestPyPI.
96
+
97
+ Загрузите те же два проверенных файла:
98
+
99
+ ```powershell
100
+ & $publishPython -m twine upload --repository pypi --username __token__ dist/pypi/openpyxl2-3.2.0b2*
101
+ ```
102
+
103
+ Эта команда публикует пакет для всех пользователей. После первой публикации
104
+ создайте токен только для проекта `openpyxl2` и отзовите использованный токен
105
+ с областью всего аккаунта, если он больше не нужен.
106
+
107
+ Для установки опубликованной беты:
108
+
109
+ ```powershell
110
+ python -m pip install openpyxl2==3.2.0b2
111
+ ```
112
+
113
+ Явная версия выбирает бету предсказуемо. Для получения подходящих предварительных
114
+ версий можно использовать `python -m pip install --pre openpyxl2`.
115
+
116
+ ## Следующие релизы
117
+
118
+ Версия берётся из `openpyxl/_constants.py`. Для следующей беты используйте,
119
+ например, `3.2.0b3`; версию `3.2.0` выбирайте, когда готовы объявить стабильный
120
+ релиз. Обновите примеры версий в README и этой инструкции вместе с константой.
121
+ PyPI не позволяет заменить уже загруженные файлы; удаление релиза не разрешает
122
+ повторное использование тех же имён файлов. Перед новой загрузкой собирайте
123
+ в отдельный каталог и выбирайте файлы нужной версии явно.
124
+
125
+ Существующий `.github/workflows/pytest.yml` проверяет Python 3.8–3.14, собирает
126
+ wheel и sdist, запускает `twine check --strict` и smoke-тест wheel. На событие
127
+ GitHub Release он прикрепляет файлы к релизу, но не отправляет их в PyPI.
128
+ Изменения workflow начнут действовать после отправки коммита на GitHub.
129
+
130
+ ## Официальные инструкции
131
+
132
+ - [Сборка и публикация пакетов](https://packaging.python.org/en/latest/tutorials/packaging-projects/)
133
+ - [Работа с TestPyPI](https://packaging.python.org/en/latest/guides/using-testpypi/)
134
+ - [API tokens PyPI](https://pypi.org/help/#apitoken)
135
+ - [Почему имя может быть недоступно](https://pypi.org/help/#project-name)
136
+
137
+ Для будущей автоматизации можно настроить
138
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/adding-a-publisher/)
139
+ через GitHub Actions. Текущая подготовка использует ручную загрузку через Twine.
@@ -0,0 +1,244 @@
1
+ # openpyxl2
2
+
3
+ [Русская версия](README.ru.md)
4
+
5
+ **openpyxl2** is a fork of [openpyxl](https://foss.heptapod.net/openpyxl/openpyxl), a Python library for reading and writing Excel `.xlsx`, `.xlsm`, `.xltx` and `.xltm` files. It improves row and column editing, maintains formula references, and preserves formatting of merged cells.
6
+
7
+ The distribution name is **`openpyxl2`**; the Python import remains **`openpyxl`**. Python 3.8 or newer is required; the repository's CI matrix covers Python 3.8–3.14. The code is based on the upstream 3.2 development line and reports version `3.2.0b2` (beta).
8
+
9
+ ## Installation and a first workbook
10
+
11
+ Install this repository in a virtual environment to use the fork:
12
+
13
+ ```sh
14
+ git clone https://github.com/EugeneRymarev/openpyxl2.git
15
+ cd openpyxl2
16
+ python -m pip install .
17
+ ```
18
+
19
+ After publication on PyPI, install the fork with `python -m pip install openpyxl2==3.2.0b2`. `pip install openpyxl` installs the original distribution. Do not install both in the same environment: they write to the same `openpyxl` module directory. Dependencies requiring the `openpyxl` distribution are not satisfied by `openpyxl2`. Excel is not required to generate or read files; a spreadsheet application is needed to calculate formulas and view the screenshots' results.
20
+
21
+ ```python
22
+ from openpyxl import Workbook, load_workbook
23
+
24
+ wb = Workbook()
25
+ ws = wb.active
26
+ ws.title = "Sales"
27
+ ws.append(["Item", "Units"])
28
+ ws.append(["Pens", 10])
29
+ ws.append(["Folders", 20])
30
+ ws["B4"] = "=SUM(B2:B3)"
31
+ ws.insert_rows(2) # B5 now contains =SUM(B3:B4)
32
+ wb.save("sales.xlsx")
33
+
34
+ loaded = load_workbook("sales.xlsx")
35
+ assert loaded["Sales"]["B5"].value == "=SUM(B3:B4)"
36
+ loaded.close()
37
+ ```
38
+
39
+ ## Downloading wheels and publishing releases
40
+
41
+ The **Python package** workflow builds a universal `py3-none-any.whl` and a source archive (`.tar.gz`). After the wheel passes its clean-environment smoke test, both files are saved for 30 days in the **python-distributions** artifact on the Actions run page. Builds run on pushes and pull requests configured in the workflow, and can also be started with **Actions → Python package → Run workflow**. Extract the downloaded artifact ZIP before installing the wheel:
42
+
43
+ ```sh
44
+ python -m pip install /path/to/openpyxl2-3.2.0b2-py3-none-any.whl
45
+ ```
46
+
47
+ Use the filename for the version you downloaded. The wheel works on Windows, Linux and macOS with supported Python versions.
48
+
49
+ To publish packages in [GitHub Releases](https://github.com/EugeneRymarev/openpyxl2/releases):
50
+
51
+ 1. Update `__version__` in `openpyxl/_constants.py` and commit the change together with the workflow.
52
+ 2. Create and publish a GitHub Release (or prerelease) from that commit with a tag equal to the package version, optionally prefixed with `v`, for example `v3.2.0b2`. Saving a draft or pushing a tag alone does not publish packages.
53
+ 3. The workflow checks the tag, builds the packages and runs the test matrix. Once all test and wheel jobs succeed, it attaches the same `.whl` and `.tar.gz` files to the release.
54
+
55
+ The upload job uses the automatic `GITHUB_TOKEN` with `contents: write`; no extra secret is needed. Existing assets are not overwritten, so rerunning publication with the same filenames fails rather than replacing them. Publishing only works for tags whose commit includes this workflow. The distribution is `openpyxl2`, and the import is `openpyxl`; this workflow does not upload to PyPI. See [PUBLISHING.md](PUBLISHING.md) for the PyPI metadata, TestPyPI check and upload commands.
56
+
57
+ ## Differences from original openpyxl
58
+
59
+ This overview compares the fork with the upstream revision imported into its history, [`1fa786a9d`](https://github.com/EugeneRymarev/openpyxl2/commit/1fa786a9dd5d77754d703f967832f5243e08ba0a). The examples below use fork revision `4c2bce2eb6f4682a1c3bafbb00e8802e3aac88a5`. This is a reproducible comparison of two source revisions, not a claim that every upstream release behaves identically.
60
+
61
+ - **Row and column edits maintain dependencies.** `insert_rows`, `insert_cols`, `delete_rows` and `delete_cols` update merged ranges, static workbook and worksheet names, print areas, print titles, row heights and column widths, including stored hidden/grouping/style properties. Supported literal names include unions and whole-row/whole-column references.
62
+ - **Formulas follow structural edits.** Direct A1 references in ordinary cell formulas throughout the workbook and in calculated defined names are adjusted, including absolute and mixed references. Ranges expand or shrink; references that are completely deleted become `#REF!`. Dollar signs still control copying, but do not prevent structural updates.
63
+ - **Merged ranges survive partial deletion.** A partially deleted merge shrinks and retains the original anchor's content and style at the surviving upper-left cell. A fully deleted merge is removed. Single-cell merged ranges are supported, and resized ranges receive the corresponding outer borders.
64
+ - **Edits are planned before mutation.** Supported-reference and bounds checks happen before applying the edit. The implementation stages changes and rolls them back on commit failures. Sparse worksheets stay sparse, surviving `Cell` objects retain their identity, and subsequent `append()` uses the updated worksheet position.
65
+ - **Styles assigned after merging propagate.** Assigning font, fill, alignment, protection, number format or a named style to the top-left cell updates the merged range. Borders are projected onto its perimeter; the anchor retains the full border definition. Replacing or clearing the anchor border updates previously projected edges. Explicit edits to a non-anchor `MergedCell` remain local.
66
+ - **Stored merged-cell styles survive loading.** Individual styles and explicit protection of non-anchor merged cells are preserved through save/load cycles. Missing border information is reconstructed without replacing those stored styles. Merging still discards non-anchor values; this feature does not recover data when unmerging.
67
+ - **Merged-range lookup by coordinate.** `MultiCellRange.__getitem__`, including `ws.merged_cells["C3"]`, returns the containing range as a coordinate **string**. A coordinate outside all ranges raises `CellNotMergedException`.
68
+ - **Named styles compare by name.** A `NamedStyle` can be compared with a string or another `NamedStyle`. Equality compares names, not the formatting properties. Other operand types raise `NotNamedStyleOrStrException`.
69
+ - **Named styles are rebound before serialization.** Their font, fill, border and other component indexes are resolved against the workbook being saved, avoiding indexes from another workbook when a style object is reused. This does not make shared mutable styles thread-safe.
70
+ - **The `openpyxl.open` alias was removed.** Use `openpyxl.load_workbook`. Imports were also made explicit; code should import objects from their defining modules rather than rely on incidental re-exports.
71
+ - **Runtime corrections.** Fixes cover mutable default arguments and shared state, missing returns and ineffective statements, mismatched method signatures, incorrect variable/class references, typos, built-in name shadowing, Decimal imports and numeric-type tuple construction, and theme XML namespace serialization.
72
+ - **XML backend corrections.** The standard-library `iterparse` fallback is available without optional `defusedxml`. XML tests exercise the selected backend and use appropriate byte input for lxml. Dependency constraints were updated, including Python 3.14-compatible lxml and NumPy selections.
73
+ - **Upstream maintenance fixes were ported.** Worksheet ZIP streams and normal-mode workbook archives are closed on the covered success/error paths, reducing resource leaks and Windows file locks. MIME lookup is case-insensitive while preserving the extension written into the package. These are upstream fixes carried into this fork, not features unique to it. Successful read-only loads still require the caller to close the workbook.
74
+ - **Development and compatibility work.** Code and imports were reformatted with Black and import-sorting tools; line endings and repository configuration were normalized. Regression coverage was added for structural edits, formulas, merged styles, resource handling and XML backends. CI includes Python 3.8–3.14, Windows/Linux checks for 3.14, and installation of a built wheel without optional XML packages.
75
+
76
+ The fork also inherits upstream 3.2 development changes, such as the cell `Coordinate` model, external connections, controls and volatile dependencies. Those are part of its upstream base, not original additions by this fork. For the history of that comparison, see the [upstream audit](doc/upstream-audit-2026-10-09.md).
77
+
78
+ ## Before and after in Microsoft Excel
79
+
80
+ All ten images below are screenshots of **actual workbooks opened in Microsoft Excel for Windows**. Both versions run the same Python operations on the same data. “Before” means the pinned original openpyxl revision; “After” means this fork. The comparison shows library-generated files opened in Excel, not Excel's own Insert/Delete commands.
81
+
82
+ The starting table contains 10 pens and 20 folders. `C9` contains `=SUM(C6:C7)`, so the expected total is **30**. Its title is merged across `B3:E3`. Each operation starts from a fresh copy of this table:
83
+
84
+ ```python
85
+ from openpyxl import Workbook
86
+
87
+ wb = Workbook()
88
+ ws = wb.active
89
+ ws.merge_cells("B3:E3")
90
+ ws["B3"] = "OFFICE SUPPLIES"
91
+ for row, values in (
92
+ (5, ["Item", "Units", "Unit price", "Batch"]),
93
+ (6, ["Pens", 10, 2, "A"]),
94
+ (7, ["Folders", 20, 3, "B"]),
95
+ ):
96
+ for column, value in enumerate(values, 2):
97
+ ws.cell(row, column, value)
98
+ ws["B9"] = "Total units"
99
+ ws["C9"] = "=SUM(C6:C7)"
100
+ # Apply one of the operations below, then save the workbook.
101
+ ```
102
+
103
+ The [complete generator](doc/readme/build_examples.py) adds the formatting, dimensions, defined names, print areas and explanatory captions used in the screenshots. The formula bar in this Excel installation displays the localized name `СУММ`; the stored XLSX formula is `SUM`.
104
+
105
+ ### 1. `insert_rows`
106
+
107
+ ```python
108
+ ws.insert_rows(3)
109
+ wb.save("insert_rows.xlsx")
110
+ ```
111
+
112
+ Previously, the cells moved but the formula in `C10` stayed `=SUM(C6:C7)`, giving **10**, and the merge stayed at `B3:E3`. Now the formula becomes `=SUM(C7:C8)`, giving **30**, and the title's merge moves to `B4:E4`.
113
+
114
+ | Before: original openpyxl | After: openpyxl2 |
115
+ |-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
116
+ | ![insert_rows before: total 10 and displaced title](doc/readme/images/insert_rows_before.jpg) | ![insert_rows after: total 30 and correctly merged title](doc/readme/images/insert_rows_after.jpg) |
117
+
118
+ ### 2. `insert_cols`
119
+
120
+ ```python
121
+ ws.insert_cols(2)
122
+ wb.save("insert_cols.xlsx")
123
+ ```
124
+
125
+ Previously, `D9` still summed `C6:C7`, now containing item names, and displayed **0**. The old `B3:E3` merge also hid the moved title when Excel opened the file. Now `D9` sums `D6:D7` and displays **30**; the merge moves to `C3:F3`, and the column dimensions follow the table.
126
+
127
+ | Before: original openpyxl | After: openpyxl2 |
128
+ |--------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
129
+ | ![insert_cols before: total 0 and missing title](doc/readme/images/insert_cols_before.jpg) | ![insert_cols after: total 30 and visible title](doc/readme/images/insert_cols_after.jpg) |
130
+
131
+ ### 3. `delete_rows`
132
+
133
+ ```python
134
+ ws.delete_rows(2)
135
+ wb.save("delete_rows.xlsx")
136
+ ```
137
+
138
+ Previously, the formula moved to `C8` but kept `=SUM(C6:C7)`, giving **20**. The title moved away from its merge. Now `C8` contains `=SUM(C5:C6)`, giving **30**, and the merge moves to `B2:E2`.
139
+
140
+ | Before: original openpyxl | After: openpyxl2 |
141
+ |-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
142
+ | ![delete_rows before: total 20 and displaced title](doc/readme/images/delete_rows_before.jpg) | ![delete_rows after: total 30 and correctly merged title](doc/readme/images/delete_rows_after.jpg) |
143
+
144
+ ### 4. `delete_cols`
145
+
146
+ ```python
147
+ ws.delete_cols(1)
148
+ wb.save("delete_cols.xlsx")
149
+ ```
150
+
151
+ Previously, `B9` still summed `C6:C7`, now the unit prices, giving **5**. The original narrow column A clipped the moved labels, and the merge stayed at `B3:E3`. Now `B9` sums `B6:B7`, giving **30**; the merge becomes `A3:D3`, and the widths move with the columns.
152
+
153
+ | Before: original openpyxl | After: openpyxl2 |
154
+ |---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
155
+ | ![delete_cols before: total 5 and clipped labels](doc/readme/images/delete_cols_before.jpg) | ![delete_cols after: total 30 and preserved widths](doc/readme/images/delete_cols_after.jpg) |
156
+
157
+ ### 5. `merge_cells` and formatting after merging
158
+
159
+ This independent example assigns formatting to the anchor **after** creating each merge:
160
+
161
+ ```python
162
+ from openpyxl import Workbook
163
+ from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
164
+
165
+ wb = Workbook()
166
+ ws = wb.active
167
+ blue = Side(style="medium", color="FF2563EB")
168
+ for area, anchor in (("B3:E6", "B3"), ("B9:E9", "B9")):
169
+ ws.merge_cells(area)
170
+ cell = ws[anchor]
171
+ cell.value = f"Merged {area}"
172
+ cell.font = Font(name="Calibri", size=18, bold=True, color="FF17365D")
173
+ cell.fill = PatternFill("solid", fgColor="FFDBEAFE")
174
+ cell.alignment = Alignment(horizontal="center", vertical="center")
175
+ cell.border = Border(left=blue, right=blue, top=blue, bottom=blue)
176
+ wb.save("merged.xlsx")
177
+ ```
178
+
179
+ Previously, only the anchor stored the assigned border, so Excel displayed an incomplete outline. Now both the rectangular and single-row merges have a **complete outer border**. Excel already displays the anchor's fill and centered text across the old merge; the visible correction here is the border. The fork additionally propagates the other style properties in the underlying cells and preserves stored merged-cell styles on reload.
180
+
181
+ | Before: original openpyxl | After: openpyxl2 |
182
+ |------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
183
+ | ![merge_cells before: incomplete blue borders](doc/readme/images/merge_cells_before.jpg) | ![merge_cells after: complete blue borders](doc/readme/images/merge_cells_after.jpg) |
184
+
185
+ ### Reproduce the comparison
186
+
187
+ Download the [original-version workbook](doc/readme/workbooks/before.xlsx) and the [fork workbook](doc/readme/workbooks/after.xlsx). Each has five sheets named after the demonstrated operations. Open them in Excel and allow formula recalculation to see the totals; openpyxl itself does not calculate cached results.
188
+
189
+ To regenerate the workbooks from a checkout containing the baseline Git history and installed dependencies:
190
+
191
+ ```sh
192
+ python doc/readme/build_examples.py
193
+ ```
194
+
195
+ The script extracts the pinned upstream source into a temporary directory, runs it and the current checkout separately, and checks the resulting formulas and border differences. It records source revisions, merges, names and print areas in [provenance.json](doc/readme/provenance.json). Screenshots are captured separately in Excel; running the script does not capture or refresh them. The [capture manifest](doc/readme/screenshots.json) records image dimensions and SHA-256 hashes of the screenshots and displayed workbooks. Both books contain synthetic sample data only.
196
+
197
+ ## Additional API examples
198
+
199
+ ```python
200
+ from openpyxl import Workbook
201
+ from openpyxl.styles import NamedStyle
202
+
203
+ wb = Workbook()
204
+ ws = wb.active
205
+ ws.merge_cells("B2:D4")
206
+ assert ws.merged_cells["C3"] == "B2:D4" # str, not CellRange
207
+
208
+ style = NamedStyle(name="Heading")
209
+ assert style == "Heading"
210
+ assert style == NamedStyle(name="Heading") # compares names only
211
+ ```
212
+
213
+ By default, all four structural methods use `update_dependencies=True` and `update_formulas=True`. These switches let existing applications keep responsibility for part or all of the update:
214
+
215
+ ```python
216
+ ws.insert_rows(5, amount=2) # Update supported metadata and formulas.
217
+ ws.insert_rows(5, amount=2, update_formulas=False) # Keep formula text.
218
+ ws.insert_rows(5, amount=2, update_dependencies=False) # Legacy cell movement.
219
+ ```
220
+
221
+ These are alternative calls, not a recipe to apply all three to the same sheet. `update_dependencies=False` also disables formula rewriting. The same options are available for `insert_cols`, `delete_rows` and `delete_cols`.
222
+
223
+ ## Scope and limitations
224
+
225
+ - Structural edits do not update tables, chart/pivot references, filters, data validation, conditional formatting, hyperlinks, drawings/controls or worksheet views. `move_range` retains its separate behavior. Merged-range and dimension policies are documented in [editing worksheets](doc/editing_worksheets.rst).
226
+ - Formula updates support direct A1 cells/ranges, whole axes, unions/intersections and ordinary-string `@`/spill references. External-workbook references and structured table references to remain unchanged. Text inside `INDIRECT` is literal; dynamic addressing is not evaluated.
227
+ - Detected unsupported constructs, including 3D references, named/function range endpoints, unparseable formulas and array/data-table formula objects, raise `FormulaTranslationError` before mutation. Array/data-table objects anywhere in the workbook block formula-enabled edits. The tokenizer is not a complete Excel grammar validator.
228
+ - Unqualified workbook-level names have no reliable sheet context and remain unchanged. Relative names use their stored A1 coordinates; prefer absolute references for predictable maintenance. Recognizing spill syntax does not maintain dynamic-array output metadata.
229
+ - The library rewrites formula text and requests recalculation; it does not evaluate formulas or write their cached results. Existing manual/iterative calculation settings are preserved. Each formula-enabled edit scans materialized cells across the workbook; there is no dependency graph.
230
+ - XML hardening is optional: install `defusedxml` when processing untrusted XML. `lxml` is an optional backend, not a requirement for using the library.
231
+
232
+ See [formula support](doc/issue-1273-formulas.md), [structural edits](doc/issue-1273.md) and [merged-cell borders](doc/issue-2024.md) for implementation details and recorded regression results. Those records describe their respective revisions; the README describes the combined behavior.
233
+
234
+ ## Development, documentation and license
235
+
236
+ ```sh
237
+ python -m pip install -r requirements.txt
238
+ python -m pip install -e .
239
+ python -m pytest
240
+ ```
241
+
242
+ The [upstream documentation](https://openpyxl.pages.heptapod.net/openpyxl/) explains the shared API. Use this README and the repository's `doc/` notes for fork-specific behavior. Report fork issues in the [openpyxl2 tracker](https://github.com/EugeneRymarev/openpyxl2/issues).
243
+
244
+ Distributed under the [MIT license](LICENCE.rst). Original openpyxl authors and contributors are listed in [AUTHORS.rst](AUTHORS.rst); the project was initially inspired by PHPExcel.