[Lxml-checkins] r47893 - in lxml/trunk: . src/lxml src/lxml/tests
scoder at codespeak.net
scoder at codespeak.net
Thu Oct 25 10:08:00 CEST 2007
Author: scoder
Date: Thu Oct 25 10:08:00 2007
New Revision: 47893
Modified:
lxml/trunk/CHANGES.txt
lxml/trunk/src/lxml/apihelpers.pxi
lxml/trunk/src/lxml/lxml.etree.pyx
lxml/trunk/src/lxml/python.pxd
lxml/trunk/src/lxml/tests/test_elementtree.py
lxml/trunk/src/lxml/tests/test_etree.py
Log:
replaced __getslice__() etc. by __*item__() equivalents and a custom slicing implementation
Modified: lxml/trunk/CHANGES.txt
==============================================================================
--- lxml/trunk/CHANGES.txt (original)
+++ lxml/trunk/CHANGES.txt Thu Oct 25 10:08:00 2007
@@ -8,6 +8,8 @@
Features added
--------------
+* Extended slicing of Elements as in ``element[1:-1:2]``
+
* Resolvers can now provide a ``base_url`` keyword argument when
resolving a document as string data.
Modified: lxml/trunk/src/lxml/apihelpers.pxi
==============================================================================
--- lxml/trunk/src/lxml/apihelpers.pxi (original)
+++ lxml/trunk/src/lxml/apihelpers.pxi Thu Oct 25 10:08:00 2007
@@ -473,6 +473,60 @@
c_node = c_node.next
return count
+cdef int _findChildSlice(
+ python.slice sliceobject, xmlNode* c_parent,
+ xmlNode** c_start_node, Py_ssize_t* c_step, Py_ssize_t* c_length) except -1:
+ """Resolve a children slice.
+
+ Returns the start node, step size and the slice length in the
+ pointer arguments.
+ """
+ cdef Py_ssize_t start, stop, childcount
+ childcount = _countElements(c_parent.children)
+ if childcount == 0:
+ c_start_node[0] = NULL
+ c_length[0] = 0
+ if sliceobject.step is None:
+ c_step[0] = 1
+ else:
+ python._PyEval_SliceIndex(sliceobject.step, c_step)
+ return 0
+ python.PySlice_GetIndicesEx(
+ sliceobject, childcount, &start, &stop, c_step, c_length)
+ if start > childcount / 2:
+ c_start_node[0] = _findChildBackwards(c_parent, childcount - start - 1)
+ else:
+ c_start_node[0] = _findChild(c_parent, start)
+ return 0
+
+cdef bint _isFullSlice(python.slice sliceobject):
+ """Conservative guess if this slice is a full slice as in ``s[:]``.
+ """
+ cdef Py_ssize_t step
+ if sliceobject is None:
+ return 0
+ if sliceobject.start is None and \
+ sliceobject.stop is None:
+ if sliceobject.step is None:
+ return 1
+ python._PyEval_SliceIndex(sliceobject.step, &step)
+ if step == 1:
+ return 1
+ return 0
+ return 0
+
+cdef _collectChildren(_Element element):
+ cdef xmlNode* c_node
+ result = []
+ c_node = element._c_node.children
+ if c_node is not NULL:
+ if not _isElement(c_node):
+ c_node = _nextElement(c_node)
+ while c_node is not NULL:
+ python.PyList_Append(result, _elementFactory(element._doc, c_node))
+ c_node = _nextElement(c_node)
+ return result
+
cdef xmlNode* _findChild(xmlNode* c_node, Py_ssize_t index):
if index < 0:
return _findChildBackwards(c_node, -index - 1)
@@ -530,6 +584,8 @@
cdef xmlNode* _nextElement(xmlNode* c_node):
"""Given a node, find the next sibling that is an element.
"""
+ if c_node is NULL:
+ return NULL
c_node = c_node.next
while c_node is not NULL:
if _isElement(c_node):
@@ -540,6 +596,8 @@
cdef xmlNode* _previousElement(xmlNode* c_node):
"""Given a node, find the next sibling that is an element.
"""
+ if c_node is NULL:
+ return NULL
c_node = c_node.prev
while c_node is not NULL:
if _isElement(c_node):
@@ -599,7 +657,7 @@
else:
return 0
-cdef void _removeNode(_Document doc, xmlNode* c_node):
+cdef int _removeNode(_Document doc, xmlNode* c_node) except -1:
"""Unlink and free a node and subnodes if possible. Otherwise, make sure
it's self-contained.
"""
@@ -610,6 +668,7 @@
if not attemptDeallocation(c_node):
# make namespaces absolute
moveNodeToDocument(doc, c_node)
+ return 0
cdef void _moveTail(xmlNode* c_tail, xmlNode* c_target):
cdef xmlNode* c_next
@@ -637,27 +696,169 @@
c_target = c_new_tail
c_tail = _textNodeOrSkip(c_tail.next)
-cdef xmlNode* _deleteSlice(_Document doc, xmlNode* c_node,
- Py_ssize_t start, Py_ssize_t stop):
- """Delete slice, starting with c_node, start counting at start, end at stop.
+cdef int _deleteSlice(_Document doc, xmlNode* c_node,
+ Py_ssize_t count, Py_ssize_t step) except -1:
+ """Delete slice, ``count`` items starting with ``c_node`` with a step
+ width of ``step``.
"""
cdef xmlNode* c_next
- cdef Py_ssize_t c
+ cdef Py_ssize_t c, i
+ cdef _node_to_node_function next_element
if c_node is NULL:
- return NULL
+ return 0
+ if step > 0:
+ next_element = _nextElement
+ else:
+ step = -step
+ next_element = _previousElement
# now start deleting nodes
- c = start
- while c_node is not NULL and c < stop:
- c_next = c_node.next
- if _isElement(c_node):
- while c_next is not NULL and not _isElement(c_next):
- c_next = c_next.next
- _removeNode(doc, c_node)
- c = c + 1
+ c = 0
+ c_next = c_node
+ while c_node is not NULL and c < count:
+ for i from 0 <= i < step:
+ c_next = next_element(c_next)
+ _removeNode(doc, c_node)
+ c = c + 1
c_node = c_next
- return c_node
+ return 0
+
+cdef int _replaceSlice(_Element parent, xmlNode* c_node,
+ Py_ssize_t slicelength, Py_ssize_t step,
+ bint left_to_right, elements) except -1:
+ """Replace the slice of ``count`` elements starting at ``c_node`` with
+ positive step width ``step`` by the Elements in ``elements``. The
+ direction is given by the boolean argument ``left_to_right``.
+
+ ``c_node`` may be NULL to indicate the end of the children list.
+ """
+ cdef xmlNode* c_orig_neighbour
+ cdef xmlNode* c_next
+ cdef _Element element
+ cdef Py_ssize_t seqlength, i, c
+ cdef _node_to_node_function next_element
+ assert step > 0
+ if left_to_right:
+ next_element = _nextElement
+ else:
+ next_element = _previousElement
+
+ if not python.PyList_Check(elements) and \
+ not python.PyTuple_Check(elements):
+ elements = list(elements)
+
+ if step > 1:
+ # *replacing* children stepwise with list => check size!
+ seqlength = len(elements)
+ if seqlength != slicelength:
+ raise ValueError(
+ "attempt to assign sequence of size %d "
+ "to extended slice of size %d" % (seqlength, c))
+
+ if c_node is NULL:
+ # no children yet => add all elements straight away
+ if left_to_right:
+ for element in elements:
+ assert element is not None, "Node must not be None"
+ _appendChild(parent, element)
+ else:
+ for element in elements:
+ assert element is not None, "Node must not be None"
+ _prependChild(parent, element)
+ return 0
+
+ # remove the elements first as some might be re-added
+ if left_to_right:
+ # L->R, remember left neighbour
+ c_orig_neighbour = _previousElement(c_node)
+ else:
+ # R->L, remember right neighbour
+ c_orig_neighbour = _nextElement(c_node)
+
+ c = 0
+ c_next = c_node
+ while c_node is not NULL and c < slicelength:
+ for i from 0 <= i < step:
+ c_next = next_element(c_next)
+ _removeNode(parent._doc, c_node)
+ c = c + 1
+ c_node = c_next
+
+ # make sure each element is inserted only once
+ elements = iter(elements)
+
+ # find the first node right of the new insertion point
+ if left_to_right:
+ if c_orig_neighbour is not NULL:
+ c_node = next_element(c_orig_neighbour)
+ else:
+ # before the first element
+ c_node = _findChildForwards(parent._c_node, 0)
+ elif c_orig_neighbour is NULL:
+ # at the end, but reversed stepping
+ # append one element and go to the next insertion point
+ for element in elements:
+ assert element is not None, "Node must not be None"
+ _appendChild(parent, element)
+ c_node = element._c_node
+ if slicelength > 0:
+ slicelength = slicelength - 1
+ for i from 1 <= i < step:
+ c_node = next_element(c_node)
+ break
+
+ if left_to_right:
+ # adjust step size after removing slice as we are not stepping
+ # over the newly inserted elements
+ step = step - 1
+
+ # now insert elements where we removed them
+ if c_node is not NULL:
+ for element in elements:
+ assert element is not None, "Node must not be None"
+
+ # move element and tail over
+ c_next = element._c_node.next
+ tree.xmlAddPrevSibling(c_node, element._c_node)
+ _moveTail(c_next, element._c_node)
+
+ # integrate element into new document
+ moveNodeToDocument(parent._doc, element._c_node)
+
+ # stop at the end of the slice
+ if slicelength > 0:
+ slicelength = slicelength - 1
+ for i from 0 <= i < step:
+ c_node = next_element(c_node)
+ if c_node is NULL:
+ break
+ else:
+ # everything inserted
+ return 0
+
+ # append the remaining elements at the respective end
+ if left_to_right:
+ for element in elements:
+ _appendChild(parent, element)
+ else:
+ for element in elements:
+ _prependChild(parent, element)
+
+ return 0
+
+cdef _fillUpChildrenSlice(_Element sibling, elements, bint append_right):
+ cdef _Element element
+ if append_right:
+ for element in elements:
+ assert element is not None, "Node must not be None"
+ _appendSibling(sibling, element)
+ sibling = element
+ else:
+ for element in elements:
+ assert element is not None, "Node must not be None"
+ _prependSibling(sibling, element)
+ sibling = element
-cdef void _appendChild(_Element parent, _Element child):
+cdef int _appendChild(_Element parent, _Element child) except -1:
"""Append a new child to a parent element.
"""
cdef xmlNode* c_next
@@ -665,15 +866,36 @@
c_node = child._c_node
# store possible text node
c_next = c_node.next
- tree.xmlUnlinkNode(c_node)
# move node itself
+ tree.xmlUnlinkNode(c_node)
tree.xmlAddChild(parent._c_node, c_node)
_moveTail(c_next, c_node)
# uh oh, elements may be pointing to different doc when
# parent element has moved; change them too..
moveNodeToDocument(parent._doc, c_node)
-cdef void _appendSibling(_Element element, _Element sibling):
+cdef int _prependChild(_Element parent, _Element child) except -1:
+ """Prepend a new child to a parent element.
+ """
+ cdef xmlNode* c_next
+ cdef xmlNode* c_child
+ cdef xmlNode* c_node
+ c_node = child._c_node
+ # store possible text node
+ c_next = c_node.next
+ # move node itself
+ c_child = _findChildForwards(parent._c_node, 0)
+ if c_child is NULL:
+ tree.xmlUnlinkNode(c_node)
+ tree.xmlAddChild(parent._c_node, c_node)
+ else:
+ tree.xmlAddPrevSibling(c_child, c_node)
+ _moveTail(c_next, c_node)
+ # uh oh, elements may be pointing to different doc when
+ # parent element has moved; change them too..
+ moveNodeToDocument(parent._doc, c_node)
+
+cdef int _appendSibling(_Element element, _Element sibling) except -1:
"""Append a new child to a parent element.
"""
cdef xmlNode* c_next
@@ -681,7 +903,6 @@
c_node = sibling._c_node
# store possible text node
c_next = c_node.next
- tree.xmlUnlinkNode(c_node)
# move node itself
tree.xmlAddNextSibling(element._c_node, c_node)
_moveTail(c_next, c_node)
@@ -689,7 +910,7 @@
# parent element has moved; change them too..
moveNodeToDocument(element._doc, c_node)
-cdef void _prependSibling(_Element element, _Element sibling):
+cdef int _prependSibling(_Element element, _Element sibling) except -1:
"""Append a new child to a parent element.
"""
cdef xmlNode* c_next
@@ -697,7 +918,6 @@
c_node = sibling._c_node
# store possible text node
c_next = c_node.next
- tree.xmlUnlinkNode(c_node)
# move node itself
tree.xmlAddPrevSibling(element._c_node, c_node)
_moveTail(c_next, c_node)
Modified: lxml/trunk/src/lxml/lxml.etree.pyx
==============================================================================
--- lxml/trunk/src/lxml/lxml.etree.pyx (original)
+++ lxml/trunk/src/lxml/lxml.etree.pyx Thu Oct 25 10:08:00 2007
@@ -225,6 +225,8 @@
# forward declaration of _BaseParser, see parser.pxi
cdef class _BaseParser
+ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*)
+
cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:
"""Internal base class to reference a libxml document.
@@ -495,71 +497,67 @@
# MANIPULATORS
- def __setitem__(self, Py_ssize_t index, _Element element not None):
- """Replaces the given subelement.
- """
- cdef xmlNode* c_node
- cdef xmlNode* c_next
- c_node = _findChild(self._c_node, index)
- if c_node is NULL:
- raise IndexError, index
- c_next = element._c_node.next
- _removeText(c_node.next)
- tree.xmlReplaceNode(c_node, element._c_node)
- _moveTail(c_next, element._c_node)
- moveNodeToDocument(self._doc, element._c_node)
- if not attemptDeallocation(c_node):
- moveNodeToDocument(self._doc, c_node)
-
- def __delitem__(self, Py_ssize_t index):
- """Deletes the given subelement.
- """
- cdef xmlNode* c_node
- c_node = _findChild(self._c_node, index)
- if c_node is NULL:
- raise IndexError, index
- _removeText(c_node.next)
- _removeNode(self._doc, c_node)
-
- def __delslice__(self, Py_ssize_t start, Py_ssize_t stop):
- """Deletes a number of subelements.
- """
- cdef xmlNode* c_node
- c_node = _findChild(self._c_node, start)
- _deleteSlice(self._doc, c_node, start, stop)
-
- def __setslice__(self, Py_ssize_t start, Py_ssize_t stop, value):
- """Replaces a number of subelements with elements
- from a sequence.
+ def __setitem__(self, x, value):
+ """Replaces the given subelement index or slice.
"""
cdef xmlNode* c_node
cdef xmlNode* c_next
cdef _Element element
- # first, find start of slice
- if start == python.PY_SSIZE_T_MAX:
- c_node = NULL
- else:
- c_node = _findChild(self._c_node, start)
- # now delete the slice
- if c_node is not NULL and start != stop:
- c_node = _deleteSlice(self._doc, c_node, start, stop)
- # if the insertion point is at the end, append there
- if c_node is NULL:
- for element in value:
- _appendChild(self, element)
+ cdef bint left_to_right
+ cdef Py_ssize_t slicelength, step
+ if value is None:
+ raise ValueError("cannot assign None")
+ if python.PySlice_Check(x):
+ # slice assignment
+ _findChildSlice(x, self._c_node, &c_node, &step, &slicelength)
+ if step > 0:
+ left_to_right = 1
+ else:
+ left_to_right = 0
+ step = -step
+ _replaceSlice(self, c_node, slicelength, step, left_to_right, value)
return
- # if the next element is in the list, insert before it
- for element in value:
- if element is None:
- raise TypeError, "Node must not be None."
- # store possible text tail
+ else:
+ # otherwise: normal item assignment
+ element = value
+ c_node = _findChild(self._c_node, x)
+ if c_node is NULL:
+ raise IndexError, "list index out of range"
c_next = element._c_node.next
- # now move node previous to insertion point
- tree.xmlAddPrevSibling(c_node, element._c_node)
- # and move tail just behind his node
+ _removeText(c_node.next)
+ tree.xmlReplaceNode(c_node, element._c_node)
_moveTail(c_next, element._c_node)
- # move it into a new document
moveNodeToDocument(self._doc, element._c_node)
+ if not attemptDeallocation(c_node):
+ moveNodeToDocument(self._doc, c_node)
+
+ def __delitem__(self, x):
+ """Deletes the given subelement or a slice.
+ """
+ cdef xmlNode* c_node
+ cdef xmlNode* c_next
+ cdef Py_ssize_t index, step, slicelength
+ if python.PySlice_Check(x):
+ # slice deletion
+ if _isFullSlice(<python.slice>x):
+ c_node = self._c_node.children
+ if c_node is not NULL:
+ if not _isElement(c_node):
+ c_node = _nextElement(c_node)
+ while c_node is not NULL:
+ c_next = _nextElement(c_node)
+ _removeNode(self._doc, c_node)
+ c_node = c_next
+ else:
+ _findChildSlice(x, self._c_node, &c_node, &step, &slicelength)
+ _deleteSlice(self._doc, c_node, slicelength, step)
+ else:
+ # item deletion
+ c_node = _findChild(self._c_node, x)
+ if c_node is NULL:
+ raise IndexError, index
+ _removeText(c_node.next)
+ _removeNode(self._doc, c_node)
def __deepcopy__(self, memo):
return self.__copy__()
@@ -648,14 +646,14 @@
c_attr = c_attr_next
# remove all subelements
c_node = c_node.children
- while c_node is not NULL:
- c_node_next = c_node.next
- if _isElement(c_node):
- while c_node_next is not NULL and not _isElement(c_node_next):
- c_node_next = c_node_next.next
+ if c_node is not NULL:
+ if not _isElement(c_node):
+ c_node = _nextElement(c_node)
+ while c_node is not NULL:
+ c_node_next = _nextElement(c_node)
_removeNode(self._doc, c_node)
- c_node = c_node_next
-
+ c_node = c_node_next
+
def insert(self, index, _Element element not None):
"""Inserts a subelement at the given position in this element
"""
@@ -820,49 +818,46 @@
def __repr__(self):
return "<Element %s at %x>" % (self.tag, id(self))
- def __getitem__(self, Py_ssize_t index):
- """Returns the subelement at the given position.
- """
- cdef xmlNode* c_node
- c_node = _findChild(self._c_node, index)
- if c_node is NULL:
- raise IndexError, "list index out of range"
- return _elementFactory(self._doc, c_node)
-
- def __getslice__(self, Py_ssize_t start, Py_ssize_t stop):
- """Returns a list containing subelements in the given range.
- """
- cdef xmlNode* c_node
- cdef _Document doc
- cdef Py_ssize_t c
- # this does not work for negative start, stop, however,
- # python seems to convert these to positive start, stop before
- # calling, so this all works perfectly (at the cost of a len() call)
- c_node = _findChild(self._c_node, start)
- if c_node is NULL:
- return []
- c = start
- result = []
- while c_node is not NULL and c < stop:
- if _isElement(c_node):
+ def __getitem__(self, x):
+ """Returns the subelement at the given position or the requested
+ slice.
+ """
+ cdef xmlNode* c_node
+ cdef Py_ssize_t step, slicelength
+ cdef Py_ssize_t c, i
+ cdef _node_to_node_function next_element
+ if python.PySlice_Check(x):
+ # slicing
+ if _isFullSlice(<python.slice>x):
+ return _collectChildren(self)
+ _findChildSlice(x, self._c_node, &c_node, &step, &slicelength)
+ if c_node is NULL:
+ return []
+ if step > 0:
+ next_element = _nextElement
+ else:
+ step = -step
+ next_element = _previousElement
+ result = []
+ c = 0
+ while c_node is not NULL and c < slicelength:
python.PyList_Append(
result, _elementFactory(self._doc, c_node))
c = c + 1
- c_node = c_node.next
- return result
+ for i from 0 <= i < step:
+ c_node = next_element(c_node)
+ return result
+ else:
+ # indexing
+ c_node = _findChild(self._c_node, x)
+ if c_node is NULL:
+ raise IndexError, "list index out of range"
+ return _elementFactory(self._doc, c_node)
def __len__(self):
"""Returns the number of subelements.
"""
- cdef Py_ssize_t c
- cdef xmlNode* c_node
- c = 0
- c_node = self._c_node.children
- while c_node is not NULL:
- if _isElement(c_node):
- c = c + 1
- c_node = c_node.next
- return c
+ return _countElements(self._c_node.children)
def __nonzero__(self):
import warnings
@@ -989,15 +984,7 @@
Note that this method has been deprecated as of ElementTree 1.3. New
code should use ``list(element)`` or simply iterate over elements.
"""
- cdef xmlNode* c_node
- result = []
- c_node = self._c_node.children
- while c_node is not NULL:
- if _isElement(c_node):
- python.PyList_Append(
- result, _elementFactory(self._doc, c_node))
- c_node = c_node.next
- return result
+ return _collectChildren(self)
def getparent(self):
"""Returns the parent of this element or None for the root element.
@@ -1205,7 +1192,7 @@
cdef class __ContentOnlyElement(_Element):
cdef int _raiseImmutable(self) except -1:
- raise TypeError, "this element does not have children or attributes"
+ raise TypeError("this element does not have children or attributes")
def set(self, key, value):
self._raiseImmutable()
@@ -1244,8 +1231,11 @@
tree.xmlNodeSetContent(self._c_node, c_text)
# ACCESSORS
- def __getitem__(self, n):
- raise IndexError
+ def __getitem__(self, x):
+ if python.PySlice_Check(x):
+ return []
+ else:
+ raise IndexError("list index out of range")
def __len__(self):
return 0
@@ -1769,8 +1759,6 @@
return attribs
-ctypedef xmlNode* (*_node_to_node_function)(xmlNode*)
-
cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher,
type LxmlElementTagMatcherType ]:
cdef object _pystrings
Modified: lxml/trunk/src/lxml/python.pxd
==============================================================================
--- lxml/trunk/src/lxml/python.pxd (original)
+++ lxml/trunk/src/lxml/python.pxd Thu Oct 25 10:08:00 2007
@@ -4,7 +4,6 @@
ctypedef struct PyObject
ctypedef struct PyThreadState
ctypedef int size_t
- ctypedef int Py_ssize_t
cdef int INT_MAX
cdef int PY_SSIZE_T_MAX
@@ -12,6 +11,11 @@
cdef void Py_DECREF(object o)
cdef void Py_XDECREF(PyObject* o)
+ ctypedef class __builtin__.slice [object PySliceObject]:
+ cdef object start
+ cdef object stop
+ cdef object step
+
cdef FILE* PyFile_AsFile(object p)
cdef int PyFile_Check(object p)
cdef object PyFile_Name(object p)
@@ -73,6 +77,11 @@
cdef bint PyTuple_CheckExact(object instance)
cdef bint PySlice_Check(object instance)
+ cdef int _PyEval_SliceIndex(object value, Py_ssize_t* index) except 0
+ cdef int PySlice_GetIndicesEx(object slice, Py_ssize_t length,
+ Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step,
+ Py_ssize_t *slicelength) except -1
+
cdef int PyObject_SetAttr(object o, object name, object value)
cdef object PyObject_RichCompare(object o1, object o2, int op)
cdef int PyObject_RichCompareBool(object o1, object o2, int op)
Modified: lxml/trunk/src/lxml/tests/test_elementtree.py
==============================================================================
--- lxml/trunk/src/lxml/tests/test_elementtree.py (original)
+++ lxml/trunk/src/lxml/tests/test_elementtree.py Thu Oct 25 10:08:00 2007
@@ -1244,16 +1244,6 @@
self.assertXML('<b><bs></bs></b>', b)
self.assertXML('<c><cs></cs></c>', c)
- def test_delslice_tail(self):
- XML = self.etree.XML
- a = XML('<a><b></b>B2<c></c>C2</a>')
- b, c = a
-
- del a[:]
-
- self.assertEquals("B2", b.tail)
- self.assertEquals("C2", c.tail)
-
def test_replace_slice_tail(self):
XML = self.etree.XML
a = XML('<a><b></b>B2<c></c>C2</a>')
@@ -1718,6 +1708,32 @@
[b, c],
a[-3:2])
+ def test_getslice_step(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ self.assertEquals(
+ [e,d,c,b],
+ a[::-1])
+ self.assertEquals(
+ [b,d],
+ a[::2])
+ self.assertEquals(
+ [e,c],
+ a[::-2])
+ self.assertEquals(
+ [d,c],
+ a[-2:0:-1])
+ self.assertEquals(
+ [e],
+ a[:1:-2])
+
def test_getslice_text(self):
ElementTree = self.etree.ElementTree
@@ -1805,7 +1821,52 @@
[b, e],
list(a))
- def test_delslice_tail(self):
+ def test_delslice_step(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ del a[1::2]
+ self.assertEquals(
+ [b, d],
+ list(a))
+
+ def test_delslice_step_negative(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ del a[::-1]
+ self.assertEquals(
+ [],
+ list(a))
+
+ def test_delslice_step_negative2(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ del a[::-2]
+ self.assertEquals(
+ [b, d],
+ list(a))
+
+ def test_delslice_child_tail(self):
ElementTree = self.etree.ElementTree
f = StringIO('<a><b></b>B2<c></c>C2<d></d>D2<e></e>E2</a>')
doc = ElementTree(file=f)
@@ -1815,6 +1876,16 @@
'<a><b></b>B2<e></e>E2</a>',
a)
+ def test_delslice_tail(self):
+ XML = self.etree.XML
+ a = XML('<a><b></b>B2<c></c>C2</a>')
+ b, c = a
+
+ del a[:]
+
+ self.assertEquals("B2", b.tail)
+ self.assertEquals("C2", c.tail)
+
def test_delslice_memory(self):
# this could trigger a crash
Element = self.etree.Element
@@ -1845,6 +1916,118 @@
[b, e, f, g, d],
list(a))
+ def test_setslice_all(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+
+ e = Element('e')
+ f = Element('f')
+ g = Element('g')
+
+ s = [e, f, g]
+ a[:] = s
+ self.assertEquals(
+ [e, f, g],
+ list(a))
+
+ def test_setslice_all_empty(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+
+ e = Element('e')
+ f = Element('f')
+ g = Element('g')
+
+ s = [e, f, g]
+ a[:] = s
+ self.assertEquals(
+ [e, f, g],
+ list(a))
+
+ def test_setslice_all_replace(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+
+ s = [b, c, d]
+ a[:] = s
+ self.assertEquals(
+ [b, c, d],
+ list(a))
+
+ def test_setslice_all_replace_reversed(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+
+ s = [d, c, b]
+ a[:] = s
+ self.assertEquals(
+ [d, c, b],
+ list(a))
+
+ def test_setslice_end(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+
+ e = Element('e')
+ f = Element('f')
+ g = Element('g')
+ h = Element('h')
+
+ s = [e, f]
+ a[99:] = s
+ self.assertEquals(
+ [a, b, e, f],
+ list(a))
+
+ s = [g, h]
+ a[:0] = s
+ self.assertEquals(
+ [g, h, a, b, e, f],
+ list(a))
+
+ def test_setslice_single(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+
+ e = Element('e')
+ f = Element('f')
+
+ s = [e]
+ a[0:1] = s
+ self.assertEquals(
+ [e, c],
+ list(a))
+
+ s = [f]
+ a[1:2] = s
+ self.assertEquals(
+ [e, f],
+ list(a))
+
def test_setslice_tail(self):
ElementTree = self.etree.ElementTree
Element = self.etree.Element
@@ -1861,7 +2044,7 @@
self.assertXML(
'<a><b></b>B2<x></x>X2<y></y>Y2<z></z>Z2<e></e>E2</a>',
a)
-
+
def test_setslice_negative(self):
Element = self.etree.Element
SubElement = self.etree.SubElement
@@ -1879,6 +2062,23 @@
[b, x, y, d],
list(a))
+ def test_setslice_negative2(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+
+ x = Element('x')
+ y = Element('y')
+
+ a[1:-2] = [x, y]
+ self.assertEquals(
+ [b, x, y, c, d],
+ list(a))
+
def test_setslice_end(self):
Element = self.etree.Element
SubElement = self.etree.SubElement
Modified: lxml/trunk/src/lxml/tests/test_etree.py
==============================================================================
--- lxml/trunk/src/lxml/tests/test_etree.py (original)
+++ lxml/trunk/src/lxml/tests/test_etree.py Thu Oct 25 10:08:00 2007
@@ -8,7 +8,7 @@
"""
-import unittest, copy, sys
+import unittest, copy, sys, operator
from common_imports import etree, StringIO, HelperTestCase, fileInTestDir
from common_imports import SillyFileLike, canonicalize, doctest
@@ -1572,6 +1572,103 @@
self.assertEquals(
child1, e[1])
+ def test_setslice_all_empty_reversed(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+
+ e = Element('e')
+ f = Element('f')
+ g = Element('g')
+
+ s = [e, f, g]
+ a[::-1] = s
+ self.assertEquals(
+ [g, f, e],
+ list(a))
+
+ def test_setslice_step(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ x = Element('x')
+ y = Element('y')
+
+ a[1::2] = [x, y]
+ self.assertEquals(
+ [b, x, d, y],
+ list(a))
+
+ def test_setslice_step_negative(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ x = Element('x')
+ y = Element('y')
+
+ a[1::-1] = [x, y]
+ self.assertEquals(
+ [y, x, d, e],
+ list(a))
+
+ def test_setslice_step_negative2(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ x = Element('x')
+ y = Element('y')
+
+ a[::-2] = [x, y]
+ self.assertEquals(
+ [b, y, d, x],
+ list(a))
+
+ def test_setslice_step_overrun(self):
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+ try:
+ slice
+ except NameError:
+ print "slice() not found"
+ return
+
+ a = Element('a')
+ b = SubElement(a, 'b')
+ c = SubElement(a, 'c')
+ d = SubElement(a, 'd')
+ e = SubElement(a, 'e')
+
+ x = Element('x')
+ y = Element('y')
+ z = Element('z')
+
+ self.assertRaises(
+ ValueError,
+ operator.setitem, a, slice(1,None,2), [x, y, z])
+
+ self.assertEquals(
+ [b, c, d, e],
+ list(a))
+
def test_extend(self):
etree = self.etree
root = etree.Element('foo')
More information about the lxml-checkins
mailing list