From 957549460bb4dc4b35b32807c9bbe41eee1d6079 Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Thu, 24 Jul 2003 12:02:57 +0000 Subject: [PATCH] Initial Commit of Indexing Suite [SVN r19296] --- .../indexing/detail/indexing_suite_detail.hpp | 362 ++++++++++++++++ .../boost/python/indexing/indexing_suite.hpp | 407 ++++++++++++++++++ .../python/indexing/vector_indexing_suite.hpp | 141 ++++++ test/Jamfile | 2 + test/vector_indexing_suite.cpp | 40 ++ test/vector_indexing_suite.py | 306 +++++++++++++ 6 files changed, 1258 insertions(+) create mode 100644 include/boost/python/indexing/detail/indexing_suite_detail.hpp create mode 100644 include/boost/python/indexing/indexing_suite.hpp create mode 100644 include/boost/python/indexing/vector_indexing_suite.hpp create mode 100644 test/vector_indexing_suite.cpp create mode 100644 test/vector_indexing_suite.py diff --git a/include/boost/python/indexing/detail/indexing_suite_detail.hpp b/include/boost/python/indexing/detail/indexing_suite_detail.hpp new file mode 100644 index 00000000..7de78f2a --- /dev/null +++ b/include/boost/python/indexing/detail/indexing_suite_detail.hpp @@ -0,0 +1,362 @@ +// (C) Copyright Joel de Guzman 2003. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. This +// software is provided "as is" without express or implied warranty, and +// with no claim as to its suitability for any purpose. + +#ifndef INDEXING_SUITE_DETAIL_JDG20036_HPP +# define INDEXING_SUITE_DETAIL_JDG20036_HPP + +# include +# include +# include +# include +# include + +namespace boost { namespace python { namespace detail { + + template + struct compare_proxy_index + { + // This functor compares a proxy and an index. + // This is used by proxy_group::first_proxy to + // get first proxy with index i. + + template + bool operator()(PyObject* prox, Index i) const + { + return extract(prox)().get_index() < i; + } + }; + + // The proxy_group class holds a vector of container element + // proxies. First, what is a container element proxy? A container + // element proxy acts like a smart pointer holding a reference to + // a container and an index (see container_element, for details). + // + // The proxies are held in a vector always sorted by its index. + // Various functions manage the addition, removal and searching + // of proxies from the vector. + // + template + class proxy_group + { + public: + + typedef typename std::vector::iterator iterator; + typedef typename Proxy::index_type index_type; + typedef typename Proxy::policies_type policies_type; + + iterator + first_proxy(index_type i) + { + // Return the first proxy with index <= i + return boost::detail::lower_bound( + proxies.begin(), proxies.end(), + i, compare_proxy_index()); + } + + void + remove(Proxy& proxy) + { + // Remove a proxy + for (iterator iter = first_proxy(proxy.get_index()); + iter != proxies.end(); ++iter) + { + if (&extract(*iter)() == &proxy) + { + proxies.erase(iter); + break; + } + } + } + + void + add(PyObject* prox) + { + // Add a proxy + proxies.insert( + first_proxy(extract(prox)().get_index()), prox); + } + + void + erase(index_type from, index_type to) + { + // Erase all proxies with indexes from..to + replace(from, to, 0); + } + + void + replace( + index_type from, + index_type to, + typename std::vector::size_type len) + { + // Erase all proxies with indexes from..to. + // Adjust the displaced indexes such that the + // final effect is that we have inserted *len* + // number of proxies in the vacated region. This + // procedure involves adjusting the indexes of + // the proxies. + + iterator left = first_proxy(from); + iterator right = proxies.end(); // we'll adjust this later + + for (iterator iter = left; iter != right; ++iter) + { + if (extract(*iter)().get_index() > to) + { + right = iter; // adjust right + break; + } + extract(*iter)().detach(); + } + + typename std::vector::size_type + offset = left-proxies.begin(); + proxies.erase(left, right); + right = proxies.begin()+offset; + + while (right != proxies.end()) + { + extract(*right)().set_index( + policies_type::adjust_index( + extract(*right)().get_index(), from, to, len)); + ++right; + } + } + + PyObject* + find(index_type i) + { + // Find the proxy with *exact* index i. + // Return 0 (null) if no proxy with the + // given index is found. + iterator iter = first_proxy(i); + if (iter != proxies.end() + && extract(*iter)().get_index() == i) + return *iter; + return 0; + } + + typename std::vector::size_type + size() const + { + // How many proxies are there so far? + return proxies.size(); + } + + private: + + std::vector proxies; + }; + + // proxy_links holds a map of Container pointers (keys) + // with proxy_group(s) (data). Various functions manage + // the addition, removal and searching of proxies from + // the map. + // + template + class proxy_links + { + public: + + typedef std::map > links_t; + typedef typename Proxy::index_type index_type; + + void + remove(Proxy& proxy) + { + // Remove a proxy. + links_t::iterator r = links.find(&proxy.get_container()); + if (r != links.end()) + { + r->second.remove(proxy); + if (r->second.size() == 0) + links.erase(r); + } + } + + void + add(PyObject* prox, Container& container) + { + // Add a proxy + links[&container].add(prox); + } + + void + erase(Container& container, index_type from, index_type to) + { + // Erase all proxies with indexes from..to + links_t::iterator r = links.find(&container); + if (r != links.end()) + { + r->second.erase(from, to); + if (r->second.size() == 0) + links.erase(r); + } + } + + void + replace( + Container& container, + index_type from, index_type to, index_type len) + { + // Erase all proxies with indexes from..to. + // Adjust the displaced indexes such that the + // final effect is that we have inserted *len* + // number of proxies in the vacated region. This + // procedure involves adjusting the indexes of + // the proxies. + + links_t::iterator r = links.find(&container); + if (r != links.end()) + { + r->second.replace(from, to, len); + if (r->second.size() == 0) + links.erase(r); + } + } + + PyObject* + find(Container& container, index_type i) + { + // Find the proxy with *exact* index i. + // Return 0 (null) if no proxy with the given + // index is found. + links_t::iterator r = links.find(&container); + if (r != links.end()) + return r->second.find(i); + return 0; + } + + private: + + links_t links; + }; + + // container_element is our container proxy class. + // This class acts like a smart pointer to a container + // element. The class holds an index and a reference to + // a container. Dereferencing the smart pointer will + // retrieve the nth (index) element from the container. + // + // A container_element can also be detached from the + // container. In such a detached state, the container_element + // holds a copy of the nth (index) element, which it + // returns when dereferenced. + // + template + class container_element + { + public: + + typedef Index index_type; + typedef typename Container::value_type element_type; + typedef typename Policies policies_type; + typedef container_element self_t; + typedef proxy_group links_type; + + container_element(object container, Index index) + : ptr() + , container(container) + , index(index) + { + } + + container_element(container_element const& ce) + : ptr(ce.ptr.get() == 0 ? 0 : new element_type(*ce.ptr.get())) + , container(ce.container) + , index(ce.index) + { + } + + ~container_element() + { + if (!is_detached()) + get_links().remove(*this); + } + + element_type& operator*() const + { + if (is_detached()) + return *ptr.get(); + return Policies::get_item(get_container(), index); + } + + element_type* get() const + { + if (is_detached()) + return ptr.get(); + return &Policies::get_item(get_container(), index); + } + + void + detach() + { + if (!is_detached()) + { + ptr.reset( + new element_type( + Policies::get_item(get_container(), index))); + container = object(); // free container. reset it to None + } + } + + bool + is_detached() const + { + return ptr.get() != 0; + } + + Container& + get_container() const + { + return extract(container)(); + } + + Index + get_index() const + { + return index; + } + + void + set_index(Index i) + { + index = i; + } + + static proxy_links& + get_links() + { + // All container_element(s) maintain links to + // its container in a global map (see proxy_links). + // This global "links" map is a singleton. + + static proxy_links links; + return links; // singleton + } + + private: + + container_element& operator=(container_element const& ce); + + scoped_ptr ptr; + object container; + Index index; + }; + + } // namespace detail + + template + inline typename Container::value_type* + get_pointer(detail::container_element const& p) + { + // Get the pointer of a container_element smart pointer + return p.get(); + } + +}} // namespace boost::python + +#endif // INDEXING_SUITE_DETAIL_JDG20036_HPP diff --git a/include/boost/python/indexing/indexing_suite.hpp b/include/boost/python/indexing/indexing_suite.hpp new file mode 100644 index 00000000..95a9e62e --- /dev/null +++ b/include/boost/python/indexing/indexing_suite.hpp @@ -0,0 +1,407 @@ +// (C) Copyright Joel de Guzman 2003. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. This +// software is provided "as is" without express or implied warranty, and +// with no claim as to its suitability for any purpose. + +#ifndef INDEXING_SUITE_JDG20036_HPP +# define INDEXING_SUITE_JDG20036_HPP + +# include +# include +# include + +namespace boost { namespace python { + + // indexing_suite class. This class is the protocol class for + // the management of C++ containers intended to be integrated + // to Python. The objective is make a C++ container look and + // feel and behave exactly as we'd expect a Python container. + // By default indexed elements are returned by proxy. This can be + // disabled by supplying *true* in the NoProxy template parameter. + // + // Derived classes provide the hooks needed by the indexing_suite + // to do its job: + // + // static element_type& + // get_item(Container& container, index_type i); + // + // static object + // get_slice(Container& container, index_type from, index_type to); + // + // static void + // set_item(Container& container, index_type i, element_type const& v); + // + // static void + // set_slice( + // Container& container, index_type from, + // index_type to, element_type const& v + // ); + // + // template + // static void + // set_slice(Container& container, index_type from, + // index_type to, Iter first, Iter last + // ); + // + // static void + // delete_item(Container& container, index_type i); + // + // static void + // delete_slice(Container& container, index_type from, index_type to); + // + // static size_t + // size(Container& container); + // + // static index_type + // convert_index(Container& container, PyObject* i); + // + // static index_type + // adjust_index(index_type current, index_type from, + // index_type to, size_type len + // ); + // + // Most of these policies are self explanatory. convert_index and + // adjust_index, however, deserves some explanation. + // + // convert_index converts an Python index into a C++ index that the + // container can handle. For instance, negative indexes in Python, by + // convention, indexes from the right (e.g. C[-1] indexes the rightmost + // element in C). convert_index should handle the necessary conversion + // for the C++ container (e.g. convert -1 to C.size()-1). convert_index + // should also be able to convert the type of the index (A dynamic Python + // type) to the actual type that the C++ container expects. + // + // When a container expands or contracts, held indexes to its elements + // must be adjusted to follow the movement of data. For instance, if + // we erase 3 elements, starting from index 0 from a 5 element vector, + // what used to be at index 4 will now be at index 1: + // + // [a][b][c][d][e] ---> [d][e] + // ^ ^ + // 4 1 + // + // adjust_index takes care of the adjustment. Given a current index, + // the function should return the adjusted index when data in the + // container at index from..to is replaced by *len* elements. + // + template < + class Container + , class DerivedPolicies + , bool NoProxy = false + , class Element = typename Container::value_type + , class Index = typename Container::size_type + > + class indexing_suite + : public def_arg< + indexing_suite< + Container + , DerivedPolicies + , NoProxy + , Element + , Index + > > + { + public: + + typedef detail::container_element + container_element_t; + + template + void visit(Class& cl) const + { + // Hook into the class_ generic visitation .def function + register_ptr_to_python(); + + cl + .def("__len__", base_size) + .def("__setitem__", &base_set_item) + .def("__delitem__", &base_delete_item) + .def("__getitem__", &base_get_item) + ; + } + + private: + + typedef boost::mpl::bool_ no_proxy; + + static object + base_get_item_( + back_reference const& container, + PyObject* i, boost::mpl::false_) + { + // Proxy + Index idx = DerivedPolicies::convert_index(container.get(), i); + + if (PyObject* shared = + container_element_t::get_links().find(container.get(), idx)) + { + return extract(shared); + } + else + { + object prox(container_element_t(container.source(), idx)); + container_element_t:: + get_links().add(prox.ptr(), container.get()); + return prox; + } + } + + static object + base_get_item_( + back_reference const& container, + PyObject* i, boost::mpl::true_) + { + // No Proxy + return object( + DerivedPolicies::get_item( + container, DerivedPolicies:: + convert_index(container.get(), i))); + } + + static object + base_get_item(back_reference container, PyObject* i) + { + if (PySlice_Check(i)) + return base_get_slice( + container.get(), reinterpret_cast(i)); + + return base_get_item_(container, i, + boost::mpl::or_< + no_proxy + , boost::mpl::or_< + boost::is_integral + , boost::is_float + , boost::is_pointer + , boost::is_member_pointer + , boost::is_enum + > + >()); + } + + static object + base_get_slice(Container& container, PySliceObject* slice) + { + Index from, to; + base_get_slice_data(container, slice, from, to); + return DerivedPolicies::get_slice(container, from, to); + } + + static + base_get_slice_data( + Container& container, PySliceObject* slice, Index& from, Index& to) + { + if (Py_None == slice->start) + from = 0; + else + from = DerivedPolicies::convert_index(container, slice->start); + + if (Py_None == slice->stop) + to = container.size(); + else + to = DerivedPolicies::convert_index(container, slice->stop); + } + + static void + base_set_item(Container& container, PyObject* i, PyObject* v) + { + if (PySlice_Check(i)) + { + base_set_slice(container, + reinterpret_cast(i), v); + } + else + { + extract elem(v); + // try if elem is an exact Element + if (elem.check()) + { + DerivedPolicies:: + set_item(container, + DerivedPolicies:: + convert_index(container, i), elem()); + } + else + { + // try to convert elem to Element + extract elem(v); + if (elem.check()) + { + DerivedPolicies:: + set_item(container, + DerivedPolicies:: + convert_index(container, i), elem()); + } + else + { + PyErr_SetString(PyExc_TypeError, "Invalid assignment"); + throw_error_already_set(); + } + } + } + } + + static void + base_replace_indexes( + Container& container, Index from, + Index to, Index n, boost::mpl::false_) + { + // Proxy + container_element_t::get_links().replace(container, from, to, n); + } + + static void + base_replace_indexes( + Container& container, Index from, + Index to, Index n, boost::mpl::true_) + { + // No Proxy + } + + static void + base_erase_indexes( + Container& container, Index from, Index to, boost::mpl::false_) + { + // Proxy + container_element_t::get_links().erase(container, from, to); + } + + static void + base_erase_indexes( + Container& container, Index from, Index to, boost::mpl::true_) + { + // No Proxy + } + + static void + base_set_slice(Container& container, PySliceObject* slice, PyObject* v) + { + Index from, to; + base_get_slice_data(container, slice, from, to); + + extract elem(v); + // try if elem is an exact Element + if (elem.check()) + { + base_replace_indexes(container, from, to, 1, no_proxy()); + DerivedPolicies::set_slice(container, from, to, elem()); + } + else + { + // try to convert elem to Element + extract elem(v); + if (elem.check()) + { + base_replace_indexes(container, from, to, 1, no_proxy()); + DerivedPolicies::set_slice(container, from, to, elem()); + } + else + { + // Otherwise, it must be a list or some container + handle<> l_(v); + object l(l_); + + std::vector temp; + for (int i = 0; i < l.attr("__len__")(); i++) + { + object elem = l[i]; + extract x(elem); + // try if elem is an exact element_type + if (x.check()) + { + temp.push_back(x()); + } + else + { + // try to convert elem to element_type + extract x(elem); + if (x.check()) + { + temp.push_back(x()); + } + else + { + PyErr_SetString(PyExc_TypeError, + "Invalid list element"); + throw_error_already_set(); + } + } + } + + base_replace_indexes(container, from, to, + temp.end()-temp.begin(), no_proxy()); + DerivedPolicies::set_slice(container, from, to, + temp.begin(), temp.end()); + } + } + } + + static void + base_delete_item(Container& container, PyObject* i) + { + if (PySlice_Check(i)) + { + base_delete_slice(container, reinterpret_cast(i)); + return; + } + + Index index = DerivedPolicies::convert_index(container, i); + base_erase_indexes(container, index, index+1, no_proxy()); + DerivedPolicies::delete_item(container, index); + } + + static void + base_delete_slice(Container& container, PySliceObject* slice) + { + Index from, to; + base_get_slice_data(container, slice, from, to); + base_erase_indexes(container, from, to, no_proxy()); + DerivedPolicies::delete_slice(container, from, to); + } + + static size_t + base_size(Container& container) + { + return DerivedPolicies::size(container); + } + + static object + get_slice(Container& container, Index from, Index to) + { + // default implementation + PyErr_SetString(PyExc_RuntimeError, "Slicing not supported"); + throw_error_already_set(); + return object(); + } + + static void + set_slice(Container& container, Index from, + Index to, Element const& v) + { + // default implementation + PyErr_SetString(PyExc_RuntimeError, "Slicing not supported"); + throw_error_already_set(); + } + + template + static void + set_slice(Container& container, Index from, + Index to, Iter first, Iter last) + { + // default implementation + PyErr_SetString(PyExc_RuntimeError, "Slicing not supported"); + throw_error_already_set(); + } + + static void + delete_slice(Container& container, Index from, Index to) + { + // default implementation + PyErr_SetString(PyExc_RuntimeError, "Slicing not supported"); + throw_error_already_set(); + } + }; + +}} // namespace boost::python + +#endif // INDEXING_SUITE_JDG20036_HPP diff --git a/include/boost/python/indexing/vector_indexing_suite.hpp b/include/boost/python/indexing/vector_indexing_suite.hpp new file mode 100644 index 00000000..fffa3da5 --- /dev/null +++ b/include/boost/python/indexing/vector_indexing_suite.hpp @@ -0,0 +1,141 @@ +// (C) Copyright Joel de Guzman 2003. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. This +// software is provided "as is" without express or implied warranty, and +// with no claim as to its suitability for any purpose. + +#ifndef VECTOR_INDEXING_SUITE_JDG20036_HPP +# define VECTOR_INDEXING_SUITE_JDG20036_HPP + +# include + +namespace boost { namespace python { + + // Forward declaration + template + class vector_indexing_suite; + + namespace detail + { + template + class final + : public vector_indexing_suite > {}; + } + + // The vector_indexing_suite class is a predefined indexing_suite derived + // class for wrapping std::vector (and std::vector like) classes. It provides + // all the policies required by the indexing_suite (see indexing_suite). + // Example usage: + // + // class X {...} + // + // ... + // + // class_ >("XVec") + // .def(vector_indexing_suite >()) + // ; + // + // By default indexed elements are returned by proxy. This can be + // disabled by supplying *true* in the NoProxy template parameter. + // + template < + class Container, + bool NoProxy = false, + class DerivedPolicies = detail::final > + class vector_indexing_suite + : public indexing_suite + { + public: + + typedef typename Container::value_type element_type; + typedef typename Container::size_type index_type; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + + static element_type& + get_item(Container& container, index_type i) + { + return container[i]; + } + + static object + get_slice(Container& container, index_type from, index_type to) + { + return object(Container(container.begin(), container.end())); + } + + static void + set_item(Container& container, index_type i, element_type const& v) + { + container[i] = v; + } + + static void + set_slice(Container& container, index_type from, + index_type to, element_type const& v) + { + container.erase(container.begin()+from, container.begin()+to); + container.insert(container.begin()+from, v); + } + + template + static void + set_slice(Container& container, index_type from, + index_type to, Iter first, Iter last) + { + container.erase(container.begin()+from, container.begin()+to); + container.insert(container.begin()+from, first, last); + } + + static void + delete_item(Container& container, index_type i) + { + container.erase(container.begin()+i); + } + + static void + delete_slice(Container& container, index_type from, index_type to) + { + container.erase(container.begin()+from, container.begin()+to); + } + + static size_t + size(Container& container) + { + return container.size(); + } + + static index_type + convert_index(Container& container, PyObject* i_) + { + extract i(i_); + if (i.check()) + { + long index = i(); + if (index < 0) + index += DerivedPolicies::size(container); + if (index >= container.size() || index < 0) + { + PyErr_SetString(PyExc_IndexError, "Index out of range"); + throw_error_already_set(); + } + return index; + } + + PyErr_SetString(PyExc_TypeError, "Invalid index type"); + throw_error_already_set(); + return index_type(); + } + + static index_type + adjust_index(index_type current, index_type from, + index_type to, size_type len) + { + return current - (difference_type(to) - from - len); + } + }; + +}} // namespace boost::python + +#endif // VECTOR_INDEXING_SUITE_JDG20036_HPP diff --git a/test/Jamfile b/test/Jamfile index adacc831..8812ed66 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -141,6 +141,8 @@ bpl-test nested ; bpl-test docstring ; +bpl-test vector_indexing_suite ; + if $(TEST_BIENSTMAN_NON_BUGS) { bpl-test bienstman4 ; diff --git a/test/vector_indexing_suite.cpp b/test/vector_indexing_suite.cpp new file mode 100644 index 00000000..80c3f97d --- /dev/null +++ b/test/vector_indexing_suite.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +using namespace boost::python; + +struct X // a container element +{ + std::string s; + X():s("default") {} + X(std::string s):s(s) {} + std::string repr() const { return s; } + void reset() { s = "reset"; } + void foo() { s = "foo"; } +}; + +std::string x_value(X const& x) +{ + return "gotya " + x.s; +} + +BOOST_PYTHON_MODULE(vector_indexing_suite_ext) +{ + class_("X") + .def(init<>()) + .def(init()) + .def(init()) + .def("__repr__", &X::repr) + .def("reset", &X::reset) + .def("foo", &X::foo) + ; + + def("x_value", x_value); + implicitly_convertible(); + + class_ >("XVec") + .def(vector_indexing_suite >()) + ; +} \ No newline at end of file diff --git a/test/vector_indexing_suite.py b/test/vector_indexing_suite.py new file mode 100644 index 00000000..ce233bc6 --- /dev/null +++ b/test/vector_indexing_suite.py @@ -0,0 +1,306 @@ +''' + +##################################################################### +# Check an object that we will use as container element +##################################################################### + +>>> from vector_indexing_suite_ext import * +>>> x = X('hi') +>>> x +hi +>>> x.reset() # a member function that modifies X +>>> x +reset +>>> x.foo() # another member function that modifies X +>>> x +foo + +# test that a string is implicitly convertible +# to an X +>>> x_value('bochi bochi') +'gotya bochi bochi' + +##################################################################### +# Utility +##################################################################### +>>> def print_xvec(xvec): +... s = '[ ' +... for x in xvec: +... s += repr(x) +... s += ' ' +... s += ']' +... print s + +##################################################################### +# Replace all the contents using slice syntax +##################################################################### + +>>> v = XVec() +>>> v[:] = [X('a'),X('b'),X('c'),X('d'),X('e')] +>>> print_xvec(v) +[ a b c d e ] + +##################################################################### +# Indexing +##################################################################### +>>> len(v) +5 +>>> v[0] +a +>>> v[1] +b +>>> v[2] +c +>>> v[3] +d +>>> v[4] +e +>>> v[-1] +e +>>> v[-2] +d +>>> v[-3] +c +>>> v[-4] +b +>>> v[-5] +a + +##################################################################### +# Deleting an element +##################################################################### + +>>> del v[0] +>>> v[0] = 'yaba' # must do implicit conversion +>>> print_xvec(v) +[ yaba c d e ] + +##################################################################### +# Calling a mutating function of a container element +##################################################################### +>>> v[3].reset() +>>> v[3] +reset + +##################################################################### +# Copying a container element +##################################################################### +>>> x = X(v[3]) +>>> x +reset +>>> x.foo() +>>> x +foo +>>> v[3] # should not be changed to 'foo' +reset + +##################################################################### +# Referencing a container element +##################################################################### +>>> x = v[3] +>>> x +reset +>>> x.foo() +>>> x +foo +>>> v[3] # should be changed to 'foo' +foo + +##################################################################### +# Slice +##################################################################### + +>>> sl = v[0:2] +>>> print_xvec(v) +[ yaba c d foo ] +>>> sl[0].reset() +>>> sl[0] +reset + +##################################################################### +# Reset the container again +##################################################################### +>>> v[:] = ['a','b','c','d','e'] # perform implicit conversion to X +>>> print_xvec(v) +[ a b c d e ] + +##################################################################### +# Slice: replace [1:3] with an element +##################################################################### +>>> v[1:3] = X('z') +>>> print_xvec(v) +[ a z d e ] + +##################################################################### +# Slice: replace [0:2] with a list +##################################################################### +>>> v[0:2] = ['1','2','3','4'] # perform implicit conversion to X +>>> print_xvec(v) +[ 1 2 3 4 d e ] + +##################################################################### +# Slice: delete [3:4] +##################################################################### +>>> del v[3:4] +>>> print_xvec(v) +[ 1 2 3 d e ] + +##################################################################### +# Slice: set [3:] to a list +##################################################################### +>>> v[3:] = [X('trailing'), X('stuff')] # a list +>>> print_xvec(v) +[ 1 2 3 trailing stuff ] + +##################################################################### +# Slice: delete [:3] +##################################################################### +>>> del v[:3] +>>> print_xvec(v) +[ trailing stuff ] + +##################################################################### +# Slice: insert a tuple to [0:0] +##################################################################### +>>> v[0:0] = ('leading','stuff') # can also be a tuple +>>> print_xvec(v) +[ leading stuff trailing stuff ] + +##################################################################### +# Reset the container again +##################################################################### +>>> v[:] = ['a','b','c','d','e'] + +##################################################################### +# Some references to the container elements +##################################################################### +>>> z0 = v[0] +>>> z1 = v[1] +>>> z2 = v[2] +>>> z3 = v[3] +>>> z4 = v[4] + +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy +c +>>> z3 # proxy +d +>>> z4 # proxy +e + +##################################################################### +# Delete a container element +##################################################################### + +>>> del v[2] +>>> print_xvec(v) +[ a b d e ] + +##################################################################### +# Show that the references are still valid +##################################################################### +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy detached +c +>>> z3 # proxy index adjusted +d +>>> z4 # proxy index adjusted +e + +##################################################################### +# Delete all container elements +##################################################################### +>>> del v[:] +>>> print_xvec(v) +[ ] + +##################################################################### +# Show that the references are still valid +##################################################################### +>>> z0 # proxy detached +a +>>> z1 # proxy detached +b +>>> z2 # proxy detached +c +>>> z3 # proxy detached +d +>>> z4 # proxy detached +e + +##################################################################### +# Reset the container again +##################################################################### +>>> v[:] = ['a','b','c','d','e'] + +##################################################################### +# renew the references to the container elements +##################################################################### +>>> z0 = v[0] +>>> z1 = v[1] +>>> z2 = v[2] +>>> z3 = v[3] +>>> z4 = v[4] + +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy +c +>>> z3 # proxy +d +>>> z4 # proxy +e + +##################################################################### +# Set [2:4] to a list such that there will be more elements +##################################################################### +>>> v[2:4] = ['x','y','v'] +>>> print_xvec(v) +[ a b x y v e ] + +##################################################################### +# Show that the references are still valid +##################################################################### +>>> z0 # proxy +a +>>> z1 # proxy +b +>>> z2 # proxy detached +c +>>> z3 # proxy detached +d +>>> z4 # proxy index adjusted +e + +##################################################################### +# END.... +##################################################################### + +''' + + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print 'running...' + import sys + sys.exit(run()[0]) + + + + +