openpyxl2 3.2.0b2__py3-none-any.whl

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 (204) hide show
  1. openpyxl/__init__.py +19 -0
  2. openpyxl/_constants.py +12 -0
  3. openpyxl/cell/__init__.py +5 -0
  4. openpyxl/cell/_writer.py +121 -0
  5. openpyxl/cell/cell.py +417 -0
  6. openpyxl/cell/coordinate.py +32 -0
  7. openpyxl/cell/read_only.py +98 -0
  8. openpyxl/cell/rich_text.py +188 -0
  9. openpyxl/cell/text.py +168 -0
  10. openpyxl/chart/_3d.py +85 -0
  11. openpyxl/chart/__init__.py +20 -0
  12. openpyxl/chart/_chart.py +194 -0
  13. openpyxl/chart/area_chart.py +81 -0
  14. openpyxl/chart/axis.py +389 -0
  15. openpyxl/chart/bar_chart.py +144 -0
  16. openpyxl/chart/bubble_chart.py +61 -0
  17. openpyxl/chart/chartspace.py +194 -0
  18. openpyxl/chart/data_source.py +175 -0
  19. openpyxl/chart/descriptors.py +34 -0
  20. openpyxl/chart/error_bar.py +69 -0
  21. openpyxl/chart/label.py +134 -0
  22. openpyxl/chart/layout.py +68 -0
  23. openpyxl/chart/legend.py +58 -0
  24. openpyxl/chart/line_chart.py +129 -0
  25. openpyxl/chart/marker.py +89 -0
  26. openpyxl/chart/picture.py +38 -0
  27. openpyxl/chart/pie_chart.py +136 -0
  28. openpyxl/chart/pivot.py +52 -0
  29. openpyxl/chart/plotarea.py +151 -0
  30. openpyxl/chart/print_settings.py +46 -0
  31. openpyxl/chart/radar_chart.py +44 -0
  32. openpyxl/chart/reader.py +28 -0
  33. openpyxl/chart/reference.py +101 -0
  34. openpyxl/chart/scatter_chart.py +53 -0
  35. openpyxl/chart/series.py +239 -0
  36. openpyxl/chart/series_factory.py +47 -0
  37. openpyxl/chart/shapes.py +103 -0
  38. openpyxl/chart/stock_chart.py +46 -0
  39. openpyxl/chart/surface_chart.py +85 -0
  40. openpyxl/chart/text.py +57 -0
  41. openpyxl/chart/title.py +70 -0
  42. openpyxl/chart/trendline.py +110 -0
  43. openpyxl/chart/updown_bars.py +20 -0
  44. openpyxl/chartsheet/__init__.py +2 -0
  45. openpyxl/chartsheet/chartsheet.py +109 -0
  46. openpyxl/chartsheet/custom.py +50 -0
  47. openpyxl/chartsheet/properties.py +19 -0
  48. openpyxl/chartsheet/protection.py +44 -0
  49. openpyxl/chartsheet/publish.py +63 -0
  50. openpyxl/chartsheet/relation.py +95 -0
  51. openpyxl/chartsheet/views.py +42 -0
  52. openpyxl/comments/__init__.py +2 -0
  53. openpyxl/comments/author.py +13 -0
  54. openpyxl/comments/comment_sheet.py +188 -0
  55. openpyxl/comments/comments.py +56 -0
  56. openpyxl/comments/shape_writer.py +111 -0
  57. openpyxl/compat/__init__.py +49 -0
  58. openpyxl/compat/abc.py +9 -0
  59. openpyxl/compat/numbers.py +43 -0
  60. openpyxl/compat/product.py +16 -0
  61. openpyxl/compat/singleton.py +38 -0
  62. openpyxl/compat/strings.py +24 -0
  63. openpyxl/connection/__init__.py +1 -0
  64. openpyxl/connection/connections.py +449 -0
  65. openpyxl/descriptors/__init__.py +55 -0
  66. openpyxl/descriptors/base.py +249 -0
  67. openpyxl/descriptors/container.py +34 -0
  68. openpyxl/descriptors/excel.py +101 -0
  69. openpyxl/descriptors/namespace.py +11 -0
  70. openpyxl/descriptors/nested.py +116 -0
  71. openpyxl/descriptors/sequence.py +127 -0
  72. openpyxl/descriptors/serialisable.py +208 -0
  73. openpyxl/descriptors/slots.py +18 -0
  74. openpyxl/drawing/__init__.py +2 -0
  75. openpyxl/drawing/anchor.py +166 -0
  76. openpyxl/drawing/colors.py +681 -0
  77. openpyxl/drawing/connector.py +121 -0
  78. openpyxl/drawing/drawing.py +78 -0
  79. openpyxl/drawing/effect.py +364 -0
  80. openpyxl/drawing/fill.py +456 -0
  81. openpyxl/drawing/geometry.py +764 -0
  82. openpyxl/drawing/graphic.py +140 -0
  83. openpyxl/drawing/image.py +95 -0
  84. openpyxl/drawing/legacy.py +24 -0
  85. openpyxl/drawing/line.py +142 -0
  86. openpyxl/drawing/picture.py +126 -0
  87. openpyxl/drawing/properties.py +158 -0
  88. openpyxl/drawing/relation.py +13 -0
  89. openpyxl/drawing/spreadsheet_drawing.py +269 -0
  90. openpyxl/drawing/text.py +767 -0
  91. openpyxl/drawing/xdr.py +30 -0
  92. openpyxl/formatting/__init__.py +2 -0
  93. openpyxl/formatting/formatting.py +95 -0
  94. openpyxl/formatting/rule.py +383 -0
  95. openpyxl/formula/__init__.py +2 -0
  96. openpyxl/formula/structural.py +178 -0
  97. openpyxl/formula/tokenizer.py +444 -0
  98. openpyxl/formula/translate.py +179 -0
  99. openpyxl/packaging/__init__.py +3 -0
  100. openpyxl/packaging/core.py +120 -0
  101. openpyxl/packaging/custom.py +236 -0
  102. openpyxl/packaging/extended.py +131 -0
  103. openpyxl/packaging/interface.py +49 -0
  104. openpyxl/packaging/manifest.py +196 -0
  105. openpyxl/packaging/relationship.py +148 -0
  106. openpyxl/packaging/workbook.py +189 -0
  107. openpyxl/pivot/__init__.py +1 -0
  108. openpyxl/pivot/cache.py +920 -0
  109. openpyxl/pivot/fields.py +280 -0
  110. openpyxl/pivot/record.py +81 -0
  111. openpyxl/pivot/table.py +1297 -0
  112. openpyxl/reader/__init__.py +1 -0
  113. openpyxl/reader/drawings.py +73 -0
  114. openpyxl/reader/excel.py +486 -0
  115. openpyxl/reader/strings.py +38 -0
  116. openpyxl/reader/workbook.py +121 -0
  117. openpyxl/styles/__init__.py +15 -0
  118. openpyxl/styles/alignment.py +79 -0
  119. openpyxl/styles/borders.py +133 -0
  120. openpyxl/styles/builtins.py +1362 -0
  121. openpyxl/styles/cell_style.py +203 -0
  122. openpyxl/styles/colors.py +213 -0
  123. openpyxl/styles/differential.py +81 -0
  124. openpyxl/styles/fills.py +228 -0
  125. openpyxl/styles/fonts.py +146 -0
  126. openpyxl/styles/named_styles.py +255 -0
  127. openpyxl/styles/numbers.py +181 -0
  128. openpyxl/styles/protection.py +17 -0
  129. openpyxl/styles/proxy.py +56 -0
  130. openpyxl/styles/styleable.py +217 -0
  131. openpyxl/styles/stylesheet.py +263 -0
  132. openpyxl/styles/table.py +100 -0
  133. openpyxl/utils/__init__.py +12 -0
  134. openpyxl/utils/bound_dictionary.py +24 -0
  135. openpyxl/utils/cell.py +229 -0
  136. openpyxl/utils/dataframe.py +73 -0
  137. openpyxl/utils/datetime.py +147 -0
  138. openpyxl/utils/escape.py +37 -0
  139. openpyxl/utils/exceptions.py +56 -0
  140. openpyxl/utils/formulas.py +380 -0
  141. openpyxl/utils/indexed_list.py +49 -0
  142. openpyxl/utils/inference.py +66 -0
  143. openpyxl/utils/protection.py +22 -0
  144. openpyxl/utils/units.py +116 -0
  145. openpyxl/volatile/__init__.py +1 -0
  146. openpyxl/volatile/volatile.py +79 -0
  147. openpyxl/workbook/__init__.py +2 -0
  148. openpyxl/workbook/_writer.py +185 -0
  149. openpyxl/workbook/child.py +147 -0
  150. openpyxl/workbook/defined_name.py +185 -0
  151. openpyxl/workbook/external_link/__init__.py +2 -0
  152. openpyxl/workbook/external_link/external.py +135 -0
  153. openpyxl/workbook/external_reference.py +11 -0
  154. openpyxl/workbook/function_group.py +24 -0
  155. openpyxl/workbook/properties.py +143 -0
  156. openpyxl/workbook/protection.py +180 -0
  157. openpyxl/workbook/smart_tags.py +37 -0
  158. openpyxl/workbook/views.py +142 -0
  159. openpyxl/workbook/web.py +98 -0
  160. openpyxl/workbook/workbook.py +428 -0
  161. openpyxl/worksheet/__init__.py +1 -0
  162. openpyxl/worksheet/_read_only.py +173 -0
  163. openpyxl/worksheet/_reader.py +482 -0
  164. openpyxl/worksheet/_structural_edit.py +428 -0
  165. openpyxl/worksheet/_write_only.py +138 -0
  166. openpyxl/worksheet/_writer.py +384 -0
  167. openpyxl/worksheet/cell_range.py +497 -0
  168. openpyxl/worksheet/cell_watch.py +23 -0
  169. openpyxl/worksheet/controls.py +323 -0
  170. openpyxl/worksheet/copier.py +57 -0
  171. openpyxl/worksheet/custom.py +23 -0
  172. openpyxl/worksheet/datavalidation.py +211 -0
  173. openpyxl/worksheet/dimensions.py +315 -0
  174. openpyxl/worksheet/drawing.py +11 -0
  175. openpyxl/worksheet/errors.py +74 -0
  176. openpyxl/worksheet/filters.py +558 -0
  177. openpyxl/worksheet/formula.py +47 -0
  178. openpyxl/worksheet/header_footer.py +260 -0
  179. openpyxl/worksheet/hyperlink.py +40 -0
  180. openpyxl/worksheet/merge.py +150 -0
  181. openpyxl/worksheet/ole.py +126 -0
  182. openpyxl/worksheet/page.py +167 -0
  183. openpyxl/worksheet/pagebreak.py +75 -0
  184. openpyxl/worksheet/picture.py +8 -0
  185. openpyxl/worksheet/print_settings.py +165 -0
  186. openpyxl/worksheet/properties.py +91 -0
  187. openpyxl/worksheet/protection.py +148 -0
  188. openpyxl/worksheet/related.py +13 -0
  189. openpyxl/worksheet/scenario.py +80 -0
  190. openpyxl/worksheet/smart_tag.py +57 -0
  191. openpyxl/worksheet/table.py +360 -0
  192. openpyxl/worksheet/views.py +145 -0
  193. openpyxl/worksheet/worksheet.py +982 -0
  194. openpyxl/writer/__init__.py +1 -0
  195. openpyxl/writer/excel.py +351 -0
  196. openpyxl/writer/theme.py +310 -0
  197. openpyxl/xml/__init__.py +44 -0
  198. openpyxl/xml/constants.py +121 -0
  199. openpyxl/xml/functions.py +80 -0
  200. openpyxl2-3.2.0b2.dist-info/METADATA +125 -0
  201. openpyxl2-3.2.0b2.dist-info/RECORD +204 -0
  202. openpyxl2-3.2.0b2.dist-info/WHEEL +5 -0
  203. openpyxl2-3.2.0b2.dist-info/licenses/LICENCE.rst +23 -0
  204. openpyxl2-3.2.0b2.dist-info/top_level.txt +1 -0
