python-docx-oss 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. docx/__init__.py +71 -0
  2. docx/api.py +37 -0
  3. docx/blkcntnr.py +101 -0
  4. docx/comments.py +163 -0
  5. docx/dml/__init__.py +0 -0
  6. docx/dml/color.py +112 -0
  7. docx/document.py +285 -0
  8. docx/drawing/__init__.py +59 -0
  9. docx/enum/__init__.py +0 -0
  10. docx/enum/base.py +150 -0
  11. docx/enum/dml.py +103 -0
  12. docx/enum/section.py +86 -0
  13. docx/enum/shape.py +19 -0
  14. docx/enum/style.py +452 -0
  15. docx/enum/table.py +136 -0
  16. docx/enum/text.py +367 -0
  17. docx/exceptions.py +18 -0
  18. docx/image/__init__.py +32 -0
  19. docx/image/bmp.py +43 -0
  20. docx/image/constants.py +172 -0
  21. docx/image/emf.py +46 -0
  22. docx/image/exceptions.py +13 -0
  23. docx/image/gif.py +38 -0
  24. docx/image/helpers.py +86 -0
  25. docx/image/image.py +237 -0
  26. docx/image/jpeg.py +469 -0
  27. docx/image/png.py +253 -0
  28. docx/image/svg.py +118 -0
  29. docx/image/tiff.py +289 -0
  30. docx/opc/__init__.py +0 -0
  31. docx/opc/constants.py +307 -0
  32. docx/opc/coreprops.py +142 -0
  33. docx/opc/customprops.py +59 -0
  34. docx/opc/exceptions.py +12 -0
  35. docx/opc/oxml.py +247 -0
  36. docx/opc/package.py +254 -0
  37. docx/opc/packuri.py +109 -0
  38. docx/opc/part.py +267 -0
  39. docx/opc/parts/__init__.py +0 -0
  40. docx/opc/parts/coreprops.py +48 -0
  41. docx/opc/parts/customprops.py +61 -0
  42. docx/opc/phys_pkg.py +119 -0
  43. docx/opc/pkgreader.py +254 -0
  44. docx/opc/pkgwriter.py +115 -0
  45. docx/opc/rel.py +169 -0
  46. docx/opc/shared.py +31 -0
  47. docx/opc/spec.py +24 -0
  48. docx/oxml/__init__.py +252 -0
  49. docx/oxml/comments.py +124 -0
  50. docx/oxml/coreprops.py +298 -0
  51. docx/oxml/customprops.py +141 -0
  52. docx/oxml/dml/CT_ExtensionList.py +48 -0
  53. docx/oxml/document.py +88 -0
  54. docx/oxml/drawing.py +11 -0
  55. docx/oxml/exceptions.py +10 -0
  56. docx/oxml/ns.py +111 -0
  57. docx/oxml/numbering.py +109 -0
  58. docx/oxml/parser.py +62 -0
  59. docx/oxml/section.py +537 -0
  60. docx/oxml/settings.py +138 -0
  61. docx/oxml/shape.py +426 -0
  62. docx/oxml/shared.py +52 -0
  63. docx/oxml/simpletypes.py +434 -0
  64. docx/oxml/styles.py +320 -0
  65. docx/oxml/table.py +977 -0
  66. docx/oxml/text/__init__.py +0 -0
  67. docx/oxml/text/font.py +358 -0
  68. docx/oxml/text/hyperlink.py +45 -0
  69. docx/oxml/text/pagebreak.py +278 -0
  70. docx/oxml/text/paragraph.py +106 -0
  71. docx/oxml/text/parfmt.py +392 -0
  72. docx/oxml/text/run.py +316 -0
  73. docx/oxml/xmlchemy.py +711 -0
  74. docx/package.py +119 -0
  75. docx/parts/__init__.py +0 -0
  76. docx/parts/comments.py +51 -0
  77. docx/parts/customxml.py +67 -0
  78. docx/parts/document.py +193 -0
  79. docx/parts/hdrftr.py +53 -0
  80. docx/parts/image.py +80 -0
  81. docx/parts/numbering.py +51 -0
  82. docx/parts/settings.py +50 -0
  83. docx/parts/story.py +113 -0
  84. docx/parts/styles.py +42 -0
  85. docx/py.typed +0 -0
  86. docx/section.py +479 -0
  87. docx/settings.py +35 -0
  88. docx/shape.py +103 -0
  89. docx/shared.py +382 -0
  90. docx/styles/__init__.py +40 -0
  91. docx/styles/latent.py +198 -0
  92. docx/styles/style.py +254 -0
  93. docx/styles/styles.py +136 -0
  94. docx/table.py +537 -0
  95. docx/templates/default-comments.xml +12 -0
  96. docx/templates/default-docx-template/[Content_Types].xml +17 -0
  97. docx/templates/default-docx-template/_rels/.rels +7 -0
  98. docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +4 -0
  99. docx/templates/default-docx-template/customXml/item1.xml +2 -0
  100. docx/templates/default-docx-template/customXml/itemProps1.xml +6 -0
  101. docx/templates/default-docx-template/docProps/app.xml +36 -0
  102. docx/templates/default-docx-template/docProps/core.xml +13 -0
  103. docx/templates/default-docx-template/docProps/thumbnail.jpeg +0 -0
  104. docx/templates/default-docx-template/word/_rels/document.xml.rels +11 -0
  105. docx/templates/default-docx-template/word/document.xml +11 -0
  106. docx/templates/default-docx-template/word/fontTable.xml +61 -0
  107. docx/templates/default-docx-template/word/numbering.xml +201 -0
  108. docx/templates/default-docx-template/word/settings.xml +53 -0
  109. docx/templates/default-docx-template/word/styles.xml +11844 -0
  110. docx/templates/default-docx-template/word/stylesWithEffects.xml +11800 -0
  111. docx/templates/default-docx-template/word/theme/theme1.xml +318 -0
  112. docx/templates/default-docx-template/word/webSettings.xml +5 -0
  113. docx/templates/default-footer.xml +27 -0
  114. docx/templates/default-header.xml +27 -0
  115. docx/templates/default-numbering.xml +20 -0
  116. docx/templates/default-settings.xml +26 -0
  117. docx/templates/default-styles.xml +190 -0
  118. docx/templates/default.docx +0 -0
  119. docx/text/__init__.py +0 -0
  120. docx/text/font.py +466 -0
  121. docx/text/hyperlink.py +121 -0
  122. docx/text/pagebreak.py +104 -0
  123. docx/text/paragraph.py +173 -0
  124. docx/text/parfmt.py +286 -0
  125. docx/text/run.py +272 -0
  126. docx/text/tabstops.py +123 -0
  127. docx/types.py +34 -0
  128. python_docx_oss-0.1.0.dist-info/METADATA +57 -0
  129. python_docx_oss-0.1.0.dist-info/RECORD +132 -0
  130. python_docx_oss-0.1.0.dist-info/WHEEL +5 -0
  131. python_docx_oss-0.1.0.dist-info/licenses/LICENSE +21 -0
  132. python_docx_oss-0.1.0.dist-info/top_level.txt +1 -0
