I'm working with the python-docx
library from a forked version, and I'm having an issue with editing the elements list as it is defined as a property.
# docx.document.Document
@property
def elements(self):
return self._body.elements
I tried to go with the solution mentioned here but the error AtributeError: can't set attribute
still popping out.
Next thing I tried is adding the setter
to the attribute derived from self._body
and editing the code:
# docx.blkcntnr.BlockItemContainer
@property
def elements(self):
"""
A list containing the elements in this container (paragraph and tables), in document order.
"""
return [element(item,self.part) for item in self._element.getchildren()]
I've tried to add the setter
in both levels but ended up again with the error AtributeError: can't set attribute
The setter
I wrote:
@elements.setter
def elements(self, value):
return value
The implementation I tired:
elements_list = docx__document.elements
elem_list = []
docx__document.elements = elements_list = elem_list
The main problem with that code is docx__document.elements
still contains all the elements that are supposed to have been deleted!
Editing the library was like this:
# Inside docx.document.Document
@property
def elements(self):
return self._body.elements
@elements.setter
def elements(self, value=None):
self._body.elements = value
gc.collect()
return value
The other part:
# Inside docx.blkcntnr.BlockItemContainer
@property
def elements(self):
"""
A list containing the elements in this container (paragraph and tables), in document order.
"""
return [element(item,self.part) for item in self._element.getchildren()]
@elements.setter
def elements(self, value):
"""
A list containing the elements in this container (paragraph and tables), in document order.
"""
return value
If I did add a setter
for this property
:
# docx.document.Document
@property
def elements(self):
return self._body.elements
Should I add also a setter
for the property
:
# docx.blkcntnr.BlockItemContainer
@property
def elements(self):
"""
A list containing the elements in this container (paragraph and tables), in document order.
"""
return [element(item,self.part) for item in self._element.getchildren()]
Because the value of document.elemetns
is actually the value from document._body.elements
, am I right?
Any help would appreciate it!
The main "Attribute Error" issue, @Jasmijn already covered... the setter actually needs to set something.
In regards to how to provide a setter for elements
:
First we need to figure out where elements
comes from:
Document.elements
comes from [Document]._body.elements
[Document]._body.elements
comes from _Body
, which inherits BlockItemContainer.elements
BlockItemContainer.elements
builds its elements list dynamically from [BlockItemContainer]._element.getchildren()
[BlockItemContainer]._element
is equal to [Document]._element.body
[Document]._element
comes from extending ElementProxy
, and is the first argument passed to Document's constructorIn a very round-a-bout way, given element
passed to Document
, the document's elements
are derived from: element.body.getchildren()
. (A bit tricky tracking down the lookup chain, but that's just what you get when there's a lot of abstraction, or perhaps poor object oriented design)
Now to track down what exactly getchildren()
does:
element
passed to Document
is from the included oxml
packageoxml
is itself a wrapper around lxml
_Element
class is where getchildren()
is ultimately defined (etree.pyx
)getchildren()
calls _collectChildren
(apihelpers.pxi
), which gives you an idea of how the internal element structure is setupGiven that the root implementation is Cython is going to complicate things, but I see that the _Element
class implements some additional methods which you could make use of, in particular: clear()
and extend()
.
So a possible implementation (which I've tested and appears to work):
# inside docx.document.Document
@elements.setter
def elements(self, lst):
cython_el = self._element.body
cython_el.clear()
cython_el.extend(lst)
I'll disagree with @Jasmijn here and say you don't need to provide a setter for BlockItemContainer
as well, since that's a private class.
You could also expose other _Element
methods directly on the Document
object if desired, like clear()
.