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.
- openpyxl2-3.2.0b2/AUTHORS.rst +98 -0
- openpyxl2-3.2.0b2/LICENCE.rst +23 -0
- openpyxl2-3.2.0b2/MANIFEST.in +8 -0
- openpyxl2-3.2.0b2/PKG-INFO +125 -0
- openpyxl2-3.2.0b2/PUBLISHING.md +139 -0
- openpyxl2-3.2.0b2/README.md +244 -0
- openpyxl2-3.2.0b2/README.pypi.md +83 -0
- openpyxl2-3.2.0b2/README.ru.md +244 -0
- openpyxl2-3.2.0b2/openpyxl/__init__.py +19 -0
- openpyxl2-3.2.0b2/openpyxl/_constants.py +12 -0
- openpyxl2-3.2.0b2/openpyxl/cell/__init__.py +5 -0
- openpyxl2-3.2.0b2/openpyxl/cell/_writer.py +121 -0
- openpyxl2-3.2.0b2/openpyxl/cell/cell.py +417 -0
- openpyxl2-3.2.0b2/openpyxl/cell/coordinate.py +32 -0
- openpyxl2-3.2.0b2/openpyxl/cell/read_only.py +98 -0
- openpyxl2-3.2.0b2/openpyxl/cell/rich_text.py +188 -0
- openpyxl2-3.2.0b2/openpyxl/cell/text.py +168 -0
- openpyxl2-3.2.0b2/openpyxl/chart/_3d.py +85 -0
- openpyxl2-3.2.0b2/openpyxl/chart/__init__.py +20 -0
- openpyxl2-3.2.0b2/openpyxl/chart/_chart.py +194 -0
- openpyxl2-3.2.0b2/openpyxl/chart/area_chart.py +81 -0
- openpyxl2-3.2.0b2/openpyxl/chart/axis.py +389 -0
- openpyxl2-3.2.0b2/openpyxl/chart/bar_chart.py +144 -0
- openpyxl2-3.2.0b2/openpyxl/chart/bubble_chart.py +61 -0
- openpyxl2-3.2.0b2/openpyxl/chart/chartspace.py +194 -0
- openpyxl2-3.2.0b2/openpyxl/chart/data_source.py +175 -0
- openpyxl2-3.2.0b2/openpyxl/chart/descriptors.py +34 -0
- openpyxl2-3.2.0b2/openpyxl/chart/error_bar.py +69 -0
- openpyxl2-3.2.0b2/openpyxl/chart/label.py +134 -0
- openpyxl2-3.2.0b2/openpyxl/chart/layout.py +68 -0
- openpyxl2-3.2.0b2/openpyxl/chart/legend.py +58 -0
- openpyxl2-3.2.0b2/openpyxl/chart/line_chart.py +129 -0
- openpyxl2-3.2.0b2/openpyxl/chart/marker.py +89 -0
- openpyxl2-3.2.0b2/openpyxl/chart/picture.py +38 -0
- openpyxl2-3.2.0b2/openpyxl/chart/pie_chart.py +136 -0
- openpyxl2-3.2.0b2/openpyxl/chart/pivot.py +52 -0
- openpyxl2-3.2.0b2/openpyxl/chart/plotarea.py +151 -0
- openpyxl2-3.2.0b2/openpyxl/chart/print_settings.py +46 -0
- openpyxl2-3.2.0b2/openpyxl/chart/radar_chart.py +44 -0
- openpyxl2-3.2.0b2/openpyxl/chart/reader.py +28 -0
- openpyxl2-3.2.0b2/openpyxl/chart/reference.py +101 -0
- openpyxl2-3.2.0b2/openpyxl/chart/scatter_chart.py +53 -0
- openpyxl2-3.2.0b2/openpyxl/chart/series.py +239 -0
- openpyxl2-3.2.0b2/openpyxl/chart/series_factory.py +47 -0
- openpyxl2-3.2.0b2/openpyxl/chart/shapes.py +103 -0
- openpyxl2-3.2.0b2/openpyxl/chart/stock_chart.py +46 -0
- openpyxl2-3.2.0b2/openpyxl/chart/surface_chart.py +85 -0
- openpyxl2-3.2.0b2/openpyxl/chart/text.py +57 -0
- openpyxl2-3.2.0b2/openpyxl/chart/title.py +70 -0
- openpyxl2-3.2.0b2/openpyxl/chart/trendline.py +110 -0
- openpyxl2-3.2.0b2/openpyxl/chart/updown_bars.py +20 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/chartsheet.py +109 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/custom.py +50 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/properties.py +19 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/protection.py +44 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/publish.py +63 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/relation.py +95 -0
- openpyxl2-3.2.0b2/openpyxl/chartsheet/views.py +42 -0
- openpyxl2-3.2.0b2/openpyxl/comments/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/comments/author.py +13 -0
- openpyxl2-3.2.0b2/openpyxl/comments/comment_sheet.py +188 -0
- openpyxl2-3.2.0b2/openpyxl/comments/comments.py +56 -0
- openpyxl2-3.2.0b2/openpyxl/comments/shape_writer.py +111 -0
- openpyxl2-3.2.0b2/openpyxl/compat/__init__.py +49 -0
- openpyxl2-3.2.0b2/openpyxl/compat/abc.py +9 -0
- openpyxl2-3.2.0b2/openpyxl/compat/numbers.py +43 -0
- openpyxl2-3.2.0b2/openpyxl/compat/product.py +16 -0
- openpyxl2-3.2.0b2/openpyxl/compat/singleton.py +38 -0
- openpyxl2-3.2.0b2/openpyxl/compat/strings.py +24 -0
- openpyxl2-3.2.0b2/openpyxl/connection/__init__.py +1 -0
- openpyxl2-3.2.0b2/openpyxl/connection/connections.py +449 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/__init__.py +55 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/base.py +249 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/container.py +34 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/excel.py +101 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/namespace.py +11 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/nested.py +116 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/sequence.py +127 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/serialisable.py +208 -0
- openpyxl2-3.2.0b2/openpyxl/descriptors/slots.py +18 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/anchor.py +166 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/colors.py +681 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/connector.py +121 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/drawing.py +78 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/effect.py +364 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/fill.py +456 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/geometry.py +764 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/graphic.py +140 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/image.py +95 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/legacy.py +24 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/line.py +142 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/picture.py +126 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/properties.py +158 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/relation.py +13 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/spreadsheet_drawing.py +269 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/text.py +767 -0
- openpyxl2-3.2.0b2/openpyxl/drawing/xdr.py +30 -0
- openpyxl2-3.2.0b2/openpyxl/formatting/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/formatting/formatting.py +95 -0
- openpyxl2-3.2.0b2/openpyxl/formatting/rule.py +383 -0
- openpyxl2-3.2.0b2/openpyxl/formula/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/formula/structural.py +178 -0
- openpyxl2-3.2.0b2/openpyxl/formula/tokenizer.py +444 -0
- openpyxl2-3.2.0b2/openpyxl/formula/translate.py +179 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/__init__.py +3 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/core.py +120 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/custom.py +236 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/extended.py +131 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/interface.py +49 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/manifest.py +196 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/relationship.py +148 -0
- openpyxl2-3.2.0b2/openpyxl/packaging/workbook.py +189 -0
- openpyxl2-3.2.0b2/openpyxl/pivot/__init__.py +1 -0
- openpyxl2-3.2.0b2/openpyxl/pivot/cache.py +920 -0
- openpyxl2-3.2.0b2/openpyxl/pivot/fields.py +280 -0
- openpyxl2-3.2.0b2/openpyxl/pivot/record.py +81 -0
- openpyxl2-3.2.0b2/openpyxl/pivot/table.py +1297 -0
- openpyxl2-3.2.0b2/openpyxl/reader/__init__.py +1 -0
- openpyxl2-3.2.0b2/openpyxl/reader/drawings.py +73 -0
- openpyxl2-3.2.0b2/openpyxl/reader/excel.py +486 -0
- openpyxl2-3.2.0b2/openpyxl/reader/strings.py +38 -0
- openpyxl2-3.2.0b2/openpyxl/reader/workbook.py +121 -0
- openpyxl2-3.2.0b2/openpyxl/styles/__init__.py +15 -0
- openpyxl2-3.2.0b2/openpyxl/styles/alignment.py +79 -0
- openpyxl2-3.2.0b2/openpyxl/styles/borders.py +133 -0
- openpyxl2-3.2.0b2/openpyxl/styles/builtins.py +1362 -0
- openpyxl2-3.2.0b2/openpyxl/styles/cell_style.py +203 -0
- openpyxl2-3.2.0b2/openpyxl/styles/colors.py +213 -0
- openpyxl2-3.2.0b2/openpyxl/styles/differential.py +81 -0
- openpyxl2-3.2.0b2/openpyxl/styles/fills.py +228 -0
- openpyxl2-3.2.0b2/openpyxl/styles/fonts.py +146 -0
- openpyxl2-3.2.0b2/openpyxl/styles/named_styles.py +255 -0
- openpyxl2-3.2.0b2/openpyxl/styles/numbers.py +181 -0
- openpyxl2-3.2.0b2/openpyxl/styles/protection.py +17 -0
- openpyxl2-3.2.0b2/openpyxl/styles/proxy.py +56 -0
- openpyxl2-3.2.0b2/openpyxl/styles/styleable.py +217 -0
- openpyxl2-3.2.0b2/openpyxl/styles/stylesheet.py +263 -0
- openpyxl2-3.2.0b2/openpyxl/styles/table.py +100 -0
- openpyxl2-3.2.0b2/openpyxl/utils/__init__.py +12 -0
- openpyxl2-3.2.0b2/openpyxl/utils/bound_dictionary.py +24 -0
- openpyxl2-3.2.0b2/openpyxl/utils/cell.py +229 -0
- openpyxl2-3.2.0b2/openpyxl/utils/dataframe.py +73 -0
- openpyxl2-3.2.0b2/openpyxl/utils/datetime.py +147 -0
- openpyxl2-3.2.0b2/openpyxl/utils/escape.py +37 -0
- openpyxl2-3.2.0b2/openpyxl/utils/exceptions.py +56 -0
- openpyxl2-3.2.0b2/openpyxl/utils/formulas.py +380 -0
- openpyxl2-3.2.0b2/openpyxl/utils/indexed_list.py +49 -0
- openpyxl2-3.2.0b2/openpyxl/utils/inference.py +66 -0
- openpyxl2-3.2.0b2/openpyxl/utils/protection.py +22 -0
- openpyxl2-3.2.0b2/openpyxl/utils/units.py +116 -0
- openpyxl2-3.2.0b2/openpyxl/volatile/__init__.py +1 -0
- openpyxl2-3.2.0b2/openpyxl/volatile/volatile.py +79 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/_writer.py +185 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/child.py +147 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/defined_name.py +185 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/external_link/__init__.py +2 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/external_link/external.py +135 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/external_reference.py +11 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/function_group.py +24 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/properties.py +143 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/protection.py +180 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/smart_tags.py +37 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/views.py +142 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/web.py +98 -0
- openpyxl2-3.2.0b2/openpyxl/workbook/workbook.py +428 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/__init__.py +1 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/_read_only.py +173 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/_reader.py +482 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/_structural_edit.py +428 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/_write_only.py +138 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/_writer.py +384 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/cell_range.py +497 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/cell_watch.py +23 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/controls.py +323 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/copier.py +57 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/custom.py +23 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/datavalidation.py +211 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/dimensions.py +315 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/drawing.py +11 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/errors.py +74 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/filters.py +558 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/formula.py +47 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/header_footer.py +260 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/hyperlink.py +40 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/merge.py +150 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/ole.py +126 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/page.py +167 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/pagebreak.py +75 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/picture.py +8 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/print_settings.py +165 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/properties.py +91 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/protection.py +148 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/related.py +13 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/scenario.py +80 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/smart_tag.py +57 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/table.py +360 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/views.py +145 -0
- openpyxl2-3.2.0b2/openpyxl/worksheet/worksheet.py +982 -0
- openpyxl2-3.2.0b2/openpyxl/writer/__init__.py +1 -0
- openpyxl2-3.2.0b2/openpyxl/writer/excel.py +351 -0
- openpyxl2-3.2.0b2/openpyxl/writer/theme.py +310 -0
- openpyxl2-3.2.0b2/openpyxl/xml/__init__.py +44 -0
- openpyxl2-3.2.0b2/openpyxl/xml/constants.py +121 -0
- openpyxl2-3.2.0b2/openpyxl/xml/functions.py +80 -0
- openpyxl2-3.2.0b2/openpyxl2.egg-info/PKG-INFO +125 -0
- openpyxl2-3.2.0b2/openpyxl2.egg-info/SOURCES.txt +214 -0
- openpyxl2-3.2.0b2/openpyxl2.egg-info/dependency_links.txt +1 -0
- openpyxl2-3.2.0b2/openpyxl2.egg-info/requires.txt +1 -0
- openpyxl2-3.2.0b2/openpyxl2.egg-info/top_level.txt +1 -0
- openpyxl2-3.2.0b2/pyproject.toml +3 -0
- openpyxl2-3.2.0b2/setup.cfg +7 -0
- 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,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
|
+
|  |  |
|
|
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
|
+
|  |  |
|
|
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
|
+
|  |  |
|
|
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
|
+
|  |  |
|
|
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
|
+
|  |  |
|
|
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.
|