[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