Updated script that can be controled by Nodejs web app
This commit is contained in:
332
lib/python3.13/site-packages/openpyxl/cell/cell.py
Normal file
332
lib/python3.13/site-packages/openpyxl/cell/cell.py
Normal file
@ -0,0 +1,332 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
"""Manage individual cells in a spreadsheet.
|
||||
|
||||
The Cell class is required to know its value and type, display options,
|
||||
and any other features of an Excel cell. Utilities for referencing
|
||||
cells using Excel's 'A1' column/row nomenclature are also provided.
|
||||
|
||||
"""
|
||||
|
||||
__docformat__ = "restructuredtext en"
|
||||
|
||||
# Python stdlib imports
|
||||
from copy import copy
|
||||
import datetime
|
||||
import re
|
||||
|
||||
|
||||
from openpyxl.compat import (
|
||||
NUMERIC_TYPES,
|
||||
)
|
||||
|
||||
from openpyxl.utils.exceptions import IllegalCharacterError
|
||||
|
||||
from openpyxl.utils import get_column_letter
|
||||
from openpyxl.styles import numbers, is_date_format
|
||||
from openpyxl.styles.styleable import StyleableObject
|
||||
from openpyxl.worksheet.hyperlink import Hyperlink
|
||||
from openpyxl.worksheet.formula import DataTableFormula, ArrayFormula
|
||||
from openpyxl.cell.rich_text import CellRichText
|
||||
|
||||
# constants
|
||||
|
||||
TIME_TYPES = (datetime.datetime, datetime.date, datetime.time, datetime.timedelta)
|
||||
TIME_FORMATS = {
|
||||
datetime.datetime:numbers.FORMAT_DATE_DATETIME,
|
||||
datetime.date:numbers.FORMAT_DATE_YYYYMMDD2,
|
||||
datetime.time:numbers.FORMAT_DATE_TIME6,
|
||||
datetime.timedelta:numbers.FORMAT_DATE_TIMEDELTA,
|
||||
}
|
||||
|
||||
STRING_TYPES = (str, bytes, CellRichText)
|
||||
KNOWN_TYPES = NUMERIC_TYPES + TIME_TYPES + STRING_TYPES + (bool, type(None))
|
||||
|
||||
ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]')
|
||||
ERROR_CODES = ('#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!',
|
||||
'#N/A')
|
||||
|
||||
TYPE_STRING = 's'
|
||||
TYPE_FORMULA = 'f'
|
||||
TYPE_NUMERIC = 'n'
|
||||
TYPE_BOOL = 'b'
|
||||
TYPE_NULL = 'n'
|
||||
TYPE_INLINE = 'inlineStr'
|
||||
TYPE_ERROR = 'e'
|
||||
TYPE_FORMULA_CACHE_STRING = 'str'
|
||||
|
||||
VALID_TYPES = (TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL,
|
||||
TYPE_NULL, TYPE_INLINE, TYPE_ERROR, TYPE_FORMULA_CACHE_STRING)
|
||||
|
||||
|
||||
_TYPES = {int:'n', float:'n', str:'s', bool:'b'}
|
||||
|
||||
|
||||
def get_type(t, value):
|
||||
if isinstance(value, NUMERIC_TYPES):
|
||||
dt = 'n'
|
||||
elif isinstance(value, STRING_TYPES):
|
||||
dt = 's'
|
||||
elif isinstance(value, TIME_TYPES):
|
||||
dt = 'd'
|
||||
elif isinstance(value, (DataTableFormula, ArrayFormula)):
|
||||
dt = 'f'
|
||||
else:
|
||||
return
|
||||
_TYPES[t] = dt
|
||||
return dt
|
||||
|
||||
|
||||
def get_time_format(t):
|
||||
value = TIME_FORMATS.get(t)
|
||||
if value:
|
||||
return value
|
||||
for base in t.mro()[1:]:
|
||||
value = TIME_FORMATS.get(base)
|
||||
if value:
|
||||
TIME_FORMATS[t] = value
|
||||
return value
|
||||
raise ValueError("Could not get time format for {0!r}".format(value))
|
||||
|
||||
|
||||
class Cell(StyleableObject):
|
||||
"""Describes cell associated properties.
|
||||
|
||||
Properties of interest include style, type, value, and address.
|
||||
|
||||
"""
|
||||
__slots__ = (
|
||||
'row',
|
||||
'column',
|
||||
'_value',
|
||||
'data_type',
|
||||
'parent',
|
||||
'_hyperlink',
|
||||
'_comment',
|
||||
)
|
||||
|
||||
def __init__(self, worksheet, row=None, column=None, value=None, style_array=None):
|
||||
super().__init__(worksheet, style_array)
|
||||
self.row = row
|
||||
"""Row number of this cell (1-based)"""
|
||||
self.column = column
|
||||
"""Column number of this cell (1-based)"""
|
||||
# _value is the stored value, while value is the displayed value
|
||||
self._value = None
|
||||
self._hyperlink = None
|
||||
self.data_type = 'n'
|
||||
if value is not None:
|
||||
self.value = value
|
||||
self._comment = None
|
||||
|
||||
|
||||
@property
|
||||
def coordinate(self):
|
||||
"""This cell's coordinate (ex. 'A5')"""
|
||||
col = get_column_letter(self.column)
|
||||
return f"{col}{self.row}"
|
||||
|
||||
|
||||
@property
|
||||
def col_idx(self):
|
||||
"""The numerical index of the column"""
|
||||
return self.column
|
||||
|
||||
|
||||
@property
|
||||
def column_letter(self):
|
||||
return get_column_letter(self.column)
|
||||
|
||||
|
||||
@property
|
||||
def encoding(self):
|
||||
return self.parent.encoding
|
||||
|
||||
@property
|
||||
def base_date(self):
|
||||
return self.parent.parent.epoch
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "<Cell {0!r}.{1}>".format(self.parent.title, self.coordinate)
|
||||
|
||||
def check_string(self, value):
|
||||
"""Check string coding, length, and line break character"""
|
||||
if value is None:
|
||||
return
|
||||
# convert to str string
|
||||
if not isinstance(value, str):
|
||||
value = str(value, self.encoding)
|
||||
value = str(value)
|
||||
# string must never be longer than 32,767 characters
|
||||
# truncate if necessary
|
||||
value = value[:32767]
|
||||
if next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
|
||||
raise IllegalCharacterError(f"{value} cannot be used in worksheets.")
|
||||
return value
|
||||
|
||||
def check_error(self, value):
|
||||
"""Tries to convert Error" else N/A"""
|
||||
try:
|
||||
return str(value)
|
||||
except UnicodeDecodeError:
|
||||
return u'#N/A'
|
||||
|
||||
|
||||
def _bind_value(self, value):
|
||||
"""Given a value, infer the correct data type"""
|
||||
|
||||
self.data_type = "n"
|
||||
t = type(value)
|
||||
try:
|
||||
dt = _TYPES[t]
|
||||
except KeyError:
|
||||
dt = get_type(t, value)
|
||||
|
||||
if dt is None and value is not None:
|
||||
raise ValueError("Cannot convert {0!r} to Excel".format(value))
|
||||
|
||||
if dt:
|
||||
self.data_type = dt
|
||||
|
||||
if dt == 'd':
|
||||
if not is_date_format(self.number_format):
|
||||
self.number_format = get_time_format(t)
|
||||
|
||||
elif dt == "s" and not isinstance(value, CellRichText):
|
||||
value = self.check_string(value)
|
||||
if len(value) > 1 and value.startswith("="):
|
||||
self.data_type = 'f'
|
||||
elif value in ERROR_CODES:
|
||||
self.data_type = 'e'
|
||||
|
||||
self._value = value
|
||||
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""Get or set the value held in the cell.
|
||||
|
||||
:type: depends on the value (string, float, int or
|
||||
:class:`datetime.datetime`)
|
||||
"""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
"""Set the value and infer type and display options."""
|
||||
self._bind_value(value)
|
||||
|
||||
@property
|
||||
def internal_value(self):
|
||||
"""Always returns the value for excel."""
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def hyperlink(self):
|
||||
"""Return the hyperlink target or an empty string"""
|
||||
return self._hyperlink
|
||||
|
||||
|
||||
@hyperlink.setter
|
||||
def hyperlink(self, val):
|
||||
"""Set value and display for hyperlinks in a cell.
|
||||
Automatically sets the `value` of the cell with link text,
|
||||
but you can modify it afterwards by setting the `value`
|
||||
property, and the hyperlink will remain.
|
||||
Hyperlink is removed if set to ``None``."""
|
||||
if val is None:
|
||||
self._hyperlink = None
|
||||
else:
|
||||
if not isinstance(val, Hyperlink):
|
||||
val = Hyperlink(ref="", target=val)
|
||||
val.ref = self.coordinate
|
||||
self._hyperlink = val
|
||||
if self._value is None:
|
||||
self.value = val.target or val.location
|
||||
|
||||
|
||||
@property
|
||||
def is_date(self):
|
||||
"""True if the value is formatted as a date
|
||||
|
||||
:type: bool
|
||||
"""
|
||||
return self.data_type == 'd' or (
|
||||
self.data_type == 'n' and is_date_format(self.number_format)
|
||||
)
|
||||
|
||||
|
||||
def offset(self, row=0, column=0):
|
||||
"""Returns a cell location relative to this cell.
|
||||
|
||||
:param row: number of rows to offset
|
||||
:type row: int
|
||||
|
||||
:param column: number of columns to offset
|
||||
:type column: int
|
||||
|
||||
:rtype: :class:`openpyxl.cell.Cell`
|
||||
"""
|
||||
offset_column = self.col_idx + column
|
||||
offset_row = self.row + row
|
||||
return self.parent.cell(column=offset_column, row=offset_row)
|
||||
|
||||
|
||||
@property
|
||||
def comment(self):
|
||||
""" Returns the comment associated with this cell
|
||||
|
||||
:type: :class:`openpyxl.comments.Comment`
|
||||
"""
|
||||
return self._comment
|
||||
|
||||
|
||||
@comment.setter
|
||||
def comment(self, value):
|
||||
"""
|
||||
Assign a comment to a cell
|
||||
"""
|
||||
|
||||
if value is not None:
|
||||
if value.parent:
|
||||
value = copy(value)
|
||||
value.bind(self)
|
||||
elif value is None and self._comment:
|
||||
self._comment.unbind()
|
||||
self._comment = value
|
||||
|
||||
|
||||
class MergedCell(StyleableObject):
|
||||
|
||||
"""
|
||||
Describes the properties of a cell in a merged cell and helps to
|
||||
display the borders of the merged cell.
|
||||
|
||||
The value of a MergedCell is always None.
|
||||
"""
|
||||
|
||||
__slots__ = ('row', 'column')
|
||||
|
||||
_value = None
|
||||
data_type = "n"
|
||||
comment = None
|
||||
hyperlink = None
|
||||
|
||||
|
||||
def __init__(self, worksheet, row=None, column=None):
|
||||
super().__init__(worksheet)
|
||||
self.row = row
|
||||
self.column = column
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "<MergedCell {0!r}.{1}>".format(self.parent.title, self.coordinate)
|
||||
|
||||
coordinate = Cell.coordinate
|
||||
_comment = comment
|
||||
value = _value
|
||||
|
||||
|
||||
def WriteOnlyCell(ws=None, value=None):
|
||||
return Cell(worksheet=ws, column=1, row=1, value=value)
|
Reference in New Issue
Block a user