|
7 | 7 |
|
8 | 8 | from __future__ import absolute_import, print_function, unicode_literals |
9 | 9 |
|
10 | | -from .compat import cls_method_fn |
11 | 10 | from .constants import RELATIONSHIP_TYPE as RT |
12 | | -from .oxml import serialize_part_xml |
13 | | -from ..oxml import parse_xml |
14 | | -from .packuri import PACKAGE_URI, PackURI |
| 11 | +from .packuri import PACKAGE_URI |
| 12 | +from .part import PartFactory |
15 | 13 | from .pkgreader import PackageReader |
16 | 14 | from .pkgwriter import PackageWriter |
17 | 15 | from .rel import Relationships |
@@ -169,224 +167,6 @@ def _core_properties_part(self): |
169 | 167 | raise NotImplementedError |
170 | 168 |
|
171 | 169 |
|
172 | | -class Part(object): |
173 | | - """ |
174 | | - Base class for package parts. Provides common properties and methods, but |
175 | | - intended to be subclassed in client code to implement specific part |
176 | | - behaviors. |
177 | | - """ |
178 | | - def __init__(self, partname, content_type, blob=None, package=None): |
179 | | - super(Part, self).__init__() |
180 | | - self._partname = partname |
181 | | - self._content_type = content_type |
182 | | - self._blob = blob |
183 | | - self._package = package |
184 | | - |
185 | | - def after_unmarshal(self): |
186 | | - """ |
187 | | - Entry point for post-unmarshaling processing, for example to parse |
188 | | - the part XML. May be overridden by subclasses without forwarding call |
189 | | - to super. |
190 | | - """ |
191 | | - # don't place any code here, just catch call if not overridden by |
192 | | - # subclass |
193 | | - pass |
194 | | - |
195 | | - def before_marshal(self): |
196 | | - """ |
197 | | - Entry point for pre-serialization processing, for example to finalize |
198 | | - part naming if necessary. May be overridden by subclasses without |
199 | | - forwarding call to super. |
200 | | - """ |
201 | | - # don't place any code here, just catch call if not overridden by |
202 | | - # subclass |
203 | | - pass |
204 | | - |
205 | | - @property |
206 | | - def blob(self): |
207 | | - """ |
208 | | - Contents of this package part as a sequence of bytes. May be text or |
209 | | - binary. Intended to be overridden by subclasses. Default behavior is |
210 | | - to return load blob. |
211 | | - """ |
212 | | - return self._blob |
213 | | - |
214 | | - @property |
215 | | - def content_type(self): |
216 | | - """ |
217 | | - Content type of this part. |
218 | | - """ |
219 | | - return self._content_type |
220 | | - |
221 | | - def drop_rel(self, rId): |
222 | | - """ |
223 | | - Remove the relationship identified by *rId* if its reference count |
224 | | - is less than 2. Relationships with a reference count of 0 are |
225 | | - implicit relationships. |
226 | | - """ |
227 | | - if self._rel_ref_count(rId) < 2: |
228 | | - del self.rels[rId] |
229 | | - |
230 | | - @classmethod |
231 | | - def load(cls, partname, content_type, blob, package): |
232 | | - return cls(partname, content_type, blob, package) |
233 | | - |
234 | | - def load_rel(self, reltype, target, rId, is_external=False): |
235 | | - """ |
236 | | - Return newly added |_Relationship| instance of *reltype* between this |
237 | | - part and *target* with key *rId*. Target mode is set to |
238 | | - ``RTM.EXTERNAL`` if *is_external* is |True|. Intended for use during |
239 | | - load from a serialized package, where the rId is well-known. Other |
240 | | - methods exist for adding a new relationship to a part when |
241 | | - manipulating a part. |
242 | | - """ |
243 | | - return self.rels.add_relationship(reltype, target, rId, is_external) |
244 | | - |
245 | | - @property |
246 | | - def partname(self): |
247 | | - """ |
248 | | - |PackURI| instance holding partname of this part, e.g. |
249 | | - '/ppt/slides/slide1.xml' |
250 | | - """ |
251 | | - return self._partname |
252 | | - |
253 | | - @partname.setter |
254 | | - def partname(self, partname): |
255 | | - if not isinstance(partname, PackURI): |
256 | | - tmpl = "partname must be instance of PackURI, got '%s'" |
257 | | - raise TypeError(tmpl % type(partname).__name__) |
258 | | - self._partname = partname |
259 | | - |
260 | | - @property |
261 | | - def package(self): |
262 | | - """ |
263 | | - |OpcPackage| instance this part belongs to. |
264 | | - """ |
265 | | - return self._package |
266 | | - |
267 | | - def part_related_by(self, reltype): |
268 | | - """ |
269 | | - Return part to which this part has a relationship of *reltype*. |
270 | | - Raises |KeyError| if no such relationship is found and |ValueError| |
271 | | - if more than one such relationship is found. Provides ability to |
272 | | - resolve implicitly related part, such as Slide -> SlideLayout. |
273 | | - """ |
274 | | - return self.rels.part_with_reltype(reltype) |
275 | | - |
276 | | - def relate_to(self, target, reltype, is_external=False): |
277 | | - """ |
278 | | - Return rId key of relationship of *reltype* to *target*, from an |
279 | | - existing relationship if there is one, otherwise a newly created one. |
280 | | - """ |
281 | | - if is_external: |
282 | | - return self.rels.get_or_add_ext_rel(reltype, target) |
283 | | - else: |
284 | | - rel = self.rels.get_or_add(reltype, target) |
285 | | - return rel.rId |
286 | | - |
287 | | - @property |
288 | | - def related_parts(self): |
289 | | - """ |
290 | | - Dictionary mapping related parts by rId, so child objects can resolve |
291 | | - explicit relationships present in the part XML, e.g. sldIdLst to a |
292 | | - specific |Slide| instance. |
293 | | - """ |
294 | | - return self.rels.related_parts |
295 | | - |
296 | | - @lazyproperty |
297 | | - def rels(self): |
298 | | - """ |
299 | | - |Relationships| instance holding the relationships for this part. |
300 | | - """ |
301 | | - return Relationships(self._partname.baseURI) |
302 | | - |
303 | | - def target_ref(self, rId): |
304 | | - """ |
305 | | - Return URL contained in target ref of relationship identified by |
306 | | - *rId*. |
307 | | - """ |
308 | | - rel = self.rels[rId] |
309 | | - return rel.target_ref |
310 | | - |
311 | | - def _rel_ref_count(self, rId): |
312 | | - """ |
313 | | - Return the count of references in this part's XML to the relationship |
314 | | - identified by *rId*. |
315 | | - """ |
316 | | - rIds = self._element.xpath('//@r:id') |
317 | | - return len([_rId for _rId in rIds if _rId == rId]) |
318 | | - |
319 | | - |
320 | | -class XmlPart(Part): |
321 | | - """ |
322 | | - Base class for package parts containing an XML payload, which is most of |
323 | | - them. Provides additional methods to the |Part| base class that take care |
324 | | - of parsing and reserializing the XML payload and managing relationships |
325 | | - to other parts. |
326 | | - """ |
327 | | - def __init__(self, partname, content_type, element, package): |
328 | | - super(XmlPart, self).__init__( |
329 | | - partname, content_type, package=package |
330 | | - ) |
331 | | - self._element = element |
332 | | - |
333 | | - @property |
334 | | - def blob(self): |
335 | | - return serialize_part_xml(self._element) |
336 | | - |
337 | | - @classmethod |
338 | | - def load(cls, partname, content_type, blob, package): |
339 | | - element = parse_xml(blob) |
340 | | - return cls(partname, content_type, element, package) |
341 | | - |
342 | | - @property |
343 | | - def part(self): |
344 | | - """ |
345 | | - Part of the parent protocol, "children" of the document will not know |
346 | | - the part that contains them so must ask their parent object. That |
347 | | - chain of delegation ends here for child objects. |
348 | | - """ |
349 | | - return self |
350 | | - |
351 | | - |
352 | | -class PartFactory(object): |
353 | | - """ |
354 | | - Provides a way for client code to specify a subclass of |Part| to be |
355 | | - constructed by |Unmarshaller| based on its content type and/or a custom |
356 | | - callable. Setting ``PartFactory.part_class_selector`` to a callable |
357 | | - object will cause that object to be called with the parameters |
358 | | - ``content_type, reltype``, once for each part in the package. If the |
359 | | - callable returns an object, it is used as the class for that part. If it |
360 | | - returns |None|, part class selection falls back to the content type map |
361 | | - defined in ``PartFactory.part_type_for``. If no class is returned from |
362 | | - either of these, the class contained in ``PartFactory.default_part_type`` |
363 | | - is used to construct the part, which is by default ``opc.package.Part``. |
364 | | - """ |
365 | | - part_class_selector = None |
366 | | - part_type_for = {} |
367 | | - default_part_type = Part |
368 | | - |
369 | | - def __new__(cls, partname, content_type, reltype, blob, package): |
370 | | - PartClass = None |
371 | | - if cls.part_class_selector is not None: |
372 | | - part_class_selector = cls_method_fn(cls, 'part_class_selector') |
373 | | - PartClass = part_class_selector(content_type, reltype) |
374 | | - if PartClass is None: |
375 | | - PartClass = cls._part_cls_for(content_type) |
376 | | - return PartClass.load(partname, content_type, blob, package) |
377 | | - |
378 | | - @classmethod |
379 | | - def _part_cls_for(cls, content_type): |
380 | | - """ |
381 | | - Return the custom part class registered for *content_type*, or the |
382 | | - default part class if no custom class is registered for |
383 | | - *content_type*. |
384 | | - """ |
385 | | - if content_type in cls.part_type_for: |
386 | | - return cls.part_type_for[content_type] |
387 | | - return cls.default_part_type |
388 | | - |
389 | | - |
390 | 170 | class Unmarshaller(object): |
391 | 171 | """ |
392 | 172 | Hosts static methods for unmarshalling a package from a |PackageReader| |
|
0 commit comments