aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/openpyxl/packaging
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/packaging')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/__init__.py3
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/core.py115
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/custom.py289
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/extended.py137
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/interface.py56
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py194
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py158
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/workbook.py185
8 files changed, 1137 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/__init__.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/__init__.py
new file mode 100644
index 00000000..c3085ee5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/__init__.py
@@ -0,0 +1,3 @@
+"""
+Stuff related to Office OpenXML packaging: relationships, archive, content types.
+"""
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/core.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/core.py
new file mode 100644
index 00000000..45153732
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/core.py
@@ -0,0 +1,115 @@
+# Copyright (c) 2010-2024 openpyxl
+
+import datetime
+
+from openpyxl.descriptors import (
+ DateTime,
+ Alias,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors.nested import NestedText
+from openpyxl.xml.functions import (
+ Element,
+ QName,
+)
+from openpyxl.xml.constants import (
+ COREPROPS_NS,
+ DCORE_NS,
+ XSI_NS,
+ DCTERMS_NS,
+)
+
+
+class NestedDateTime(DateTime, NestedText):
+
+ expected_type = datetime.datetime
+
+ def to_tree(self, tagname=None, value=None, namespace=None):
+ namespace = getattr(self, "namespace", namespace)
+ if namespace is not None:
+ tagname = "{%s}%s" % (namespace, tagname)
+ el = Element(tagname)
+ if value is not None:
+ value = value.replace(tzinfo=None)
+ el.text = value.isoformat(timespec="seconds") + 'Z'
+ return el
+
+
+class QualifiedDateTime(NestedDateTime):
+
+ """In certain situations Excel will complain if the additional type
+ attribute isn't set"""
+
+ def to_tree(self, tagname=None, value=None, namespace=None):
+ el = super().to_tree(tagname, value, namespace)
+ el.set("{%s}type" % XSI_NS, QName(DCTERMS_NS, "W3CDTF"))
+ return el
+
+
+class DocumentProperties(Serialisable):
+ """High-level properties of the document.
+ Defined in ECMA-376 Par2 Annex D
+ """
+
+ tagname = "coreProperties"
+ namespace = COREPROPS_NS
+
+ category = NestedText(expected_type=str, allow_none=True)
+ contentStatus = NestedText(expected_type=str, allow_none=True)
+ keywords = NestedText(expected_type=str, allow_none=True)
+ lastModifiedBy = NestedText(expected_type=str, allow_none=True)
+ lastPrinted = NestedDateTime(allow_none=True)
+ revision = NestedText(expected_type=str, allow_none=True)
+ version = NestedText(expected_type=str, allow_none=True)
+ last_modified_by = Alias("lastModifiedBy")
+
+ # Dublin Core Properties
+ subject = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
+ title = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
+ creator = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
+ description = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
+ identifier = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
+ language = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
+ # Dublin Core Terms
+ created = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) # assumed to be UTC
+ modified = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) # assumed to be UTC
+
+ __elements__ = ("creator", "title", "description", "subject","identifier",
+ "language", "created", "modified", "lastModifiedBy", "category",
+ "contentStatus", "version", "revision", "keywords", "lastPrinted",
+ )
+
+
+ def __init__(self,
+ category=None,
+ contentStatus=None,
+ keywords=None,
+ lastModifiedBy=None,
+ lastPrinted=None,
+ revision=None,
+ version=None,
+ created=None,
+ creator="openpyxl",
+ description=None,
+ identifier=None,
+ language=None,
+ modified=None,
+ subject=None,
+ title=None,
+ ):
+ now = datetime.datetime.now(tz=datetime.timezone.utc).replace(tzinfo=None)
+ self.contentStatus = contentStatus
+ self.lastPrinted = lastPrinted
+ self.revision = revision
+ self.version = version
+ self.creator = creator
+ self.lastModifiedBy = lastModifiedBy
+ self.modified = modified or now
+ self.created = created or now
+ self.title = title
+ self.subject = subject
+ self.description = description
+ self.identifier = identifier
+ self.language = language
+ self.keywords = keywords
+ self.category = category
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/custom.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/custom.py
new file mode 100644
index 00000000..7e253d78
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/custom.py
@@ -0,0 +1,289 @@
+# Copyright (c) 2010-2024 openpyxl
+
+"""Implementation of custom properties see ยง 22.3 in the specification"""
+
+
+from warnings import warn
+
+from openpyxl.descriptors import Strict
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors.sequence import Sequence
+from openpyxl.descriptors import (
+ Alias,
+ String,
+ Integer,
+ Float,
+ DateTime,
+ Bool,
+)
+from openpyxl.descriptors.nested import (
+ NestedText,
+)
+
+from openpyxl.xml.constants import (
+ CUSTPROPS_NS,
+ VTYPES_NS,
+ CPROPS_FMTID,
+)
+
+from .core import NestedDateTime
+
+
+class NestedBoolText(Bool, NestedText):
+ """
+ Descriptor for handling nested elements with the value stored in the text part
+ """
+
+ pass
+
+
+class _CustomDocumentProperty(Serialisable):
+
+ """
+ Low-level representation of a Custom Document Property.
+ Not used directly
+ Must always contain a child element, even if this is empty
+ """
+
+ tagname = "property"
+ _typ = None
+
+ name = String(allow_none=True)
+ lpwstr = NestedText(expected_type=str, allow_none=True, namespace=VTYPES_NS)
+ i4 = NestedText(expected_type=int, allow_none=True, namespace=VTYPES_NS)
+ r8 = NestedText(expected_type=float, allow_none=True, namespace=VTYPES_NS)
+ filetime = NestedDateTime(allow_none=True, namespace=VTYPES_NS)
+ bool = NestedBoolText(expected_type=bool, allow_none=True, namespace=VTYPES_NS)
+ linkTarget = String(expected_type=str, allow_none=True)
+ fmtid = String()
+ pid = Integer()
+
+ def __init__(self,
+ name=None,
+ pid=0,
+ fmtid=CPROPS_FMTID,
+ linkTarget=None,
+ **kw):
+ self.fmtid = fmtid
+ self.pid = pid
+ self.name = name
+ self._typ = None
+ self.linkTarget = linkTarget
+
+ for k, v in kw.items():
+ setattr(self, k, v)
+ setattr(self, "_typ", k) # ugh!
+ for e in self.__elements__:
+ if e not in kw:
+ setattr(self, e, None)
+
+
+ @property
+ def type(self):
+ if self._typ is not None:
+ return self._typ
+ for a in self.__elements__:
+ if getattr(self, a) is not None:
+ return a
+ if self.linkTarget is not None:
+ return "linkTarget"
+
+
+ def to_tree(self, tagname=None, idx=None, namespace=None):
+ child = getattr(self, self._typ, None)
+ if child is None:
+ setattr(self, self._typ, "")
+
+ return super().to_tree(tagname=None, idx=None, namespace=None)
+
+
+class _CustomDocumentPropertyList(Serialisable):
+
+ """
+ Parses and seriliases property lists but is not used directly
+ """
+
+ tagname = "Properties"
+
+ property = Sequence(expected_type=_CustomDocumentProperty, namespace=CUSTPROPS_NS)
+ customProps = Alias("property")
+
+
+ def __init__(self, property=()):
+ self.property = property
+
+
+ def __len__(self):
+ return len(self.property)
+
+
+ def to_tree(self, tagname=None, idx=None, namespace=None):
+ for idx, p in enumerate(self.property, 2):
+ p.pid = idx
+ tree = super().to_tree(tagname, idx, namespace)
+ tree.set("xmlns", CUSTPROPS_NS)
+
+ return tree
+
+
+class _TypedProperty(Strict):
+
+ name = String()
+
+ def __init__(self,
+ name,
+ value):
+ self.name = name
+ self.value = value
+
+
+ def __eq__(self, other):
+ return self.name == other.name and self.value == other.value
+
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}, name={self.name}, value={self.value}"
+
+
+class IntProperty(_TypedProperty):
+
+ value = Integer()
+
+
+class FloatProperty(_TypedProperty):
+
+ value = Float()
+
+
+class StringProperty(_TypedProperty):
+
+ value = String(allow_none=True)
+
+
+class DateTimeProperty(_TypedProperty):
+
+ value = DateTime()
+
+
+class BoolProperty(_TypedProperty):
+
+ value = Bool()
+
+
+class LinkProperty(_TypedProperty):
+
+ value = String()
+
+
+# from Python
+CLASS_MAPPING = {
+ StringProperty: "lpwstr",
+ IntProperty: "i4",
+ FloatProperty: "r8",
+ DateTimeProperty: "filetime",
+ BoolProperty: "bool",
+ LinkProperty: "linkTarget"
+}
+
+XML_MAPPING = {v:k for k,v in CLASS_MAPPING.items()}
+
+
+class CustomPropertyList(Strict):
+
+
+ props = Sequence(expected_type=_TypedProperty)
+
+ def __init__(self):
+ self.props = []
+
+
+ @classmethod
+ def from_tree(cls, tree):
+ """
+ Create list from OOXML element
+ """
+ prop_list = _CustomDocumentPropertyList.from_tree(tree)
+ props = []
+
+ for prop in prop_list.property:
+ attr = prop.type
+
+ typ = XML_MAPPING.get(attr, None)
+ if not typ:
+ warn(f"Unknown type for {prop.name}")
+ continue
+ value = getattr(prop, attr)
+ link = prop.linkTarget
+ if link is not None:
+ typ = LinkProperty
+ value = prop.linkTarget
+
+ new_prop = typ(name=prop.name, value=value)
+ props.append(new_prop)
+
+ new_prop_list = cls()
+ new_prop_list.props = props
+ return new_prop_list
+
+
+ def append(self, prop):
+ if prop.name in self.names:
+ raise ValueError(f"Property with name {prop.name} already exists")
+
+ self.props.append(prop)
+
+
+ def to_tree(self):
+ props = []
+
+ for p in self.props:
+ attr = CLASS_MAPPING.get(p.__class__, None)
+ if not attr:
+ raise TypeError("Unknown adapter for {p}")
+ np = _CustomDocumentProperty(name=p.name, **{attr:p.value})
+ if isinstance(p, LinkProperty):
+ np._typ = "lpwstr"
+ #np.lpwstr = ""
+ props.append(np)
+
+ prop_list = _CustomDocumentPropertyList(property=props)
+ return prop_list.to_tree()
+
+
+ def __len__(self):
+ return len(self.props)
+
+
+ @property
+ def names(self):
+ """List of property names"""
+ return [p.name for p in self.props]
+
+
+ def __getitem__(self, name):
+ """
+ Get property by name
+ """
+ for p in self.props:
+ if p.name == name:
+ return p
+ raise KeyError(f"Property with name {name} not found")
+
+
+ def __delitem__(self, name):
+ """
+ Delete a propery by name
+ """
+ for idx, p in enumerate(self.props):
+ if p.name == name:
+ self.props.pop(idx)
+ return
+ raise KeyError(f"Property with name {name} not found")
+
+
+ def __repr__(self):
+ return f"{self.__class__.__name__} containing {self.props}"
+
+
+ def __iter__(self):
+ return iter(self.props)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/extended.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/extended.py
new file mode 100644
index 00000000..fbd794af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/extended.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2010-2024 openpyxl
+
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+ Typed,
+)
+from openpyxl.descriptors.nested import (
+ NestedText,
+)
+
+from openpyxl.xml.constants import XPROPS_NS
+from openpyxl import __version__
+
+
+class DigSigBlob(Serialisable):
+
+ __elements__ = __attrs__ = ()
+
+
+class VectorLpstr(Serialisable):
+
+ __elements__ = __attrs__ = ()
+
+
+class VectorVariant(Serialisable):
+
+ __elements__ = __attrs__ = ()
+
+
+class ExtendedProperties(Serialisable):
+
+ """
+ See 22.2
+
+ Most of this is irrelevant but Excel is very picky about the version number
+
+ It uses XX.YYYY (Version.Build) and expects everyone else to
+
+ We provide Major.Minor and the full version in the application name
+ """
+
+ tagname = "Properties"
+
+ Template = NestedText(expected_type=str, allow_none=True)
+ Manager = NestedText(expected_type=str, allow_none=True)
+ Company = NestedText(expected_type=str, allow_none=True)
+ Pages = NestedText(expected_type=int, allow_none=True)
+ Words = NestedText(expected_type=int,allow_none=True)
+ Characters = NestedText(expected_type=int, allow_none=True)
+ PresentationFormat = NestedText(expected_type=str, allow_none=True)
+ Lines = NestedText(expected_type=int, allow_none=True)
+ Paragraphs = NestedText(expected_type=int, allow_none=True)
+ Slides = NestedText(expected_type=int, allow_none=True)
+ Notes = NestedText(expected_type=int, allow_none=True)
+ TotalTime = NestedText(expected_type=int, allow_none=True)
+ HiddenSlides = NestedText(expected_type=int, allow_none=True)
+ MMClips = NestedText(expected_type=int, allow_none=True)
+ ScaleCrop = NestedText(expected_type=bool, allow_none=True)
+ HeadingPairs = Typed(expected_type=VectorVariant, allow_none=True)
+ TitlesOfParts = Typed(expected_type=VectorLpstr, allow_none=True)
+ LinksUpToDate = NestedText(expected_type=bool, allow_none=True)
+ CharactersWithSpaces = NestedText(expected_type=int, allow_none=True)
+ SharedDoc = NestedText(expected_type=bool, allow_none=True)
+ HyperlinkBase = NestedText(expected_type=str, allow_none=True)
+ HLinks = Typed(expected_type=VectorVariant, allow_none=True)
+ HyperlinksChanged = NestedText(expected_type=bool, allow_none=True)
+ DigSig = Typed(expected_type=DigSigBlob, allow_none=True)
+ Application = NestedText(expected_type=str, allow_none=True)
+ AppVersion = NestedText(expected_type=str, allow_none=True)
+ DocSecurity = NestedText(expected_type=int, allow_none=True)
+
+ __elements__ = ('Application', 'AppVersion', 'DocSecurity', 'ScaleCrop',
+ 'LinksUpToDate', 'SharedDoc', 'HyperlinksChanged')
+
+ def __init__(self,
+ Template=None,
+ Manager=None,
+ Company=None,
+ Pages=None,
+ Words=None,
+ Characters=None,
+ PresentationFormat=None,
+ Lines=None,
+ Paragraphs=None,
+ Slides=None,
+ Notes=None,
+ TotalTime=None,
+ HiddenSlides=None,
+ MMClips=None,
+ ScaleCrop=None,
+ HeadingPairs=None,
+ TitlesOfParts=None,
+ LinksUpToDate=None,
+ CharactersWithSpaces=None,
+ SharedDoc=None,
+ HyperlinkBase=None,
+ HLinks=None,
+ HyperlinksChanged=None,
+ DigSig=None,
+ Application=None,
+ AppVersion=None,
+ DocSecurity=None,
+ ):
+ self.Template = Template
+ self.Manager = Manager
+ self.Company = Company
+ self.Pages = Pages
+ self.Words = Words
+ self.Characters = Characters
+ self.PresentationFormat = PresentationFormat
+ self.Lines = Lines
+ self.Paragraphs = Paragraphs
+ self.Slides = Slides
+ self.Notes = Notes
+ self.TotalTime = TotalTime
+ self.HiddenSlides = HiddenSlides
+ self.MMClips = MMClips
+ self.ScaleCrop = ScaleCrop
+ self.HeadingPairs = None
+ self.TitlesOfParts = None
+ self.LinksUpToDate = LinksUpToDate
+ self.CharactersWithSpaces = CharactersWithSpaces
+ self.SharedDoc = SharedDoc
+ self.HyperlinkBase = HyperlinkBase
+ self.HLinks = None
+ self.HyperlinksChanged = HyperlinksChanged
+ self.DigSig = None
+ self.Application = f"Microsoft Excel Compatible / Openpyxl {__version__}"
+ self.AppVersion = ".".join(__version__.split(".")[:-1])
+ self.DocSecurity = DocSecurity
+
+
+ def to_tree(self):
+ tree = super().to_tree()
+ tree.set("xmlns", XPROPS_NS)
+ return tree
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/interface.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/interface.py
new file mode 100644
index 00000000..cacc0462
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/interface.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from abc import abstractproperty
+from openpyxl.compat.abc import ABC
+
+
+class ISerialisableFile(ABC):
+
+ """
+ Interface for Serialisable classes that represent files in the archive
+ """
+
+
+ @abstractproperty
+ def id(self):
+ """
+ Object id making it unique
+ """
+ pass
+
+
+ @abstractproperty
+ def _path(self):
+ """
+ File path in the archive
+ """
+ pass
+
+
+ @abstractproperty
+ def _namespace(self):
+ """
+ Qualified namespace when serialised
+ """
+ pass
+
+
+ @abstractproperty
+ def _type(self):
+ """
+ The content type for the manifest
+ """
+
+
+ @abstractproperty
+ def _rel_type(self):
+ """
+ The content type for relationships
+ """
+
+
+ @abstractproperty
+ def _rel_id(self):
+ """
+ Links object with parent
+ """
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py
new file mode 100644
index 00000000..41da07f4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2010-2024 openpyxl
+
+"""
+File manifest
+"""
+from mimetypes import MimeTypes
+import os.path
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import String, Sequence
+from openpyxl.xml.functions import fromstring
+from openpyxl.xml.constants import (
+ ARC_CONTENT_TYPES,
+ ARC_THEME,
+ ARC_STYLE,
+ THEME_TYPE,
+ STYLES_TYPE,
+ CONTYPES_NS,
+ ACTIVEX,
+ CTRL,
+ VBA,
+)
+from openpyxl.xml.functions import tostring
+
+# initialise mime-types
+mimetypes = MimeTypes()
+mimetypes.add_type('application/xml', ".xml")
+mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels")
+mimetypes.add_type("application/vnd.ms-office.vbaProject", ".bin")
+mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml")
+mimetypes.add_type("image/x-emf", ".emf")
+
+
+class FileExtension(Serialisable):
+
+ tagname = "Default"
+
+ Extension = String()
+ ContentType = String()
+
+ def __init__(self, Extension, ContentType):
+ self.Extension = Extension
+ self.ContentType = ContentType
+
+
+class Override(Serialisable):
+
+ tagname = "Override"
+
+ PartName = String()
+ ContentType = String()
+
+ def __init__(self, PartName, ContentType):
+ self.PartName = PartName
+ self.ContentType = ContentType
+
+
+DEFAULT_TYPES = [
+ FileExtension("rels", "application/vnd.openxmlformats-package.relationships+xml"),
+ FileExtension("xml", "application/xml"),
+]
+
+DEFAULT_OVERRIDE = [
+ Override("/" + ARC_STYLE, STYLES_TYPE), # Styles
+ Override("/" + ARC_THEME, THEME_TYPE), # Theme
+ Override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"),
+ Override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml")
+]
+
+
+class Manifest(Serialisable):
+
+ tagname = "Types"
+
+ Default = Sequence(expected_type=FileExtension, unique=True)
+ Override = Sequence(expected_type=Override, unique=True)
+ path = "[Content_Types].xml"
+
+ __elements__ = ("Default", "Override")
+
+ def __init__(self,
+ Default=(),
+ Override=(),
+ ):
+ if not Default:
+ Default = DEFAULT_TYPES
+ self.Default = Default
+ if not Override:
+ Override = DEFAULT_OVERRIDE
+ self.Override = Override
+
+
+ @property
+ def filenames(self):
+ return [part.PartName for part in self.Override]
+
+
+ @property
+ def extensions(self):
+ """
+ Map content types to file extensions
+ Skip parts without extensions
+ """
+ exts = {os.path.splitext(part.PartName)[-1] for part in self.Override}
+ return [(ext[1:], mimetypes.types_map[True][ext]) for ext in sorted(exts) if ext]
+
+
+ def to_tree(self):
+ """
+ Custom serialisation method to allow setting a default namespace
+ """
+ defaults = [t.Extension for t in self.Default]
+ for ext, mime in self.extensions:
+ if ext not in defaults:
+ mime = FileExtension(ext, mime)
+ self.Default.append(mime)
+ tree = super().to_tree()
+ tree.set("xmlns", CONTYPES_NS)
+ return tree
+
+
+ def __contains__(self, content_type):
+ """
+ Check whether a particular content type is contained
+ """
+ for t in self.Override:
+ if t.ContentType == content_type:
+ return True
+
+
+ def find(self, content_type):
+ """
+ Find specific content-type
+ """
+ try:
+ return next(self.findall(content_type))
+ except StopIteration:
+ return
+
+
+ def findall(self, content_type):
+ """
+ Find all elements of a specific content-type
+ """
+ for t in self.Override:
+ if t.ContentType == content_type:
+ yield t
+
+
+ def append(self, obj):
+ """
+ Add content object to the package manifest
+ # needs a contract...
+ """
+ ct = Override(PartName=obj.path, ContentType=obj.mime_type)
+ self.Override.append(ct)
+
+
+ def _write(self, archive, workbook):
+ """
+ Write manifest to the archive
+ """
+ self.append(workbook)
+ self._write_vba(workbook)
+ self._register_mimetypes(filenames=archive.namelist())
+ archive.writestr(self.path, tostring(self.to_tree()))
+
+
+ def _register_mimetypes(self, filenames):
+ """
+ Make sure that the mime type for all file extensions is registered
+ """
+ for fn in filenames:
+ ext = os.path.splitext(fn)[-1]
+ if not ext:
+ continue
+ mime = mimetypes.types_map[True][ext]
+ fe = FileExtension(ext[1:], mime)
+ self.Default.append(fe)
+
+
+ def _write_vba(self, workbook):
+ """
+ Add content types from cached workbook when keeping VBA
+ """
+ if workbook.vba_archive:
+ node = fromstring(workbook.vba_archive.read(ARC_CONTENT_TYPES))
+ mf = Manifest.from_tree(node)
+ filenames = self.filenames
+ for override in mf.Override:
+ if override.PartName not in (ACTIVEX, CTRL, VBA):
+ continue
+ if override.PartName not in filenames:
+ self.Override.append(override)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py
new file mode 100644
index 00000000..4318282d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2010-2024 openpyxl
+
+import posixpath
+from warnings import warn
+
+from openpyxl.descriptors import (
+ String,
+ Alias,
+ Sequence,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors.container import ElementList
+
+from openpyxl.xml.constants import REL_NS, PKG_REL_NS
+from openpyxl.xml.functions import (
+ Element,
+ fromstring,
+)
+
+
+class Relationship(Serialisable):
+ """Represents many kinds of relationships."""
+
+ tagname = "Relationship"
+
+ Type = String()
+ Target = String()
+ target = Alias("Target")
+ TargetMode = String(allow_none=True)
+ Id = String(allow_none=True)
+ id = Alias("Id")
+
+
+ def __init__(self,
+ Id=None,
+ Type=None,
+ type=None,
+ Target=None,
+ TargetMode=None
+ ):
+ """
+ `type` can be used as a shorthand with the default relationships namespace
+ otherwise the `Type` must be a fully qualified URL
+ """
+ if type is not None:
+ Type = "{0}/{1}".format(REL_NS, type)
+ self.Type = Type
+ self.Target = Target
+ self.TargetMode = TargetMode
+ self.Id = Id
+
+
+class RelationshipList(ElementList):
+
+ tagname = "Relationships"
+ expected_type = Relationship
+
+
+ def append(self, value):
+ super().append(value)
+ if not value.Id:
+ value.Id = f"rId{len(self)}"
+
+
+ def find(self, content_type):
+ """
+ Find relationships by content-type
+ NB. these content-types namespaced objects and different to the MIME-types
+ in the package manifest :-(
+ """
+ for r in self:
+ if r.Type == content_type:
+ yield r
+
+
+ def get(self, key):
+ for r in self:
+ if r.Id == key:
+ return r
+ raise KeyError("Unknown relationship: {0}".format(key))
+
+
+ def to_dict(self):
+ """Return a dictionary of relations keyed by id"""
+ return {r.id:r for r in self}
+
+
+ def to_tree(self):
+ tree = super().to_tree()
+ tree.set("xmlns", PKG_REL_NS)
+ return tree
+
+
+def get_rels_path(path):
+ """
+ Convert relative path to absolutes that can be loaded from a zip
+ archive.
+ The path to be passed in is that of containing object (workbook,
+ worksheet, etc.)
+ """
+ folder, obj = posixpath.split(path)
+ filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj))
+ return filename
+
+
+def get_dependents(archive, filename):
+ """
+ Normalise dependency file paths to absolute ones
+
+ Relative paths are relative to parent object
+ """
+ src = archive.read(filename)
+ node = fromstring(src)
+ try:
+ rels = RelationshipList.from_tree(node)
+ except TypeError:
+ msg = "{0} contains invalid dependency definitions".format(filename)
+ warn(msg)
+ rels = RelationshipList()
+ folder = posixpath.dirname(filename)
+ parent = posixpath.split(folder)[0]
+ for r in rels:
+ if r.TargetMode == "External":
+ continue
+ elif r.target.startswith("/"):
+ r.target = r.target[1:]
+ else:
+ pth = posixpath.join(parent, r.target)
+ r.target = posixpath.normpath(pth)
+ return rels
+
+
+def get_rel(archive, deps, id=None, cls=None):
+ """
+ Get related object based on id or rel_type
+ """
+ if not any([id, cls]):
+ raise ValueError("Either the id or the content type are required")
+ if id is not None:
+ rel = deps.get(id)
+ else:
+ try:
+ rel = next(deps.find(cls.rel_type))
+ except StopIteration: # no known dependency
+ return
+
+ path = rel.target
+ src = archive.read(path)
+ tree = fromstring(src)
+ obj = cls.from_tree(tree)
+
+ rels_path = get_rels_path(path)
+ try:
+ obj.deps = get_dependents(archive, rels_path)
+ except KeyError:
+ obj.deps = []
+
+ return obj
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/workbook.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/workbook.py
new file mode 100644
index 00000000..a6413cdc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/workbook.py
@@ -0,0 +1,185 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+ Alias,
+ Typed,
+ String,
+ Integer,
+ Bool,
+ NoneSet,
+)
+from openpyxl.descriptors.excel import ExtensionList, Relation
+from openpyxl.descriptors.sequence import NestedSequence
+from openpyxl.descriptors.nested import NestedString
+
+from openpyxl.xml.constants import SHEET_MAIN_NS
+
+from openpyxl.workbook.defined_name import DefinedNameList
+from openpyxl.workbook.external_reference import ExternalReference
+from openpyxl.workbook.function_group import FunctionGroupList
+from openpyxl.workbook.properties import WorkbookProperties, CalcProperties, FileVersion
+from openpyxl.workbook.protection import WorkbookProtection, FileSharing
+from openpyxl.workbook.smart_tags import SmartTagList, SmartTagProperties
+from openpyxl.workbook.views import CustomWorkbookView, BookView
+from openpyxl.workbook.web import WebPublishing, WebPublishObjectList
+
+
+class FileRecoveryProperties(Serialisable):
+
+ tagname = "fileRecoveryPr"
+
+ autoRecover = Bool(allow_none=True)
+ crashSave = Bool(allow_none=True)
+ dataExtractLoad = Bool(allow_none=True)
+ repairLoad = Bool(allow_none=True)
+
+ def __init__(self,
+ autoRecover=None,
+ crashSave=None,
+ dataExtractLoad=None,
+ repairLoad=None,
+ ):
+ self.autoRecover = autoRecover
+ self.crashSave = crashSave
+ self.dataExtractLoad = dataExtractLoad
+ self.repairLoad = repairLoad
+
+
+class ChildSheet(Serialisable):
+ """
+ Represents a reference to a worksheet or chartsheet in workbook.xml
+
+ It contains the title, order and state but only an indirect reference to
+ the objects themselves.
+ """
+
+ tagname = "sheet"
+
+ name = String()
+ sheetId = Integer()
+ state = NoneSet(values=(['visible', 'hidden', 'veryHidden']))
+ id = Relation()
+
+ def __init__(self,
+ name=None,
+ sheetId=None,
+ state="visible",
+ id=None,
+ ):
+ self.name = name
+ self.sheetId = sheetId
+ self.state = state
+ self.id = id
+
+
+class PivotCache(Serialisable):
+
+ tagname = "pivotCache"
+
+ cacheId = Integer()
+ id = Relation()
+
+ def __init__(self,
+ cacheId=None,
+ id=None
+ ):
+ self.cacheId = cacheId
+ self.id = id
+
+
+class WorkbookPackage(Serialisable):
+
+ """
+ Represent the workbook file in the archive
+ """
+
+ tagname = "workbook"
+
+ conformance = NoneSet(values=['strict', 'transitional'])
+ fileVersion = Typed(expected_type=FileVersion, allow_none=True)
+ fileSharing = Typed(expected_type=FileSharing, allow_none=True)
+ workbookPr = Typed(expected_type=WorkbookProperties, allow_none=True)
+ properties = Alias("workbookPr")
+ workbookProtection = Typed(expected_type=WorkbookProtection, allow_none=True)
+ bookViews = NestedSequence(expected_type=BookView)
+ sheets = NestedSequence(expected_type=ChildSheet)
+ functionGroups = Typed(expected_type=FunctionGroupList, allow_none=True)
+ externalReferences = NestedSequence(expected_type=ExternalReference)
+ definedNames = Typed(expected_type=DefinedNameList, allow_none=True)
+ calcPr = Typed(expected_type=CalcProperties, allow_none=True)
+ oleSize = NestedString(allow_none=True, attribute="ref")
+ customWorkbookViews = NestedSequence(expected_type=CustomWorkbookView)
+ pivotCaches = NestedSequence(expected_type=PivotCache, allow_none=True)
+ smartTagPr = Typed(expected_type=SmartTagProperties, allow_none=True)
+ smartTagTypes = Typed(expected_type=SmartTagList, allow_none=True)
+ webPublishing = Typed(expected_type=WebPublishing, allow_none=True)
+ fileRecoveryPr = Typed(expected_type=FileRecoveryProperties, allow_none=True)
+ webPublishObjects = Typed(expected_type=WebPublishObjectList, allow_none=True)
+ extLst = Typed(expected_type=ExtensionList, allow_none=True)
+ Ignorable = NestedString(namespace="http://schemas.openxmlformats.org/markup-compatibility/2006", allow_none=True)
+
+ __elements__ = ('fileVersion', 'fileSharing', 'workbookPr',
+ 'workbookProtection', 'bookViews', 'sheets', 'functionGroups',
+ 'externalReferences', 'definedNames', 'calcPr', 'oleSize',
+ 'customWorkbookViews', 'pivotCaches', 'smartTagPr', 'smartTagTypes',
+ 'webPublishing', 'fileRecoveryPr', 'webPublishObjects')
+
+ def __init__(self,
+ conformance=None,
+ fileVersion=None,
+ fileSharing=None,
+ workbookPr=None,
+ workbookProtection=None,
+ bookViews=(),
+ sheets=(),
+ functionGroups=None,
+ externalReferences=(),
+ definedNames=None,
+ calcPr=None,
+ oleSize=None,
+ customWorkbookViews=(),
+ pivotCaches=(),
+ smartTagPr=None,
+ smartTagTypes=None,
+ webPublishing=None,
+ fileRecoveryPr=None,
+ webPublishObjects=None,
+ extLst=None,
+ Ignorable=None,
+ ):
+ self.conformance = conformance
+ self.fileVersion = fileVersion
+ self.fileSharing = fileSharing
+ if workbookPr is None:
+ workbookPr = WorkbookProperties()
+ self.workbookPr = workbookPr
+ self.workbookProtection = workbookProtection
+ self.bookViews = bookViews
+ self.sheets = sheets
+ self.functionGroups = functionGroups
+ self.externalReferences = externalReferences
+ self.definedNames = definedNames
+ self.calcPr = calcPr
+ self.oleSize = oleSize
+ self.customWorkbookViews = customWorkbookViews
+ self.pivotCaches = pivotCaches
+ self.smartTagPr = smartTagPr
+ self.smartTagTypes = smartTagTypes
+ self.webPublishing = webPublishing
+ self.fileRecoveryPr = fileRecoveryPr
+ self.webPublishObjects = webPublishObjects
+
+
+ def to_tree(self):
+ tree = super().to_tree()
+ tree.set("xmlns", SHEET_MAIN_NS)
+ return tree
+
+
+ @property
+ def active(self):
+ for view in self.bookViews:
+ if view.activeTab is not None:
+ return view.activeTab
+ return 0