codehaus


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

lxml - minor problem appending new element


Frank Millman wrote:

> Hi all
> 
> I usually send lxml queries to the lxml mailing list, but it appears to
> be not working, so I thought I would try here.
> 
> This is a minor issue, and I have found an ugly workaround, but I
> thought I would mention it.

Like this?

children = list(xml)
for y in children:
    print(etree.tostring(y))
    if y.get('z') == 'c':
        child = etree.Element('y', attrib={'z': 'd'})
        xml.append(child)
        children.append(child)

It doesn't look /that/ ugly to me.
 
> In Python I can iterate through a list, and on a certain condition
> append a new item to the list, which is then included in the iteration.

Personally I follow the rule "never mutate a list you are iterating over", 
even for appends, where the likelihood of problems is small:

items = ["a"]
for item in items:
   if item == "a": items.append("a")
 
>  >>> x = ['a', 'b', 'c']
>  >>> for y in x:
> ...   print(y)
> ...   if y == 'b':
> ...     x.append('d')
> ...
> a
> b
> c
> d
>  >>> x
> ['a', 'b', 'c', 'd']
>  >>>
> 
> The same thing works in lxml -
> 
>  >>> lmx = '<x><y z="a"/><y z="b"/><y z="c"/></x>'
>  >>> xml = etree.fromstring(lmx)
>  >>> for y in xml:
> ...   print(etree.tostring(y))
> ...   if y.get('z') == 'b':
> ...     xml.append(etree.Element('y', attrib={'z': 'd'}))
> ...
> b'<y z="a"/>'
> b'<y z="b"/>'
> b'<y z="c"/>'
> b'<y z="d"/>'
>  >>> etree.tostring(xml)
> b'<x><y z="a"/><y z="b"/><y z="c"/><y z="d"/></x>'
> 
> However, if it happens that the condition is met on the last item in the
> list, Python still works, but lxml does not include the appended item in
> the iteration. In the following, the only change is checking for 'c'
> instead of 'b'.
> 
>  >>> x = ['a', 'b', 'c']
>  >>> for y in x:
> ...   print(y)
> ...   if y == 'c':
> ...     x.append('d')
> ...
> a
> b
> c
> d
>  >>> x
> ['a', 'b', 'c', 'd']
>  >>>
> 
>  >>> lmx = '<x><y z="a"/><y z="b"/><y z="c"/></x>'
>  >>> xml = etree.fromstring(lmx)
>  >>> for y in xml:
> ...   print(etree.tostring(y))
> ...   if y.get('z') == 'c':
> ...     xml.append(etree.Element('y', attrib={'z': 'd'}))
> ...
> b'<y z="a"/>'
> b'<y z="b"/>'
> b'<y z="c"/>'
>  >>> etree.tostring(xml)
> b'<x><y z="a"/><y z="b"/><y z="c"/><y z="d"/></x>'
> 
> As you can see, the last element is correctly appended, but is not
> included in the iteration.
> 
> Is there any chance that this can be looked at, or is it just the way it
> works?

File a bug report and see if the author is willing to emulate the list 
behaviour.
 
> BTW, I see that ElementTree in the standard library does not have this
> problem.

Maybe uses a list under the hood.