openpyxl/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+
3
+ DEBUG = False
4
+
5
+ from openpyxl.compat.numbers import NUMPY
6
+ from openpyxl.xml import DEFUSEDXML
7
+ from openpyxl.xml import LXML
8
+ from openpyxl.workbook.workbook import Workbook
9
+ from openpyxl.reader.excel import load_workbook
10
+ import openpyxl._constants as constants
11
+
12
+ # Expose constants especially the version number
13
+
14
+ __author__ = constants.__author__
15
+ __author_email__ = constants.__author_email__
16
+ __license__ = constants.__license__
17
+ __maintainer_email__ = constants.__maintainer_email__
18
+ __url__ = constants.__url__
19
+ __version__ = constants.__version__
openpyxl/_constants.py ADDED
@@ -0,0 +1,12 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+ """
3
+ Package metadata
4
+ """
5
+
6
+ __author__ = "See AUTHORS"
7
+ __author_email__ = "charlie.clark@clark-consulting.eu"
8
+ __license__ = "MIT"
9
+ __maintainer_email__ = "openpyxl-users@googlegroups.com"
10
+ __url__ = "https://openpyxl.pages.heptapod.net/openpyxl/"
11
+ __version__ = "3.2.0b2"
12
+ __python__ = "3.8"
@@ -0,0 +1,5 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+ from openpyxl.cell.cell import Cell
3
+ from openpyxl.cell.cell import MergedCell
4
+ from openpyxl.cell.cell import WriteOnlyCell
5
+ from openpyxl.cell.read_only import ReadOnlyCell
@@ -0,0 +1,121 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+ import datetime
3
+
4
+ from openpyxl.cell.rich_text import CellRichText
5
+ from openpyxl.compat.strings import safe_string
6
+ from openpyxl.utils.datetime import to_excel
7
+ from openpyxl.utils.datetime import to_ISO8601
8
+ from openpyxl.worksheet.formula import ArrayFormula
9
+ from openpyxl.worksheet.formula import DataTableFormula
10
+ from openpyxl.xml import LXML
11
+ from openpyxl.xml.functions import XML_NS
12
+ from openpyxl.xml.functions import Element
13
+ from openpyxl.xml.functions import SubElement
14
+ from openpyxl.xml.functions import whitespace
15
+
16
+
17
+ def _set_attributes(cell, styled=None):
18
+ """
19
+ Set coordinate and datatype
20
+ """
21
+ coordinate = cell.coordinate
22
+ attrs = {"r": coordinate}
23
+ if styled:
24
+ attrs["s"] = f"{cell.style_id}"
25
+ if cell.data_type == "s":
26
+ attrs["t"] = "inlineStr"
27
+ elif cell.data_type != "f":
28
+ attrs["t"] = cell.data_type
29
+ value = cell._value
30
+ if cell.data_type == "d":
31
+ if hasattr(value, "tzinfo") and value.tzinfo is not None:
32
+ raise TypeError(
33
+ "Excel does not support timezones in datetimes. "
34
+ "The tzinfo in the datetime/time object must be set to None."
35
+ )
36
+ if cell.parent.parent.iso_dates and not isinstance(value, datetime.timedelta):
37
+ value = to_ISO8601(value)
38
+ else:
39
+ attrs["t"] = "n"
40
+ value = to_excel(value, cell.parent.parent.epoch)
41
+ if cell.hyperlink:
42
+ cell.parent._hyperlinks.append(cell.hyperlink)
43
+ return value, attrs
44
+
45
+
46
+ def etree_write_cell(xf, worksheet, cell, styled=None):
47
+ value, attributes = _set_attributes(cell, styled)
48
+ el = Element("c", attributes)
49
+ if value is None or value == "":
50
+ xf.write(el)
51
+ return
52
+ if cell.data_type == "f":
53
+ attrib = {}
54
+ if isinstance(value, ArrayFormula):
55
+ attrib = dict(value)
56
+ value = value.text
57
+ elif isinstance(value, DataTableFormula):
58
+ attrib = dict(value)
59
+ value = None
60
+ formula = SubElement(el, "f", attrib)
61
+ if value is not None and not attrib.get("t") == "dataTable":
62
+ formula.text = value[1:]
63
+ value = None
64
+ if cell.data_type == "s":
65
+ if isinstance(value, CellRichText):
66
+ el.append(value.to_tree())
67
+ else:
68
+ inline_string = Element("is")
69
+ text = Element("t")
70
+ text.text = value
71
+ whitespace(text)
72
+ inline_string.append(text)
73
+ el.append(inline_string)
74
+ else:
75
+ cell_content = SubElement(el, "v")
76
+ if value is not None:
77
+ cell_content.text = safe_string(value)
78
+ xf.write(el)
79
+
80
+
81
+ def lxml_write_cell(xf, worksheet, cell, styled=False):
82
+ value, attributes = _set_attributes(cell, styled)
83
+ if value == "" or value is None:
84
+ with xf.element("c", attributes):
85
+ return
86
+ with xf.element("c", attributes):
87
+ if cell.data_type == "f":
88
+ attrib = {}
89
+ if isinstance(value, ArrayFormula):
90
+ attrib = dict(value)
91
+ value = value.text
92
+ elif isinstance(value, DataTableFormula):
93
+ attrib = dict(value)
94
+ value = None
95
+ with xf.element("f", attrib):
96
+ if value is not None and not attrib.get("t") == "dataTable":
97
+ xf.write(value[1:])
98
+ value = None
99
+ if cell.data_type == "s":
100
+ if isinstance(value, CellRichText):
101
+ el = value.to_tree()
102
+ xf.write(el)
103
+ else:
104
+ with xf.element("is"):
105
+ if isinstance(value, str):
106
+ attrs = {}
107
+ if value != value.strip():
108
+ attrs[f"{{{XML_NS}}}space"] = "preserve"
109
+ el = Element("t", attrs) # lxml can't handle xml-ns
110
+ el.text = value
111
+ xf.write(el)
112
+ else:
113
+ with xf.element("v"):
114
+ if value is not None:
115
+ xf.write(safe_string(value))
116
+
117
+
118
+ if LXML:
119
+ write_cell = lxml_write_cell
120
+ else:
121
+ write_cell = etree_write_cell
openpyxl/cell/cell.py ADDED
@@ -0,0 +1,417 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+ """Manage individual cells in a spreadsheet.
3
+
4
+ The Cell class is required to know its value and type, display options,
5
+ and any other features of an Excel cell. Utilities for referencing
6
+ cells using Excel's 'A1' column/row nomenclature are also provided.
7
+
8
+ """
9
+ import copy
10
+ import datetime
11
+ import re
12
+
13
+ from openpyxl.cell.coordinate import Coordinate
14
+ from openpyxl.cell.rich_text import CellRichText
15
+ from openpyxl.compat.numbers import NUMERIC_TYPES
16
+ from openpyxl.styles.alignment import Alignment
17
+ from openpyxl.styles.borders import Border
18
+ from openpyxl.styles.fills import PatternFill
19
+ from openpyxl.styles.fonts import Font
20
+ from openpyxl.styles.numbers import FORMAT_DATE_DATETIME
21
+ from openpyxl.styles.numbers import FORMAT_DATE_TIME6
22
+ from openpyxl.styles.numbers import FORMAT_DATE_TIMEDELTA
23
+ from openpyxl.styles.numbers import FORMAT_DATE_YYYYMMDD2
24
+ from openpyxl.styles.numbers import FORMAT_GENERAL
25
+ from openpyxl.styles.numbers import is_date_format
26
+ from openpyxl.styles.protection import Protection
27
+ from openpyxl.styles.styleable import NamedStyleDescriptor
28
+ from openpyxl.styles.styleable import StyleableObject
29
+ from openpyxl.utils.exceptions import IllegalCharacterError
30
+ from openpyxl.worksheet.formula import ArrayFormula
31
+ from openpyxl.worksheet.formula import DataTableFormula
32
+ from openpyxl.worksheet.hyperlink import Hyperlink
33
+
34
+ TIME_TYPES = (datetime.datetime, datetime.date, datetime.time, datetime.timedelta)
35
+ TIME_FORMATS = {
36
+ datetime.datetime: FORMAT_DATE_DATETIME,
37
+ datetime.date: FORMAT_DATE_YYYYMMDD2,
38
+ datetime.time: FORMAT_DATE_TIME6,
39
+ datetime.timedelta: FORMAT_DATE_TIMEDELTA,
40
+ }
41
+ STRING_TYPES = (str, bytes, CellRichText)
42
+ KNOWN_TYPES = NUMERIC_TYPES + TIME_TYPES + STRING_TYPES + (bool, type(None))
43
+ ILLEGAL_CHARACTERS_RE = re.compile(r"[\000-\010]|[\013-\014]|[\016-\037]")
44
+ ERROR_CODES = ("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A")
45
+ TYPE_STRING = "s"
46
+ TYPE_FORMULA = "f"
47
+ TYPE_NUMERIC = "n"
48
+ TYPE_BOOL = "b"
49
+ TYPE_NULL = "n"
50
+ TYPE_INLINE = "inlineStr"
51
+ TYPE_ERROR = "e"
52
+ TYPE_FORMULA_CACHE_STRING = "str"
53
+ VALID_TYPES = (
54
+ TYPE_STRING,
55
+ TYPE_FORMULA,
56
+ TYPE_NUMERIC,
57
+ TYPE_BOOL,
58
+ TYPE_NULL,
59
+ TYPE_INLINE,
60
+ TYPE_ERROR,
61
+ TYPE_FORMULA_CACHE_STRING,
62
+ )
63
+ _TYPES = {int: "n", float: "n", str: "s", bool: "b"}
64
+
65
+
66
+ def get_type(t, value):
67
+ if isinstance(value, NUMERIC_TYPES):
68
+ dt = "n"
69
+ elif isinstance(value, STRING_TYPES):
70
+ dt = "s"
71
+ elif isinstance(value, TIME_TYPES):
72
+ dt = "d"
73
+ elif isinstance(value, (DataTableFormula, ArrayFormula)):
74
+ dt = "f"
75
+ else:
76
+ return None
77
+ _TYPES[t] = dt
78
+ return dt
79
+
80
+
81
+ def get_time_format(t):
82
+ value = TIME_FORMATS.get(t)
83
+ if value:
84
+ return value
85
+ for base in t.mro()[1:]:
86
+ value = TIME_FORMATS.get(base)
87
+ if value:
88
+ TIME_FORMATS[t] = value
89
+ return value
90
+ raise ValueError(f"Could not get time format for {value!r}")
91
+
92
+
93
+ class Cell(StyleableObject):
94
+ """Describes cell associated properties.
95
+
96
+ Properties of interest include style, type, value, and address.
97
+
98
+ """
99
+
100
+ __slots__ = ("_coord", "_value", "data_type", "parent", "_hyperlink", "_comment")
101
+
102
+ def __init__(self, worksheet, row=None, column=None, value=None, style_array=None):
103
+ super().__init__(worksheet, style_array)
104
+ self._coord = Coordinate(row, column) # 1-based index
105
+ self._value = None
106
+ self._hyperlink = None
107
+ self.data_type = "n"
108
+ if value is not None:
109
+ self.value = value
110
+ self._comment = None
111
+
112
+ @property
113
+ def column(self):
114
+ return self._coord.column
115
+
116
+ col_idx = column
117
+
118
+ @property
119
+ def row(self):
120
+ return self._coord.row
121
+
122
+ @property
123
+ def coordinate(self):
124
+ """This cell's coordinate (ex. 'A5')"""
125
+ return str(self._coord)
126
+
127
+ @property
128
+ def column_letter(self):
129
+ return self._coord.column_letter
130
+
131
+ @property
132
+ def encoding(self):
133
+ return self.parent.encoding
134
+
135
+ @property
136
+ def base_date(self):
137
+ return self.parent.parent.epoch
138
+
139
+ def __repr__(self):
140
+ return f"<Cell {self.parent.title!r}.{self.coordinate}>"
141
+
142
+ def check_string(self, value):
143
+ """Check string coding, length, and line break character"""
144
+ if value is None:
145
+ return None
146
+ # convert to str string
147
+ if not isinstance(value, str):
148
+ value = str(value, self.encoding)
149
+ value = str(value)
150
+ # string must never be longer than 32,767 characters
151
+ # truncate if necessary
152
+ value = value[:32767]
153
+ if next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
154
+ raise IllegalCharacterError(f"{value} cannot be used in worksheets.")
155
+ return value
156
+
157
+ def check_error(self, value):
158
+ """Tries to convert Error" else N/A"""
159
+ try:
160
+ return str(value)
161
+ except UnicodeDecodeError:
162
+ return "#N/A"
163
+
164
+ @property
165
+ def value(self):
166
+ """Get or set the value held in the cell.
167
+
168
+ :type: depends on the value (string, float, int or
169
+ :class:`datetime.datetime`)
170
+ """
171
+ return self._value
172
+
173
+ @value.setter
174
+ def value(self, value):
175
+ """Set the value and infer type and display options."""
176
+ self.data_type = "n"
177
+ t = type(value)
178
+ try:
179
+ dt = _TYPES[t]
180
+ except KeyError:
181
+ dt = get_type(t, value)
182
+
183
+ if dt is None and value is not None:
184
+ raise ValueError(f"Cannot convert {value!r} to Excel")
185
+ if dt:
186
+ self.data_type = dt
187
+ if dt == "d":
188
+ if not is_date_format(self.number_format):
189
+ self.number_format = get_time_format(t)
190
+ elif dt == "s" and not isinstance(value, CellRichText):
191
+ value = self.check_string(value)
192
+ if len(value) > 1 and value.startswith("="):
193
+ self.data_type = "f"
194
+ elif value in ERROR_CODES:
195
+ self.data_type = "e"
196
+ self._value = value
197
+
198
+ @property
199
+ def hyperlink(self):
200
+ """Return the hyperlink target or an empty string"""
201
+ return self._hyperlink
202
+
203
+ @hyperlink.setter
204
+ def hyperlink(self, val):
205
+ """Set value and display for hyperlinks in a cell.
206
+ Automatically sets the `value` of the cell with link text,
207
+ but you can modify it afterward by setting the `value`
208
+ property, and the hyperlink will remain.
209
+ Hyperlink is removed if set to ``None``."""
210
+ if val is None:
211
+ self._hyperlink = None
212
+ else:
213
+ if not isinstance(val, Hyperlink):
214
+ val = Hyperlink(ref="", target=val)
215
+ val.ref = self.coordinate
216
+ self._hyperlink = val
217
+ if self._value is None:
218
+ self.value = val.target or val.location
219
+
220
+ @property
221
+ def is_date(self):
222
+ """True if the value is formatted as a date
223
+
224
+ :type: bool
225
+ """
226
+ con1 = self.data_type == "d"
227
+ con2 = self.data_type == "n" and is_date_format(self.number_format)
228
+ return con1 or con2
229
+
230
+ def offset(self, row=0, column=0):
231
+ """Returns a cell location relative to this cell.
232
+
233
+ :param row: number of rows to offset
234
+ :type row: int
235
+
236
+ :param column: number of columns to offset
237
+ :type column: int
238
+
239
+ :rtype: :class:`openpyxl.cell.Cell`
240
+ """
241
+ row = self._coord.row + row
242
+ column = self._coord.column + column
243
+ return self.parent.cell(row=row, column=column)
244
+
245
+ @property
246
+ def comment(self):
247
+ """Returns the comment associated with this cell
248
+
249
+ :type: :class:`openpyxl.comments.Comment`
250
+ """
251
+ return self._comment
252
+
253
+ @comment.setter
254
+ def comment(self, value):
255
+ """
256
+ Assign a comment to a cell
257
+ """
258
+
259
+ if value is not None:
260
+ if value.parent:
261
+ value = copy.copy(value)
262
+ value.bind(self)
263
+ elif value is None and self._comment:
264
+ self._comment.unbind()
265
+ self._comment = value
266
+
267
+ def set_style_to_merged_cells(self, key, value):
268
+ if not hasattr(self.parent, "merged_cells"):
269
+ return
270
+ for merged in self.parent.merged_cells:
271
+ if (self.row, self.column) != (merged.min_row, merged.min_col):
272
+ continue
273
+ if key == "border":
274
+ merged._apply_border(value)
275
+ return
276
+ for row, column in merged.cells:
277
+ cell = self.parent._cells[(row, column)]
278
+ if cell is self:
279
+ continue
280
+ if key == "style":
281
+ NamedStyleDescriptor().__set__(cell, value)
282
+ else:
283
+ setattr(cell, key, value)
284
+ if key == "style":
285
+ merged._apply_border(copy.copy(self.border))
286
+ return
287
+
288
+ @property
289
+ def alignment(self):
290
+ return self._alignment
291
+
292
+ @alignment.setter
293
+ def alignment(self, value):
294
+ self._alignment = value
295
+ self.set_style_to_merged_cells("alignment", value)
296
+
297
+ @alignment.deleter
298
+ def alignment(self):
299
+ self._alignment = Alignment()
300
+ self.set_style_to_merged_cells("alignment", Alignment())
301
+
302
+ @property
303
+ def border(self):
304
+ return self._border
305
+
306
+ @border.setter
307
+ def border(self, value):
308
+ self._border = value
309
+ self.set_style_to_merged_cells("border", value)
310
+
311
+ @border.deleter
312
+ def border(self):
313
+ self._border = Border()
314
+ self.set_style_to_merged_cells("border", Border())
315
+
316
+ @property
317
+ def fill(self):
318
+ return self._fill
319
+
320
+ @fill.setter
321
+ def fill(self, value):
322
+ self._fill = value
323
+ self.set_style_to_merged_cells("fill", value)
324
+
325
+ @fill.deleter
326
+ def fill(self):
327
+ self._fill = PatternFill()
328
+ self.set_style_to_merged_cells("fill", PatternFill())
329
+
330
+ @property
331
+ def font(self):
332
+ return self._font
333
+
334
+ @font.setter
335
+ def font(self, value):
336
+ self._font = value
337
+ self.set_style_to_merged_cells("font", value)
338
+
339
+ @font.deleter
340
+ def font(self):
341
+ self._font = Font()
342
+ self.set_style_to_merged_cells("font", Font())
343
+
344
+ @property
345
+ def number_format(self):
346
+ return self._number_format
347
+
348
+ @number_format.setter
349
+ def number_format(self, value):
350
+ self._number_format = value
351
+ self.set_style_to_merged_cells("number_format", value)
352
+
353
+ @number_format.deleter
354
+ def number_format(self):
355
+ self._number_format = FORMAT_GENERAL
356
+ self.set_style_to_merged_cells("number_format", FORMAT_GENERAL)
357
+
358
+ @property
359
+ def protection(self):
360
+ return self._protection
361
+
362
+ @protection.setter
363
+ def protection(self, value):
364
+ self._protection = value
365
+ self.set_style_to_merged_cells("protection", value)
366
+
367
+ @protection.deleter
368
+ def protection(self):
369
+ self._protection = Protection()
370
+ self.set_style_to_merged_cells("protection", Protection())
371
+
372
+ @property
373
+ def style(self):
374
+ return NamedStyleDescriptor().__get__(self, None)
375
+
376
+ @style.setter
377
+ def style(self, value):
378
+ NamedStyleDescriptor().__set__(self, value)
379
+ self.set_style_to_merged_cells("style", value)
380
+
381
+ @style.deleter
382
+ def style(self):
383
+ NamedStyleDescriptor().__set__(self, "Normal")
384
+ self.set_style_to_merged_cells("style", "Normal")
385
+
386
+
387
+ class MergedCell(StyleableObject):
388
+ """
389
+ Describes the properties of a cell in a merged cell and helps to
390
+ display the borders of the merged cell.
391
+
392
+ The value of a MergedCell is always None.
393
+ """
394
+
395
+ __slots__ = ("_coord",)
396
+
397
+ _value = None
398
+ data_type = "n"
399
+ comment = None
400
+ hyperlink = None
401
+
402
+ def __init__(self, worksheet, row=None, column=None):
403
+ super().__init__(worksheet)
404
+ self._coord = Coordinate(row, column)
405
+
406
+ def __repr__(self):
407
+ return f"<MergedCell {self.parent.title!r}.{self.coordinate}>"
408
+
409
+ coordinate = Cell.coordinate
410
+ row = Cell.row
411
+ column = Cell.column
412
+ _comment = comment
413
+ value = _value
414
+
415
+
416
+ def WriteOnlyCell(ws=None, value=None):
417
+ return Cell(worksheet=ws, column=1, row=1, value=value)
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+ """
3
+ Represent a cell's coordinate in any collection
4
+ """
5
+ from openpyxl.utils.cell import coordinate_to_tuple
6
+ from openpyxl.utils.cell import get_column_letter
7
+
8
+
9
+ class Coordinate:
10
+ __slots__ = ("row", "column")
11
+
12
+ def __init__(self, row, column):
13
+ self.row = row
14
+ self.column = column
15
+
16
+ def __str__(self):
17
+ return f"{get_column_letter(self.column)}{self.row}"
18
+
19
+ @property
20
+ def column_letter(self):
21
+ return get_column_letter(self.column)
22
+
23
+ @classmethod
24
+ def from_string(cls, coord):
25
+ row, column = coordinate_to_tuple(coord)
26
+ return cls(row, column)
27
+
28
+ def __eq__(self, other):
29
+ return self.row == other.row and self.column == other.column
30
+
31
+ def __ne__(self, other):
32
+ return not self == other
@@ -0,0 +1,98 @@
1
+ # Copyright (c) 2010-2025 openpyxl
2
+ from openpyxl.cell.cell import Cell
3
+ from openpyxl.cell.coordinate import Coordinate
4
+ from openpyxl.styles.numbers import BUILTIN_FORMATS
5
+ from openpyxl.styles.numbers import BUILTIN_FORMATS_MAX_SIZE
6
+
7
+
8
+ class ReadOnlyCell:
9
+ __slots__ = ("parent", "_coord", "_value", "data_type", "_style_id")
10
+
11
+ def __init__(self, sheet, row, column, value, data_type="n", style_id=0):
12
+ self.parent = sheet
13
+ self._value = None
14
+ self._coord = Coordinate(row, column)
15
+ self.data_type = data_type
16
+ self._value = value
17
+ self._style_id = style_id
18
+
19
+ def __eq__(self, other):
20
+ for a in self.__slots__:
21
+ if getattr(self, a) != getattr(other, a):
22
+ return False
23
+ return True
24
+
25
+ def __ne__(self, other):
26
+ return not self.__eq__(other)
27
+
28
+ def __repr__(self):
29
+ return f"<ReadOnlyCell {self.parent.title!r}.{self.coordinate}>"
30
+
31
+ row = Cell.row
32
+ column = Cell.column
33
+ column_letter = Cell.column_letter
34
+ coordinate = Cell.coordinate
35
+ is_date = Cell.is_date
36
+
37
+ @property
38
+ def style_array(self):
39
+ return self.parent.parent._cell_styles[self._style_id]
40
+
41
+ @property
42
+ def has_style(self):
43
+ return self._style_id != 0
44
+
45
+ @property
46
+ def number_format(self):
47
+ _id = self.style_array.numFmtId
48
+ if _id < BUILTIN_FORMATS_MAX_SIZE:
49
+ return BUILTIN_FORMATS.get(_id, "General")
50
+ else:
51
+ return self.parent.parent._number_formats[_id - BUILTIN_FORMATS_MAX_SIZE]
52
+
53
+ @property
54
+ def font(self):
55
+ _id = self.style_array.fontId
56
+ return self.parent.parent._fonts[_id]
57
+
58
+ @property
59
+ def fill(self):
60
+ _id = self.style_array.fillId
61
+ return self.parent.parent._fills[_id]
62
+
63
+ @property
64
+ def border(self):
65
+ _id = self.style_array.borderId
66
+ return self.parent.parent._borders[_id]
67
+
68
+ @property
69
+ def alignment(self):
70
+ _id = self.style_array.alignmentId
71
+ return self.parent.parent._alignments[_id]
72
+
73
+ @property
74
+ def protection(self):
75
+ _id = self.style_array.protectionId
76
+ return self.parent.parent._protections[_id]
77
+
78
+ @property
79
+ def value(self):
80
+ return self._value
81
+
82
+
83
+ class EmptyCell:
84
+ __slots__ = ()
85
+ value = None
86
+ is_date = False
87
+ font = None
88
+ border = None
89
+ fill = None
90
+ number_format = None
91
+ alignment = None
92
+ data_type = "n"
93
+
94
+ def __repr__(self):
95
+ return "<EmptyCell>"
96
+
97
+
98
+ EMPTY_CELL = EmptyCell()