diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/workbook/workbook.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/openpyxl/workbook/workbook.py | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/workbook/workbook.py b/.venv/lib/python3.12/site-packages/openpyxl/workbook/workbook.py new file mode 100644 index 00000000..b83ac442 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/openpyxl/workbook/workbook.py @@ -0,0 +1,438 @@ +# Copyright (c) 2010-2024 openpyxl + +"""Workbook is the top-level container for all document information.""" +from copy import copy + +from openpyxl.compat import deprecated +from openpyxl.worksheet.worksheet import Worksheet +from openpyxl.worksheet._read_only import ReadOnlyWorksheet +from openpyxl.worksheet._write_only import WriteOnlyWorksheet +from openpyxl.worksheet.copier import WorksheetCopy + +from openpyxl.utils import quote_sheetname +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.utils.datetime import WINDOWS_EPOCH, MAC_EPOCH +from openpyxl.utils.exceptions import ReadOnlyWorkbookException + +from openpyxl.writer.excel import save_workbook + +from openpyxl.styles.cell_style import StyleArray +from openpyxl.styles.named_styles import NamedStyle +from openpyxl.styles.differential import DifferentialStyleList +from openpyxl.styles.alignment import Alignment +from openpyxl.styles.borders import DEFAULT_BORDER +from openpyxl.styles.fills import DEFAULT_EMPTY_FILL, DEFAULT_GRAY_FILL +from openpyxl.styles.fonts import DEFAULT_FONT +from openpyxl.styles.protection import Protection +from openpyxl.styles.colors import COLOR_INDEX +from openpyxl.styles.named_styles import NamedStyleList +from openpyxl.styles.table import TableStyleList + +from openpyxl.chartsheet import Chartsheet +from .defined_name import DefinedName, DefinedNameDict +from openpyxl.packaging.core import DocumentProperties +from openpyxl.packaging.custom import CustomPropertyList +from openpyxl.packaging.relationship import RelationshipList +from .child import _WorkbookChild +from .protection import DocumentSecurity +from .properties import CalcProperties +from .views import BookView + + +from openpyxl.xml.constants import ( + XLSM, + XLSX, + XLTM, + XLTX +) + +INTEGER_TYPES = (int,) + +class Workbook: + """Workbook is the container for all other parts of the document.""" + + _read_only = False + _data_only = False + template = False + path = "/xl/workbook.xml" + + def __init__(self, + write_only=False, + iso_dates=False, + ): + self._sheets = [] + self._pivots = [] + self._active_sheet_index = 0 + self.defined_names = DefinedNameDict() + self._external_links = [] + self.properties = DocumentProperties() + self.custom_doc_props = CustomPropertyList() + self.security = DocumentSecurity() + self.__write_only = write_only + self.shared_strings = IndexedList() + + self._setup_styles() + + self.loaded_theme = None + self.vba_archive = None + self.is_template = False + self.code_name = None + self.epoch = WINDOWS_EPOCH + self.encoding = "utf-8" + self.iso_dates = iso_dates + + if not self.write_only: + self._sheets.append(Worksheet(self)) + + self.rels = RelationshipList() + self.calculation = CalcProperties() + self.views = [BookView()] + + + def _setup_styles(self): + """Bootstrap styles""" + + self._fonts = IndexedList() + self._fonts.add(DEFAULT_FONT) + + self._alignments = IndexedList([Alignment()]) + + self._borders = IndexedList() + self._borders.add(DEFAULT_BORDER) + + self._fills = IndexedList() + self._fills.add(DEFAULT_EMPTY_FILL) + self._fills.add(DEFAULT_GRAY_FILL) + + self._number_formats = IndexedList() + self._date_formats = {} + self._timedelta_formats = {} + + self._protections = IndexedList([Protection()]) + + self._colors = COLOR_INDEX + self._cell_styles = IndexedList([StyleArray()]) + self._named_styles = NamedStyleList() + self.add_named_style(NamedStyle(font=copy(DEFAULT_FONT), border=copy(DEFAULT_BORDER), builtinId=0)) + self._table_styles = TableStyleList() + self._differential_styles = DifferentialStyleList() + + + @property + def epoch(self): + if self._epoch == WINDOWS_EPOCH: + return WINDOWS_EPOCH + return MAC_EPOCH + + + @epoch.setter + def epoch(self, value): + if value not in (WINDOWS_EPOCH, MAC_EPOCH): + raise ValueError("The epoch must be either 1900 or 1904") + self._epoch = value + + + @property + def read_only(self): + return self._read_only + + @property + def data_only(self): + return self._data_only + + @property + def write_only(self): + return self.__write_only + + + @property + def excel_base_date(self): + return self.epoch + + @property + def active(self): + """Get the currently active sheet or None + + :type: :class:`openpyxl.worksheet.worksheet.Worksheet` + """ + try: + return self._sheets[self._active_sheet_index] + except IndexError: + pass + + @active.setter + def active(self, value): + """Set the active sheet""" + if not isinstance(value, (_WorkbookChild, INTEGER_TYPES)): + raise TypeError("Value must be either a worksheet, chartsheet or numerical index") + if isinstance(value, INTEGER_TYPES): + self._active_sheet_index = value + return + #if self._sheets and 0 <= value < len(self._sheets): + #value = self._sheets[value] + #else: + #raise ValueError("Sheet index is outside the range of possible values", value) + if value not in self._sheets: + raise ValueError("Worksheet is not in the workbook") + if value.sheet_state != "visible": + raise ValueError("Only visible sheets can be made active") + + idx = self._sheets.index(value) + self._active_sheet_index = idx + + + def create_sheet(self, title=None, index=None): + """Create a worksheet (at an optional index). + + :param title: optional title of the sheet + :type title: str + :param index: optional position at which the sheet will be inserted + :type index: int + + """ + if self.read_only: + raise ReadOnlyWorkbookException('Cannot create new sheet in a read-only workbook') + + if self.write_only : + new_ws = WriteOnlyWorksheet(parent=self, title=title) + else: + new_ws = Worksheet(parent=self, title=title) + + self._add_sheet(sheet=new_ws, index=index) + return new_ws + + + def _add_sheet(self, sheet, index=None): + """Add an worksheet (at an optional index).""" + + if not isinstance(sheet, (Worksheet, WriteOnlyWorksheet, Chartsheet)): + raise TypeError("Cannot be added to a workbook") + + if sheet.parent != self: + raise ValueError("You cannot add worksheets from another workbook.") + + if index is None: + self._sheets.append(sheet) + else: + self._sheets.insert(index, sheet) + + + def move_sheet(self, sheet, offset=0): + """ + Move a sheet or sheetname + """ + if not isinstance(sheet, Worksheet): + sheet = self[sheet] + idx = self._sheets.index(sheet) + del self._sheets[idx] + new_pos = idx + offset + self._sheets.insert(new_pos, sheet) + + + def remove(self, worksheet): + """Remove `worksheet` from this workbook.""" + idx = self._sheets.index(worksheet) + self._sheets.remove(worksheet) + + + @deprecated("Use wb.remove(worksheet) or del wb[sheetname]") + def remove_sheet(self, worksheet): + """Remove `worksheet` from this workbook.""" + self.remove(worksheet) + + + def create_chartsheet(self, title=None, index=None): + if self.read_only: + raise ReadOnlyWorkbookException("Cannot create new sheet in a read-only workbook") + cs = Chartsheet(parent=self, title=title) + + self._add_sheet(cs, index) + return cs + + + @deprecated("Use wb[sheetname]") + def get_sheet_by_name(self, name): + """Returns a worksheet by its name. + + :param name: the name of the worksheet to look for + :type name: string + + """ + return self[name] + + def __contains__(self, key): + return key in self.sheetnames + + + def index(self, worksheet): + """Return the index of a worksheet.""" + return self.worksheets.index(worksheet) + + + @deprecated("Use wb.index(worksheet)") + def get_index(self, worksheet): + """Return the index of the worksheet.""" + return self.index(worksheet) + + def __getitem__(self, key): + """Returns a worksheet by its name. + + :param name: the name of the worksheet to look for + :type name: string + + """ + for sheet in self.worksheets + self.chartsheets: + if sheet.title == key: + return sheet + raise KeyError("Worksheet {0} does not exist.".format(key)) + + def __delitem__(self, key): + sheet = self[key] + self.remove(sheet) + + def __iter__(self): + return iter(self.worksheets) + + + @deprecated("Use wb.sheetnames") + def get_sheet_names(self): + return self.sheetnames + + @property + def worksheets(self): + """A list of sheets in this workbook + + :type: list of :class:`openpyxl.worksheet.worksheet.Worksheet` + """ + return [s for s in self._sheets if isinstance(s, (Worksheet, ReadOnlyWorksheet, WriteOnlyWorksheet))] + + @property + def chartsheets(self): + """A list of Chartsheets in this workbook + + :type: list of :class:`openpyxl.chartsheet.chartsheet.Chartsheet` + """ + return [s for s in self._sheets if isinstance(s, Chartsheet)] + + @property + def sheetnames(self): + """Returns the list of the names of worksheets in this workbook. + + Names are returned in the worksheets order. + + :type: list of strings + + """ + return [s.title for s in self._sheets] + + + @deprecated("Assign scoped named ranges directly to worksheets or global ones to the workbook. Deprecated in 3.1") + def create_named_range(self, name, worksheet=None, value=None, scope=None): + """Create a new named_range on a worksheet + + """ + defn = DefinedName(name=name) + if worksheet is not None: + defn.value = "{0}!{1}".format(quote_sheetname(worksheet.title), value) + else: + defn.value = value + + self.defined_names[name] = defn + + + def add_named_style(self, style): + """ + Add a named style + """ + self._named_styles.append(style) + style.bind(self) + + + @property + def named_styles(self): + """ + List available named styles + """ + return self._named_styles.names + + + @property + def mime_type(self): + """ + The mime type is determined by whether a workbook is a template or + not and whether it contains macros or not. Excel requires the file + extension to match but openpyxl does not enforce this. + + """ + ct = self.template and XLTX or XLSX + if self.vba_archive: + ct = self.template and XLTM or XLSM + return ct + + + def save(self, filename): + """Save the current workbook under the given `filename`. + Use this function instead of using an `ExcelWriter`. + + .. warning:: + When creating your workbook using `write_only` set to True, + you will only be able to call this function once. Subsequent attempts to + modify or save the file will raise an :class:`openpyxl.shared.exc.WorkbookAlreadySaved` exception. + """ + if self.read_only: + raise TypeError("""Workbook is read-only""") + if self.write_only and not self.worksheets: + self.create_sheet() + save_workbook(self, filename) + + + @property + def style_names(self): + """ + List of named styles + """ + return [s.name for s in self._named_styles] + + + def copy_worksheet(self, from_worksheet): + """Copy an existing worksheet in the current workbook + + .. warning:: + This function cannot copy worksheets between workbooks. + worksheets can only be copied within the workbook that they belong + + :param from_worksheet: the worksheet to be copied from + :return: copy of the initial worksheet + """ + if self.__write_only or self._read_only: + raise ValueError("Cannot copy worksheets in read-only or write-only mode") + + new_title = u"{0} Copy".format(from_worksheet.title) + to_worksheet = self.create_sheet(title=new_title) + cp = WorksheetCopy(source_worksheet=from_worksheet, target_worksheet=to_worksheet) + cp.copy_worksheet() + return to_worksheet + + + def close(self): + """ + Close workbook file if open. Only affects read-only and write-only modes. + """ + if hasattr(self, '_archive'): + self._archive.close() + + + def _duplicate_name(self, name): + """ + Check for duplicate name in defined name list and table list of each worksheet. + Names are not case sensitive. + """ + name = name.lower() + for sheet in self.worksheets: + for t in sheet.tables: + if name == t.lower(): + return True + + if name in self.defined_names: + return True + |