Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/cell/cell.py: 36%
171 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1# Copyright (c) 2010-2022 openpyxl
3"""Manage individual cells in a spreadsheet.
5The Cell class is required to know its value and type, display options,
6and any other features of an Excel cell. Utilities for referencing
7cells using Excel's 'A1' column/row nomenclature are also provided.
9"""
11__docformat__ = "restructuredtext en"
13# Python stdlib imports
14from copy import copy
15import datetime
16import re
19from openpyxl.compat import (
20 NUMERIC_TYPES,
21 deprecated,
22)
24from openpyxl.utils.exceptions import IllegalCharacterError
26from openpyxl.utils import get_column_letter
27from openpyxl.styles import numbers, is_date_format
28from openpyxl.styles.styleable import StyleableObject
29from openpyxl.worksheet.hyperlink import Hyperlink
31# constants
33TIME_TYPES = (datetime.datetime, datetime.date, datetime.time, datetime.timedelta)
34TIME_FORMATS = {
35 datetime.datetime:numbers.FORMAT_DATE_DATETIME,
36 datetime.date:numbers.FORMAT_DATE_YYYYMMDD2,
37 datetime.time:numbers.FORMAT_DATE_TIME6,
38 datetime.timedelta:numbers.FORMAT_DATE_TIMEDELTA,
39 }
41STRING_TYPES = (str, bytes)
42KNOWN_TYPES = NUMERIC_TYPES + TIME_TYPES + STRING_TYPES + (bool, type(None))
44ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]')
45ERROR_CODES = ('#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!',
46 '#N/A')
48TYPE_STRING = 's'
49TYPE_FORMULA = 'f'
50TYPE_NUMERIC = 'n'
51TYPE_BOOL = 'b'
52TYPE_NULL = 'n'
53TYPE_INLINE = 'inlineStr'
54TYPE_ERROR = 'e'
55TYPE_FORMULA_CACHE_STRING = 'str'
57VALID_TYPES = (TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL,
58 TYPE_NULL, TYPE_INLINE, TYPE_ERROR, TYPE_FORMULA_CACHE_STRING)
61_TYPES = {int:'n', float:'n', str:'s', bool:'b'}
64def get_type(t, value):
65 if isinstance(value, NUMERIC_TYPES):
66 dt = 'n'
67 elif isinstance(value, STRING_TYPES):
68 dt = 's'
69 elif isinstance(value, TIME_TYPES):
70 dt = 'd'
71 else:
72 return
73 _TYPES[t] = dt
74 return dt
77def get_time_format(t):
78 value = TIME_FORMATS.get(t)
79 if value:
80 return value
81 for base in t.mro()[1:]:
82 value = TIME_FORMATS.get(base)
83 if value:
84 TIME_FORMATS[t] = value
85 return value
86 raise ValueError("Could not get time format for {0!r}".format(value))
89class Cell(StyleableObject):
90 """Describes cell associated properties.
92 Properties of interest include style, type, value, and address.
94 """
95 __slots__ = (
96 'row',
97 'column',
98 '_value',
99 'data_type',
100 'parent',
101 '_hyperlink',
102 '_comment',
103 )
105 def __init__(self, worksheet, row=None, column=None, value=None, style_array=None):
106 super(Cell, self).__init__(worksheet, style_array)
107 self.row = row
108 """Row number of this cell (1-based)"""
109 self.column = column
110 """Column number of this cell (1-based)"""
111 # _value is the stored value, while value is the displayed value
112 self._value = None
113 self._hyperlink = None
114 self.data_type = 'n'
115 if value is not None:
116 self.value = value
117 self._comment = None
120 @property
121 def coordinate(self):
122 """This cell's coordinate (ex. 'A5')"""
123 col = get_column_letter(self.column)
124 return f"{col}{self.row}"
127 @property
128 def col_idx(self):
129 """The numerical index of the column"""
130 return self.column
133 @property
134 def column_letter(self):
135 return get_column_letter(self.column)
138 @property
139 def encoding(self):
140 return self.parent.encoding
142 @property
143 def base_date(self):
144 return self.parent.parent.epoch
147 def __repr__(self):
148 return "<Cell {0!r}.{1}>".format(self.parent.title, self.coordinate)
150 def check_string(self, value):
151 """Check string coding, length, and line break character"""
152 if value is None:
153 return
154 # convert to str string
155 if not isinstance(value, str):
156 value = str(value, self.encoding)
157 value = str(value)
158 # string must never be longer than 32,767 characters
159 # truncate if necessary
160 value = value[:32767]
161 if next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
162 raise IllegalCharacterError
163 return value
165 def check_error(self, value):
166 """Tries to convert Error" else N/A"""
167 try:
168 return str(value)
169 except UnicodeDecodeError:
170 return u'#N/A'
173 def _bind_value(self, value):
174 """Given a value, infer the correct data type"""
176 self.data_type = "n"
177 t = type(value)
178 try:
179 dt = _TYPES[t]
180 except KeyError:
181 dt = get_type(t, value)
183 if dt is None and value is not None:
184 raise ValueError("Cannot convert {0!r} to Excel".format(value))
186 if dt:
187 self.data_type = dt
189 if dt == 'd':
190 if not is_date_format(self.number_format):
191 self.number_format = get_time_format(t)
193 elif dt == "s":
194 value = self.check_string(value)
195 if len(value) > 1 and value.startswith("="):
196 self.data_type = 'f'
197 elif value in ERROR_CODES:
198 self.data_type = 'e'
200 self._value = value
203 @property
204 def value(self):
205 """Get or set the value held in the cell.
207 :type: depends on the value (string, float, int or
208 :class:`datetime.datetime`)
209 """
210 return self._value
212 @value.setter
213 def value(self, value):
214 """Set the value and infer type and display options."""
215 self._bind_value(value)
217 @property
218 def internal_value(self):
219 """Always returns the value for excel."""
220 return self._value
222 @property
223 def hyperlink(self):
224 """Return the hyperlink target or an empty string"""
225 return self._hyperlink
228 @hyperlink.setter
229 def hyperlink(self, val):
230 """Set value and display for hyperlinks in a cell.
231 Automatically sets the `value` of the cell with link text,
232 but you can modify it afterwards by setting the `value`
233 property, and the hyperlink will remain.
234 Hyperlink is removed if set to ``None``."""
235 if val is None:
236 self._hyperlink = None
237 else:
238 if not isinstance(val, Hyperlink):
239 val = Hyperlink(ref="", target=val)
240 val.ref = self.coordinate
241 self._hyperlink = val
242 if self._value is None:
243 self.value = val.target or val.location
246 @property
247 def is_date(self):
248 """True if the value is formatted as a date
250 :type: bool
251 """
252 return self.data_type == 'd' or (
253 self.data_type == 'n' and is_date_format(self.number_format)
254 )
257 def offset(self, row=0, column=0):
258 """Returns a cell location relative to this cell.
260 :param row: number of rows to offset
261 :type row: int
263 :param column: number of columns to offset
264 :type column: int
266 :rtype: :class:`openpyxl.cell.Cell`
267 """
268 offset_column = self.col_idx + column
269 offset_row = self.row + row
270 return self.parent.cell(column=offset_column, row=offset_row)
273 @property
274 def comment(self):
275 """ Returns the comment associated with this cell
277 :type: :class:`openpyxl.comments.Comment`
278 """
279 return self._comment
282 @comment.setter
283 def comment(self, value):
284 """
285 Assign a comment to a cell
286 """
288 if value is not None:
289 if value.parent:
290 value = copy(value)
291 value.bind(self)
292 elif value is None and self._comment:
293 self._comment.unbind()
294 self._comment = value
297class MergedCell(StyleableObject):
299 """
300 Describes the properties of a cell in a merged cell and helps to
301 display the borders of the merged cell.
303 The value of a MergedCell is always None.
304 """
306 __slots__ = ('row', 'column')
308 _value = None
309 data_type = "n"
310 comment = None
311 hyperlink = None
314 def __init__(self, worksheet, row=None, column=None):
315 super(MergedCell, self).__init__(worksheet)
316 self.row = row
317 self.column = column
320 def __repr__(self):
321 return "<MergedCell {0!r}.{1}>".format(self.parent.title, self.coordinate)
323 coordinate = Cell.coordinate
324 _comment = comment
325 value = _value
328def WriteOnlyCell(ws=None, value=None):
329 return Cell(worksheet=ws, column=1, row=1, value=value)