aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/docx/opc/part.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/docx/opc/part.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/docx/opc/part.py')
-rw-r--r--.venv/lib/python3.12/site-packages/docx/opc/part.py247
1 files changed, 247 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/docx/opc/part.py b/.venv/lib/python3.12/site-packages/docx/opc/part.py
new file mode 100644
index 00000000..cbb4ab55
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/docx/opc/part.py
@@ -0,0 +1,247 @@
+# pyright: reportImportCycles=false
+
+"""Open Packaging Convention (OPC) objects related to package parts."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Type, cast
+
+from docx.opc.oxml import serialize_part_xml
+from docx.opc.packuri import PackURI
+from docx.opc.rel import Relationships
+from docx.opc.shared import cls_method_fn
+from docx.oxml.parser import parse_xml
+from docx.shared import lazyproperty
+
+if TYPE_CHECKING:
+ from docx.oxml.xmlchemy import BaseOxmlElement
+ from docx.package import Package
+
+
+class Part:
+ """Base class for package parts.
+
+ Provides common properties and methods, but intended to be subclassed in client code
+ to implement specific part behaviors.
+ """
+
+ def __init__(
+ self,
+ partname: PackURI,
+ content_type: str,
+ blob: bytes | None = None,
+ package: Package | None = None,
+ ):
+ super(Part, self).__init__()
+ self._partname = partname
+ self._content_type = content_type
+ self._blob = blob
+ self._package = package
+
+ def after_unmarshal(self):
+ """Entry point for post-unmarshaling processing, for example to parse the part
+ XML.
+
+ May be overridden by subclasses without forwarding call to super.
+ """
+ # don't place any code here, just catch call if not overridden by
+ # subclass
+ pass
+
+ def before_marshal(self):
+ """Entry point for pre-serialization processing, for example to finalize part
+ naming if necessary.
+
+ May be overridden by subclasses without forwarding call to super.
+ """
+ # don't place any code here, just catch call if not overridden by
+ # subclass
+ pass
+
+ @property
+ def blob(self) -> bytes:
+ """Contents of this package part as a sequence of bytes.
+
+ May be text or binary. Intended to be overridden by subclasses. Default behavior
+ is to return load blob.
+ """
+ return self._blob or b""
+
+ @property
+ def content_type(self):
+ """Content type of this part."""
+ return self._content_type
+
+ def drop_rel(self, rId: str):
+ """Remove the relationship identified by `rId` if its reference count is less
+ than 2.
+
+ Relationships with a reference count of 0 are implicit relationships.
+ """
+ if self._rel_ref_count(rId) < 2:
+ del self.rels[rId]
+
+ @classmethod
+ def load(cls, partname: PackURI, content_type: str, blob: bytes, package: Package):
+ return cls(partname, content_type, blob, package)
+
+ def load_rel(self, reltype: str, target: Part | str, rId: str, is_external: bool = False):
+ """Return newly added |_Relationship| instance of `reltype`.
+
+ The new relationship relates the `target` part to this part with key `rId`.
+
+ Target mode is set to ``RTM.EXTERNAL`` if `is_external` is |True|. Intended for
+ use during load from a serialized package, where the rId is well-known. Other
+ methods exist for adding a new relationship to a part when manipulating a part.
+ """
+ return self.rels.add_relationship(reltype, target, rId, is_external)
+
+ @property
+ def package(self):
+ """|OpcPackage| instance this part belongs to."""
+ return self._package
+
+ @property
+ def partname(self):
+ """|PackURI| instance holding partname of this part, e.g.
+ '/ppt/slides/slide1.xml'."""
+ return self._partname
+
+ @partname.setter
+ def partname(self, partname: str):
+ if not isinstance(partname, PackURI):
+ tmpl = "partname must be instance of PackURI, got '%s'"
+ raise TypeError(tmpl % type(partname).__name__)
+ self._partname = partname
+
+ def part_related_by(self, reltype: str) -> Part:
+ """Return part to which this part has a relationship of `reltype`.
+
+ Raises |KeyError| if no such relationship is found and |ValueError| if more than
+ one such relationship is found. Provides ability to resolve implicitly related
+ part, such as Slide -> SlideLayout.
+ """
+ return self.rels.part_with_reltype(reltype)
+
+ def relate_to(self, target: Part | str, reltype: str, is_external: bool = False) -> str:
+ """Return rId key of relationship of `reltype` to `target`.
+
+ The returned `rId` is from an existing relationship if there is one, otherwise a
+ new relationship is created.
+ """
+ if is_external:
+ return self.rels.get_or_add_ext_rel(reltype, cast(str, target))
+ else:
+ rel = self.rels.get_or_add(reltype, cast(Part, target))
+ return rel.rId
+
+ @property
+ def related_parts(self):
+ """Dictionary mapping related parts by rId, so child objects can resolve
+ explicit relationships present in the part XML, e.g. sldIdLst to a specific
+ |Slide| instance."""
+ return self.rels.related_parts
+
+ @lazyproperty
+ def rels(self):
+ """|Relationships| instance holding the relationships for this part."""
+ # -- prevent breakage in `python-docx-template` by retaining legacy `._rels` attribute --
+ self._rels = Relationships(self._partname.baseURI)
+ return self._rels
+
+ def target_ref(self, rId: str) -> str:
+ """Return URL contained in target ref of relationship identified by `rId`."""
+ rel = self.rels[rId]
+ return rel.target_ref
+
+ def _rel_ref_count(self, rId: str) -> int:
+ """Return the count of references in this part to the relationship identified by `rId`.
+
+ Only an XML part can contain references, so this is 0 for `Part`.
+ """
+ return 0
+
+
+class PartFactory:
+ """Provides a way for client code to specify a subclass of |Part| to be constructed
+ by |Unmarshaller| based on its content type and/or a custom callable.
+
+ Setting ``PartFactory.part_class_selector`` to a callable object will cause that
+ object to be called with the parameters ``content_type, reltype``, once for each
+ part in the package. If the callable returns an object, it is used as the class for
+ that part. If it returns |None|, part class selection falls back to the content type
+ map defined in ``PartFactory.part_type_for``. If no class is returned from either of
+ these, the class contained in ``PartFactory.default_part_type`` is used to construct
+ the part, which is by default ``opc.package.Part``.
+ """
+
+ part_class_selector: Callable[[str, str], Type[Part] | None] | None
+ part_type_for: dict[str, Type[Part]] = {}
+ default_part_type = Part
+
+ def __new__(
+ cls,
+ partname: PackURI,
+ content_type: str,
+ reltype: str,
+ blob: bytes,
+ package: Package,
+ ):
+ PartClass: Type[Part] | None = None
+ if cls.part_class_selector is not None:
+ part_class_selector = cls_method_fn(cls, "part_class_selector")
+ PartClass = part_class_selector(content_type, reltype)
+ if PartClass is None:
+ PartClass = cls._part_cls_for(content_type)
+ return PartClass.load(partname, content_type, blob, package)
+
+ @classmethod
+ def _part_cls_for(cls, content_type: str):
+ """Return the custom part class registered for `content_type`, or the default
+ part class if no custom class is registered for `content_type`."""
+ if content_type in cls.part_type_for:
+ return cls.part_type_for[content_type]
+ return cls.default_part_type
+
+
+class XmlPart(Part):
+ """Base class for package parts containing an XML payload, which is most of them.
+
+ Provides additional methods to the |Part| base class that take care of parsing and
+ reserializing the XML payload and managing relationships to other parts.
+ """
+
+ def __init__(
+ self, partname: PackURI, content_type: str, element: BaseOxmlElement, package: Package
+ ):
+ super(XmlPart, self).__init__(partname, content_type, package=package)
+ self._element = element
+
+ @property
+ def blob(self):
+ return serialize_part_xml(self._element)
+
+ @property
+ def element(self):
+ """The root XML element of this XML part."""
+ return self._element
+
+ @classmethod
+ def load(cls, partname: PackURI, content_type: str, blob: bytes, package: Package):
+ element = parse_xml(blob)
+ return cls(partname, content_type, element, package)
+
+ @property
+ def part(self):
+ """Part of the parent protocol, "children" of the document will not know the
+ part that contains them so must ask their parent object.
+
+ That chain of delegation ends here for child objects.
+ """
+ return self
+
+ def _rel_ref_count(self, rId: str) -> int:
+ """Return the count of references in this part's XML to the relationship
+ identified by `rId`."""
+ rIds = cast("list[str]", self._element.xpath("//@r:id"))
+ return len([_rId for _rId in rIds if _rId == rId])