Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
pysequence.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2026 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43#ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_PYSEQUENCE_H
44#define LASS_GUARDIAN_OF_INCLUSION_UTIL_PYSEQUENCE_H
45
46#include "python_common.h"
47#include "pyshadow_object.h"
48#include "shadowee_traits.h"
49#include "exception.h"
50#include "container.h"
51#include "argument_traits.h"
52#include "subscript.h"
53#include "../util/string_cast.h"
55
56#include <vector>
57#include <list>
58#include <deque>
59
60namespace lass
61{
62namespace python
63{
64namespace impl
65{
66 class LASS_PYTHON_DLL PySequenceImplBase: public ContainerImplBase
67 {
68 public:
69 typedef std::unique_ptr<PySequenceImplBase> TPimpl;
70 using ContainerImplBase::ContainerImplBase;
71 virtual TPimpl copy() const = 0;
72 virtual bool reserve(Py_ssize_t n) = 0;
73 virtual bool append(const TPyObjPtr& i) = 0;
74 virtual bool pop(Py_ssize_t i) = 0;
75 virtual PyObject* item(Py_ssize_t i) const = 0;
76 virtual PyObject* slice(Slice slice) const = 0;
77 virtual int assItem(Py_ssize_t i, PyObject* obj) = 0;
78 virtual int assSlice(Slice slice, PyObject* obj) = 0;
79 virtual int contains(PyObject* obj) const = 0;
80 virtual bool inplaceConcat(PyObject* obj) = 0;
81 virtual bool inplaceRepeat(Py_ssize_t n) = 0;
82 };
83
84 template<typename Container>
85 class PySequenceContainer: public ContainerImpl<Container, PySequenceImplBase>
86 {
87 public:
88 typedef ContainerImpl<Container, PySequenceImplBase> TBase;
89 typedef typename TBase::TContainerPtr TContainerPtr;
90 typedef typename TBase::TConstContainerPtr TConstContainerPtr;
91 typedef typename TBase::TContainerTraits TContainerTraits;
92 typedef typename TContainerTraits::iterator TIterator;
93 typedef typename TContainerTraits::const_iterator TConstIterator;
94 typedef ArgumentTraits<typename TContainerTraits::value_type> TArgTraits;
95 typedef PySequenceImplBase::TPimpl TPimpl;
96
97 PySequenceContainer(const TContainerPtr& container, bool readOnly = false):
98 TBase(container, readOnly)
99 {
100 }
101 ~PySequenceContainer()
102 {
103 }
104 const TPyObjPtr asNative() const override
105 {
106 return pyBuildList(this->begin(), this->next(this->begin(), this->length()));
107 }
108 TPimpl copy() const override
109 {
110 TContainerPtr copy = TContainerTraits::copy(this->container());
111 return TPimpl(new PySequenceContainer(copy));
112 }
113 bool reserve(Py_ssize_t n) override
114 {
115 if (!this->checkWritable())
116 {
117 return false;
118 }
119 TContainerTraits::reserve(this->container(), n);
120 return true;
121 }
122 bool append(const TPyObjPtr& obj) override
123 {
124 if (!this->checkWritable())
125 {
126 return false;
127 }
128 typename TArgTraits::TStorage value;
129 if (pyGetSimpleObject(obj.get(), value) != 0)
130 {
131 return false;
132 }
133 TIterator end = this->next(this->begin(), this->length());
134 TContainerTraits::insert(this->container(), end, TArgTraits::arg(value));
135 return true;
136 }
137 bool pop(Py_ssize_t i) override
138 {
139 if (!this->checkWritable())
140 {
141 return false;
142 }
143 if (!this->checkIndex(i))
144 {
145 return false;
146 }
147 TContainerTraits::erase(this->container(), this->next(this->begin(), i), this->next(this->begin(), i + 1));
148 return true;
149 }
150 PyObject* item(Py_ssize_t i) const override
151 {
152 if (!this->checkIndex(i))
153 {
154 return 0;
155 }
156 return pyBuildSimpleObject(*this->next(this->begin(), i));
157 }
158 PyObject* slice(Slice slice) const override
159 {
160 Py_ssize_t sliceLength = slice.adjustIndices(this->length());
161 TPyObjPtr s(PyList_New(sliceLength));
162 if (!s)
163 {
164 return nullptr;
165 }
166 TConstIterator first = this->next(this->begin(), slice.start);
167 for (Py_ssize_t i = 0; i < sliceLength; ++i)
168 {
169 if (i > 0)
170 {
171 // this odd place of advancing is to avoid stepping beyond end
172 first = this->next(first, slice.step);
173 }
174 PyObject* item = pyBuildSimpleObject(*first);
175 if (!item)
176 {
177 return nullptr;
178 }
179 PyList_SET_ITEM(s.get(), i, item);
180 }
181 return fromSharedPtrToNakedCast(s);
182 }
183 int assItem(Py_ssize_t i, PyObject* obj) override
184 {
185 if (!this->checkWritable() || !this->checkIndex(i))
186 {
187 return -1;
188 }
189 if (!obj)
190 {
191 Slice slice { i, i + 1, 1 };
192 return assSlice(slice, nullptr);
193 }
194 if (pyGetSimpleObject(obj, *this->next(this->begin(), i)) != 0)
195 {
196 return -1;
197 }
198 return 0;
199 }
200 int assSlice(Slice slice, PyObject* other) override
201 {
202 if (!this->checkWritable())
203 {
204 return -1;
205 }
206
207 TConstContainerPtr b;
208 if (other)
209 {
210 if (pyGetSimpleObject(other, b) != 0)
211 {
212 return -1;
213 }
214 if (&this->container() == b.get())
215 {
216 b = TContainerTraits::copy(*b);
217 }
218 }
219
220 Py_ssize_t sliceLength = slice.adjustIndices(this->length());
221 if (slice.step == 1)
222 {
223 LASS_ASSERT(slice.start + sliceLength <= this->length());
224 const TIterator first = this->next(this->begin(), slice.start);
225 const TIterator last = this->next(first, sliceLength);
226 TContainerTraits::erase(this->container(), first, last);
227 if (b)
228 {
229 const TIterator pos = this->next(this->begin(), slice.start);
230 const TConstIterator bFirst = TContainerTraits::begin(*b);
231 const TConstIterator bLast = this->next(bFirst, TContainerTraits::size(*b));
232 TContainerTraits::insert(this->container(), pos, bFirst, bLast);
233 }
234 }
235 else
236 {
237 if (b)
238 {
239 const Py_ssize_t bLength = TContainerTraits::size(*b);
240 if (sliceLength != bLength)
241 {
242 std::ostringstream buffer;
243 buffer << "cannot assign sequence of length " << bLength << " to an extended slice of length " << sliceLength;
244 PyErr_SetString(PyExc_ValueError, buffer.str().c_str());
245 return -1;
246 }
247 TIterator left = this->next(this->begin(), slice.start);
248 TConstIterator right = TContainerTraits::begin(*b);
249 for (Py_ssize_t i = 0; i < sliceLength; ++i)
250 {
251 if (i > 0)
252 {
253 // this odd place of advancing is to avoid stepping beyond end
254 left = this->next(left, slice.step);
255 }
256 *left = *right++;
257 }
258 }
259 else
260 {
261 Py_ssize_t index = slice.start;
262 Py_ssize_t step = slice.step;
263 if (step > 0 && sliceLength > 0)
264 {
265 // always traverse back to front ...
266 index += (sliceLength - 1) * step;
267 step = -step;
268 }
269 for (Py_ssize_t i = 0; i < sliceLength; ++i, index += step)
270 {
271 // always recompute iterator, as erase on std::deque invalidates _all_ iterators.
272 // slow for std::list, but that's a rare case ...
273 TContainerTraits::erase(this->container(), this->next(this->begin(), index));
274 }
275 }
276 }
277 return 0;
278 }
279 int contains(PyObject* obj) const override
280 {
281 typename TArgTraits::TStorage value;
282 if (pyGetSimpleObject(obj, value) != 0)
283 {
284 if (PyErr_ExceptionMatches(PyExc_TypeError))
285 {
286 // type is not convertible and hence is not found
287 // this corresponds to the Python behavior
288 PyErr_Clear();
289 return 0;
290 }
291 return -1;
292 }
293 TConstIterator end = this->next(this->begin(), this->length());
294 if (std::find(this->begin(), end, TArgTraits::arg(value)) != end)
295 {
296 return 1;
297 }
298 return 0;
299 }
300 bool inplaceConcat(PyObject* other) override
301 {
302 if (!this->checkWritable())
303 {
304 return false;
305 }
306 TConstContainerPtr b;
307 if (pyGetSimpleObject(other, b) != 0)
308 {
309 return false;
310 }
311 if (&this->container() == b.get())
312 {
313 b = TContainerTraits::copy(*b);
314 }
315 const TIterator end = this->next(this->begin(), this->length());
316 const TConstIterator first = TContainerTraits::begin(*b);
317 const TConstIterator last = this->next(first, TContainerTraits::size(*b));
318 TContainerTraits::insert(this->container(), end, first, last);
319 return true;
320 }
321 bool inplaceRepeat(Py_ssize_t n) override
322 {
323 if (!this->checkWritable())
324 {
325 return false;
326 }
327 TContainerTraits::inplace_repeat(this->container(),n);
328 return true;
329 }
330 private:
331 bool checkIndex(Py_ssize_t& i) const
332 {
333 const Py_ssize_t size = this->length();
334 if (i < 0 || i >= size)
335 {
336 PyErr_SetString(PyExc_IndexError,"list assignment index out of range");
337 return false;
338 }
339 return true;
340 }
341 };
342
343 class Sequence;
344 typedef PyObjectPtr<Sequence>::Type TSequencePtr;
345
346 /** Object for interfacing sequence-like objects with Python
347 */
348 class LASS_PYTHON_DLL Sequence : public PyObjectPlus, util::NonCopyable
349 {
350 PY_HEADER(PyObjectPlus)
351
352 public:
353 template<typename Container> Sequence( const util::SharedPtr<Container>& container )
354 {
355 TPimpl pimpl(new PySequenceContainer<Container>(
356 LASS_ENFORCE_POINTER(container)));
357 init(std::move(pimpl));
358 }
359 template<typename Container> Sequence( const util::SharedPtr<const Container>& container )
360 {
361 TPimpl pimpl(new PySequenceContainer<Container>(
362 (LASS_ENFORCE_POINTER(container)).template constCast<Container>(), true));
363 init(std::move(pimpl));
364 }
365 template<typename Container> Sequence( const Container& container )
366 {
367 util::SharedPtr<Container> p(ContainerTraits<Container>::copy(container));
368 TPimpl pimpl(new PySequenceContainer<Container>(p, true));
369 init(std::move(pimpl));
370 }
371
372 const TSequencePtr copy() const;
373 void clear();
374 void reserve(Py_ssize_t n);
375 void append(const TPyObjPtr& obj);
376 const TPyObjPtr pop(Py_ssize_t i);
377 const TPyObjPtr pop_back();
378 const TPyObjPtr asList() const;
379 const TPyObjPtr iter() const;
380 std::string repr() const;
381
382 const std::type_info& type() const;
383 void* raw(bool writable) const;
384
385 static Py_ssize_t length(PyObject* self);
386 static PyObject* concat(PyObject* self, PyObject* other);
387 static PyObject* repeat(PyObject* self, Py_ssize_t n);
388 static PyObject* item(PyObject* self, Py_ssize_t i);
389 static int assItem(PyObject* self, Py_ssize_t i, PyObject* obj);
390 static int contains(PyObject* a, PyObject* obj);
391 static PyObject* inplaceConcat(PyObject* self, PyObject* other);
392 static PyObject* inplaceRepeat(PyObject* self, Py_ssize_t n);
393 static PyObject* subscript(PyObject* self, PyObject* key);
394 static int assSubscript(PyObject* self, PyObject* key, PyObject* value);
395
396 private:
397 typedef PySequenceImplBase::TPimpl TPimpl;
398
399 Sequence(TPimpl&& pimpl);
400 void init(TPimpl&& pimpl);
401
402 TPimpl pimpl_;
403 };
404
405 template <>
406 struct ShadowTraits<Sequence>: public ShadowTraitsContainer< Sequence, ShadowTraits<Sequence> >
407 {
408 template <typename Container> static int getObjectImpl(PyObject* obj, util::SharedPtr<Container>& value, bool writable)
409 {
410 if (!PySequence_Check(obj))
411 {
412 PyErr_SetString(PyExc_TypeError, "not a sequence");
413 return 1;
414 }
415
416 // check if we have our own Sequence object, then take a shortcut
417 if (obj->ob_type == Sequence::_lassPyClassDef.type())
418 {
419 const Sequence* const sequence = static_cast<Sequence*>(obj);
420 void* const raw = sequence->raw(writable);
421 if (raw && typeid(value) == sequence->type())
422 {
423 value = *static_cast< util::SharedPtr<Container>* >(raw);
424 return 0;
425 }
426 }
427
428 const util::SharedPtr<Container> result(new Container);
429 const Py_ssize_t size = PySequence_Length(obj);
430 ContainerTraits<Container>::reserve(*result, size);
431 typedef ArgumentTraits<typename Container::value_type> TArgTraits;
432 for (Py_ssize_t i = 0; i < size; ++i)
433 {
434 typename TArgTraits::TStorage temp;
435 TPyObjPtr item( PySequence_ITEM(obj, i) );
436 if (pyGetSimpleObject( item.get() , temp ) != 0)
437 {
438 std::ostringstream buffer;
439 buffer << "sequence element " << i;
440 impl::addMessageHeader(buffer.str().c_str());
441 return 1;
442 }
443 result->emplace_back(TArgTraits::arg(temp));
444 }
445 value = std::move(result);
446 return 0;
447 }
448 };
449}
450
451template <typename ContainerType>
452struct ShadoweeTraitsSequence: meta::True
453{
454 typedef impl::Sequence TShadow;
455 typedef impl::ShadowTraits<impl::Sequence> TShadowTraits;
456 typedef SharedPointerTraits<ContainerType> TPointerTraits;
457};
458
459/** @ingroup Python
460 * @internal
461 */
462template< typename C, typename A>
463struct ShadoweeTraits< std::vector< C, A > >:
464 public ShadoweeTraitsSequence< std::vector< C, A > >
465{
466};
467
468/** @ingroup Python
469 * @internal
470 */
471template< typename C, typename A>
472struct ShadoweeTraits< std::list< C, A > >:
473 public ShadoweeTraitsSequence< std::list< C, A > >
474{
475};
476
477/** @ingroup Python
478 * @internal
479 */
480template< typename C, typename A>
481struct ShadoweeTraits< std::deque< C, A > >:
482 public ShadoweeTraitsSequence< std::deque< C, A > >
483{
484};
485
486}
487}
488
489#endif
490
491#ifdef LASS_GUARDIAN_OF_INCLUSION_STDE_STATIC_VECTOR_H
492
493namespace lass
494{
495namespace python
496{
497/** @ingroup Python
498 * @internal
499 */
500template< typename C, size_t maxsize>
501struct ShadoweeTraits< stde::static_vector< C, maxsize > >:
502 public ShadoweeTraitsSequence< stde::static_vector< C, maxsize > >
503{
504};
505}
506}
507
508#endif
509
510// EOF
Object for interfacing sequence-like objects with Python.
Definition pysequence.h:349
use as base class if derived should not be copyable
void addMessageHeader(const char *header)
Prepend a message to the current Python exception value.
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
#define PY_HEADER(t_parentClass)
Place as first line of your Pythonized class.
PyObject * fromSharedPtrToNakedCast(const util::SharedPtr< T, PyObjectStorage, PyObjectCounter > &object)
fromSharedPtrToNakedCast.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.
Definition config.h:53