docx/opc/part.py ADDED
@@ -0,0 +1,267 @@
1
+ # pyright: reportImportCycles=false
2
+
3
+ """Open Packaging Convention (OPC) objects related to package parts."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, Callable, Type, cast
8
+
9
+ from docx.opc.oxml import serialize_part_xml
10
+ from docx.opc.packuri import PackURI
11
+ from docx.opc.rel import Relationships, _Relationship
12
+ from docx.opc.shared import cls_method_fn
13
+ from docx.oxml.parser import parse_xml
14
+ from docx.shared import lazyproperty
15
+
16
+ if TYPE_CHECKING:
17
+ from docx.oxml.xmlchemy import BaseOxmlElement
18
+ from docx.package import Package
19
+
20
+
21
+ class Part:
22
+ """Base class for package parts.
23
+
24
+ Provides common properties and methods, but intended to be subclassed in client code
25
+ to implement specific part behaviors.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ partname: PackURI,
31
+ content_type: str,
32
+ blob: bytes | None = None,
33
+ package: Package | None = None,
34
+ ):
35
+ super(Part, self).__init__()
36
+ self._partname = partname
37
+ self._content_type = content_type
38
+ self._blob = blob
39
+ self._package = package
40
+
41
+ def after_unmarshal(self):
42
+ """Entry point for post-unmarshaling processing, for example to parse the part
43
+ XML.
44
+
45
+ May be overridden by subclasses without forwarding call to super.
46
+ """
47
+ # don't place any code here, just catch call if not overridden by
48
+ # subclass
49
+ pass
50
+
51
+ def before_marshal(self):
52
+ """Entry point for pre-serialization processing, for example to finalize part
53
+ naming if necessary.
54
+
55
+ May be overridden by subclasses without forwarding call to super.
56
+ """
57
+ # don't place any code here, just catch call if not overridden by
58
+ # subclass
59
+ pass
60
+
61
+ @property
62
+ def blob(self) -> bytes:
63
+ """Contents of this package part as a sequence of bytes.
64
+
65
+ May be text or binary. Intended to be overridden by subclasses. Default behavior
66
+ is to return load blob.
67
+ """
68
+ return self._blob or b""
69
+
70
+ @property
71
+ def content_type(self):
72
+ """Content type of this part."""
73
+ return self._content_type
74
+
75
+ def drop_rel(self, rId: str):
76
+ """Remove the relationship identified by `rId` if its reference count is less
77
+ than 2.
78
+
79
+ Relationships with a reference count of 0 are implicit relationships.
80
+ """
81
+ if self._rel_ref_count(rId) < 2:
82
+ del self.rels[rId]
83
+
84
+ def drop_rels(self, cmp: Callable[[_Relationship], bool]) -> list[str]:
85
+ rids = [rid for rid, rel in self.rels.items() if cmp(rel)]
86
+ for rid in rids:
87
+ del self.rels[rid]
88
+ return rids
89
+
90
+ @classmethod
91
+ def load(cls, partname: PackURI, content_type: str, blob: bytes, package: Package):
92
+ return cls(partname, content_type, blob, package)
93
+
94
+ def load_rel(self, reltype: str, target: Part | str, rId: str, is_external: bool = False):
95
+ """Return newly added |_Relationship| instance of `reltype`.
96
+
97
+ The new relationship relates the `target` part to this part with key `rId`.
98
+
99
+ Target mode is set to ``RTM.EXTERNAL`` if `is_external` is |True|. Intended for
100
+ use during load from a serialized package, where the rId is well-known. Other
101
+ methods exist for adding a new relationship to a part when manipulating a part.
102
+ """
103
+ return self.rels.add_relationship(reltype, target, rId, is_external)
104
+
105
+ @property
106
+ def package(self):
107
+ """|OpcPackage| instance this part belongs to."""
108
+ return self._package
109
+
110
+ @property
111
+ def partname(self):
112
+ """|PackURI| instance holding partname of this part, e.g.
113
+ '/ppt/slides/slide1.xml'."""
114
+ return self._partname
115
+
116
+ @partname.setter
117
+ def partname(self, partname: str):
118
+ if not isinstance(partname, PackURI):
119
+ tmpl = "partname must be instance of PackURI, got '%s'"
120
+ raise TypeError(tmpl % type(partname).__name__)
121
+ self._partname = partname
122
+
123
+ def __eq__(self, other: object) -> bool:
124
+ return type(self) == type(other) and self.partname == other.partname
125
+
126
+ def part_related_by(self, reltype: str) -> Part:
127
+ """Return part to which this part has a relationship of `reltype`.
128
+
129
+ Raises |KeyError| if no such relationship is found and |ValueError| if more than
130
+ one such relationship is found. Provides ability to resolve implicitly related
131
+ part, such as Slide -> SlideLayout.
132
+ """
133
+ return self.rels.part_with_reltype(reltype)
134
+
135
+ def relate_to(self, target: Part | str, reltype: str, is_external: bool = False) -> str:
136
+ """Return rId key of relationship of `reltype` to `target`.
137
+
138
+ The returned `rId` is from an existing relationship if there is one, otherwise a
139
+ new relationship is created.
140
+ """
141
+ if is_external:
142
+ return self.rels.get_or_add_ext_rel(reltype, cast(str, target))
143
+ else:
144
+ rel = self.rels.get_or_add(reltype, cast(Part, target))
145
+ return rel.rId
146
+
147
+ @property
148
+ def related_parts(self):
149
+ """Dictionary mapping related parts by rId, so child objects can resolve
150
+ explicit relationships present in the part XML, e.g. sldIdLst to a specific
151
+ |Slide| instance."""
152
+ return self.rels.related_parts
153
+
154
+ @lazyproperty
155
+ def rels(self):
156
+ """|Relationships| instance holding the relationships for this part."""
157
+ # -- prevent breakage in `python-docx-template` by retaining legacy `._rels` attribute --
158
+ self._rels = Relationships(self._partname.baseURI)
159
+ return self._rels
160
+
161
+ def target_ref(self, rId: str) -> str:
162
+ """Return URL contained in target ref of relationship identified by `rId`."""
163
+ rel = self.rels[rId]
164
+ return rel.target_ref
165
+
166
+ def _rel_ref_count(self, rId: str) -> int:
167
+ """Return the count of references in this part to the relationship identified by `rId`.
168
+
169
+ Only an XML part can contain references, so this is 0 for `Part`.
170
+ """
171
+ return 0
172
+
173
+
174
+ class PartFactory:
175
+ """Provides a way for client code to specify a subclass of |Part| to be constructed
176
+ by |Unmarshaller| based on its content type and/or a custom callable.
177
+
178
+ Setting ``PartFactory.part_class_selector`` to a callable object will cause that
179
+ object to be called with the parameters ``content_type, reltype``, once for each
180
+ part in the package. If the callable returns an object, it is used as the class for
181
+ that part. If it returns |None|, part class selection falls back to the content type
182
+ map defined in ``PartFactory.part_type_for``. If no class is returned from either of
183
+ these, the class contained in ``PartFactory.default_part_type`` is used to construct
184
+ the part, which is by default ``opc.package.Part``.
185
+ """
186
+
187
+ part_class_selector: Callable[[str, str], Type[Part] | None] | None
188
+ part_type_for: dict[str, Type[Part]] = {}
189
+ default_part_type = Part
190
+
191
+ def __new__(
192
+ cls,
193
+ partname: PackURI,
194
+ content_type: str,
195
+ reltype: str,
196
+ blob: bytes,
197
+ package: Package,
198
+ ):
199
+ PartClass: Type[Part] | None = None
200
+ if cls.part_class_selector is not None:
201
+ part_class_selector = cls_method_fn(cls, "part_class_selector")
202
+ PartClass = part_class_selector(content_type, reltype)
203
+ if PartClass is None:
204
+ PartClass = cls._part_cls_for(content_type)
205
+ return PartClass.load(partname, content_type, blob, package)
206
+
207
+ @classmethod
208
+ def _part_cls_for(cls, content_type: str):
209
+ """Return the custom part class registered for `content_type`, or the default
210
+ part class if no custom class is registered for `content_type`."""
211
+ if content_type in cls.part_type_for:
212
+ return cls.part_type_for[content_type]
213
+ return cls.default_part_type
214
+
215
+
216
+ class XmlPart(Part):
217
+ """Base class for package parts containing an XML payload, which is most of them.
218
+
219
+ Provides additional methods to the |Part| base class that take care of parsing and
220
+ reserializing the XML payload and managing relationships to other parts.
221
+ """
222
+
223
+ def __init__(
224
+ self, partname: PackURI, content_type: str, element: BaseOxmlElement, package: Package
225
+ ):
226
+ super(XmlPart, self).__init__(partname, content_type, package=package)
227
+ self._element = element
228
+
229
+ @property
230
+ def blob(self):
231
+ return serialize_part_xml(self._element)
232
+
233
+ @property
234
+ def element(self):
235
+ """The root XML element of this XML part."""
236
+ return self._element
237
+
238
+ @element.setter
239
+ def element(self, e: BaseOxmlElement):
240
+ self._element = e
241
+
242
+ @classmethod
243
+ def load(cls, partname: PackURI, content_type: str, blob: bytes, package: Package):
244
+ element = parse_xml(blob)
245
+ return cls(partname, content_type, element, package)
246
+
247
+ @property
248
+ def part(self):
249
+ """Part of the parent protocol, "children" of the document will not know the
250
+ part that contains them so must ask their parent object.
251
+
252
+ That chain of delegation ends here for child objects.
253
+ """
254
+ return self
255
+
256
+ def _rel_ref_count(self, rId: str) -> int:
257
+ """Return the number of XML attributes referencing `rId` in this part."""
258
+ element = getattr(self, "_element", None)
259
+ if element is None:
260
+ return 0
261
+
262
+ count = 0
263
+ for child in element.iter():
264
+ for value in child.attrib.values():
265
+ if value == rId:
266
+ count += 1
267
+ return count
File without changes
@@ -0,0 +1,48 @@
1
+ """Core properties part, corresponds to ``/docProps/core.xml`` part in package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import datetime as dt
6
+ from typing import TYPE_CHECKING
7
+
8
+ from docx.opc.constants import CONTENT_TYPE as CT
9
+ from docx.opc.coreprops import CoreProperties
10
+ from docx.opc.packuri import PackURI
11
+ from docx.opc.part import XmlPart
12
+ from docx.oxml.coreprops import CT_CoreProperties
13
+
14
+ if TYPE_CHECKING:
15
+ from docx.opc.package import OpcPackage
16
+
17
+
18
+ class CorePropertiesPart(XmlPart):
19
+ """Corresponds to part named ``/docProps/core.xml``.
20
+
21
+ The "core" is short for "Dublin Core" and contains document metadata relatively common across
22
+ documents of all types, not just DOCX.
23
+ """
24
+
25
+ @classmethod
26
+ def default(cls, package: OpcPackage):
27
+ """Return a new |CorePropertiesPart| object initialized with default values for
28
+ its base properties."""
29
+ core_properties_part = cls._new(package)
30
+ core_properties = core_properties_part.core_properties
31
+ core_properties.title = "Word Document"
32
+ core_properties.last_modified_by = "python-docx"
33
+ core_properties.revision = 1
34
+ core_properties.modified = dt.datetime.now(dt.timezone.utc)
35
+ return core_properties_part
36
+
37
+ @property
38
+ def core_properties(self):
39
+ """A |CoreProperties| object providing read/write access to the core properties
40
+ contained in this core properties part."""
41
+ return CoreProperties(self.element)
42
+
43
+ @classmethod
44
+ def _new(cls, package: OpcPackage) -> CorePropertiesPart:
45
+ partname = PackURI("/docProps/core.xml")
46
+ content_type = CT.OPC_CORE_PROPERTIES
47
+ coreProperties = CT_CoreProperties.new()
48
+ return CorePropertiesPart(partname, content_type, coreProperties, package)
@@ -0,0 +1,61 @@
1
+ """
2
+ Custom properties part, corresponds to ``/docProps/custom.xml`` part in package.
3
+ """
4
+ from lxml import etree
5
+
6
+ from ...oxml.customprops import CT_CustomProperties
7
+ from ..constants import CONTENT_TYPE as CT
8
+ from ..customprops import CustomProperties
9
+ from ..packuri import PackURI
10
+ from ..part import XmlPart
11
+
12
+ # configure XML parser
13
+ parser_lookup = etree.ElementDefaultClassLookup(element=CT_CustomProperties)
14
+ ct_parser = etree.XMLParser(remove_blank_text=True)
15
+ ct_parser.set_element_class_lookup(parser_lookup)
16
+
17
+
18
+ def ct_parse_xml(xml):
19
+ """
20
+ Return root lxml element obtained by parsing XML character string in
21
+ *xml*, which can be either a Python 2.x string or unicode. The custom
22
+ parser is used, so custom element classes are produced for elements in
23
+ *xml* that have them.
24
+ """
25
+ return etree.fromstring(xml, ct_parser)
26
+
27
+
28
+ class CustomPropertiesPart(XmlPart):
29
+ """
30
+ Corresponds to part named ``/docProps/custom.xml``, containing the custom
31
+ document properties for this document package.
32
+ """
33
+
34
+ @classmethod
35
+ def default(cls, package):
36
+ """
37
+ Return a new |CustomPropertiesPart| object initialized with default
38
+ values for its base properties.
39
+ """
40
+ custom_properties_part = cls._new(package)
41
+ custom_properties = custom_properties_part.custom_properties
42
+ return custom_properties_part
43
+
44
+ @property
45
+ def custom_properties(self):
46
+ """
47
+ A |CustomProperties| object providing read/write access to the custom
48
+ properties contained in this custom properties part.
49
+ """
50
+ return CustomProperties(self.element)
51
+
52
+ @classmethod
53
+ def load(cls, partname, content_type, blob, package):
54
+ element = ct_parse_xml(blob)
55
+ return cls(partname, content_type, element, package)
56
+
57
+ @classmethod
58
+ def _new(cls, package):
59
+ partname = PackURI("/docProps/custom.xml")
60
+ content_type = CT.OPC_CUSTOM_PROPERTIES
61
+ return CustomPropertiesPart(partname, content_type, CT_CustomProperties.new(), package)
docx/opc/phys_pkg.py ADDED
@@ -0,0 +1,119 @@
1
+ """Provides a general interface to a `physical` OPC package, such as a zip file."""
2
+
3
+ import os
4
+ from zipfile import ZIP_DEFLATED, ZipFile, is_zipfile
5
+
6
+ from docx.opc.exceptions import PackageNotFoundError
7
+ from docx.opc.packuri import CONTENT_TYPES_URI
8
+
9
+
10
+ class PhysPkgReader:
11
+ """Factory for physical package reader objects."""
12
+
13
+ def __new__(cls, pkg_file):
14
+ # if `pkg_file` is a string, treat it as a path
15
+ if isinstance(pkg_file, str):
16
+ if os.path.isdir(pkg_file):
17
+ reader_cls = _DirPkgReader
18
+ elif is_zipfile(pkg_file):
19
+ reader_cls = _ZipPkgReader
20
+ else:
21
+ raise PackageNotFoundError("Package not found at '%s'" % pkg_file)
22
+ else: # assume it's a stream and pass it to Zip reader to sort out
23
+ reader_cls = _ZipPkgReader
24
+
25
+ return super(PhysPkgReader, cls).__new__(reader_cls)
26
+
27
+
28
+ class PhysPkgWriter:
29
+ """Factory for physical package writer objects."""
30
+
31
+ def __new__(cls, pkg_file):
32
+ return super(PhysPkgWriter, cls).__new__(_ZipPkgWriter)
33
+
34
+
35
+ class _DirPkgReader(PhysPkgReader):
36
+ """Implements |PhysPkgReader| interface for an OPC package extracted into a
37
+ directory."""
38
+
39
+ def __init__(self, path):
40
+ """`path` is the path to a directory containing an expanded package."""
41
+ super(_DirPkgReader, self).__init__()
42
+ self._path = os.path.abspath(path)
43
+
44
+ def blob_for(self, pack_uri):
45
+ """Return contents of file corresponding to `pack_uri` in package directory."""
46
+ path = os.path.join(self._path, pack_uri.membername)
47
+ with open(path, "rb") as f:
48
+ blob = f.read()
49
+ return blob
50
+
51
+ def close(self):
52
+ """Provides interface consistency with |ZipFileSystem|, but does nothing, a
53
+ directory file system doesn't need closing."""
54
+ pass
55
+
56
+ @property
57
+ def content_types_xml(self):
58
+ """Return the `[Content_Types].xml` blob from the package."""
59
+ return self.blob_for(CONTENT_TYPES_URI)
60
+
61
+ def rels_xml_for(self, source_uri):
62
+ """Return rels item XML for source with `source_uri`, or None if the item has no
63
+ rels item."""
64
+ try:
65
+ rels_xml = self.blob_for(source_uri.rels_uri)
66
+ except IOError:
67
+ rels_xml = None
68
+ return rels_xml
69
+
70
+
71
+ class _ZipPkgReader(PhysPkgReader):
72
+ """Implements |PhysPkgReader| interface for a zip file OPC package."""
73
+
74
+ def __init__(self, pkg_file):
75
+ super(_ZipPkgReader, self).__init__()
76
+ self._zipf = ZipFile(pkg_file, "r")
77
+
78
+ def blob_for(self, pack_uri):
79
+ """Return blob corresponding to `pack_uri`.
80
+
81
+ Raises |ValueError| if no matching member is present in zip archive.
82
+ """
83
+ return self._zipf.read(pack_uri.membername)
84
+
85
+ def close(self):
86
+ """Close the zip archive, releasing any resources it is using."""
87
+ self._zipf.close()
88
+
89
+ @property
90
+ def content_types_xml(self):
91
+ """Return the `[Content_Types].xml` blob from the zip package."""
92
+ return self.blob_for(CONTENT_TYPES_URI)
93
+
94
+ def rels_xml_for(self, source_uri):
95
+ """Return rels item XML for source with `source_uri` or None if no rels item is
96
+ present."""
97
+ try:
98
+ rels_xml = self.blob_for(source_uri.rels_uri)
99
+ except KeyError:
100
+ rels_xml = None
101
+ return rels_xml
102
+
103
+
104
+ class _ZipPkgWriter(PhysPkgWriter):
105
+ """Implements |PhysPkgWriter| interface for a zip file OPC package."""
106
+
107
+ def __init__(self, pkg_file):
108
+ super(_ZipPkgWriter, self).__init__()
109
+ self._zipf = ZipFile(pkg_file, "w", compression=ZIP_DEFLATED)
110
+
111
+ def close(self):
112
+ """Close the zip archive, flushing any pending physical writes and releasing any
113
+ resources it's using."""
114
+ self._zipf.close()
115
+
116
+ def write(self, pack_uri, blob):
117
+ """Write `blob` to this zip package with the membername corresponding to
118
+ `pack_uri`."""
119
+ self._zipf.writestr(pack_uri.membername, blob)