Library of Assembled Shared Sources
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-2025 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 "../util/string_cast.h"
54
55#include <vector>
56#include <list>
57#include <deque>
58
59namespace lass
60{
61namespace python
62{
63namespace impl
64{
65 class LASS_PYTHON_DLL PySequenceImplBase: public ContainerImplBase
66 {
67 public:
68 typedef std::unique_ptr<PySequenceImplBase> TPimpl;
69 virtual TPimpl copy() const = 0;
70 virtual bool reserve(Py_ssize_t n) = 0;
71 virtual bool append(const TPyObjPtr& i) = 0;
72 virtual bool pop(Py_ssize_t i) = 0;
73 virtual PyObject* item(Py_ssize_t i) const = 0;
74 virtual PyObject* slice(Py_ssize_t low, Py_ssize_t high, Py_ssize_t step) const = 0;
75 virtual int assItem(Py_ssize_t i, PyObject* obj) = 0;
76 virtual int assSlice(Py_ssize_t low, Py_ssize_t high, Py_ssize_t step, PyObject* obj) = 0;
77 virtual int contains(PyObject* obj) const = 0;
78 virtual bool inplaceConcat(PyObject* obj) = 0;
79 virtual bool inplaceRepeat(Py_ssize_t n) = 0;
80 };
81
82 template<typename Container>
83 class PySequenceContainer: public ContainerImpl<Container, PySequenceImplBase>
84 {
85 public:
86 typedef ContainerImpl<Container, PySequenceImplBase> TBase;
87 typedef typename TBase::TContainerPtr TContainerPtr;
88 typedef typename TBase::TConstContainerPtr TConstContainerPtr;
89 typedef typename TBase::TContainerTraits TContainerTraits;
90 typedef typename TContainerTraits::iterator TIterator;
91 typedef typename TContainerTraits::const_iterator TConstIterator;
92 typedef ArgumentTraits<typename TContainerTraits::value_type> TArgTraits;
93 typedef PySequenceImplBase::TPimpl TPimpl;
94
95 PySequenceContainer(const TContainerPtr& container, bool readOnly = false):
96 TBase(container, readOnly)
97 {
98 }
99 ~PySequenceContainer()
100 {
101 }
102 const TPyObjPtr asNative() const override
103 {
104 return pyBuildList(this->begin(), this->next(this->begin(), this->length()));
105 }
106 TPimpl copy() const override
107 {
108 TContainerPtr copy = TContainerTraits::copy(this->container());
109 return TPimpl(new PySequenceContainer(copy));
110 }
111 bool reserve(Py_ssize_t n) override
112 {
113 if (!this->checkWritable())
114 {
115 return false;
116 }
117 TContainerTraits::reserve(this->container(), n);
118 return true;
119 }
120 bool append(const TPyObjPtr& obj) override
121 {
122 if (!this->checkWritable())
123 {
124 return false;
125 }
126 typename TArgTraits::TStorage value;
127 if (pyGetSimpleObject(obj.get(), value) != 0)
128 {
129 return false;
130 }
131 TIterator end = this->next(this->begin(), this->length());
132 TContainerTraits::insert(this->container(), end, TArgTraits::arg(value));
133 return true;
134 }
135 bool pop(Py_ssize_t i) override
136 {
137 if (!this->checkWritable())
138 {
139 return false;
140 }
141 if (!this->checkIndex(i))
142 {
143 return false;
144 }
145 TContainerTraits::erase(this->container(), this->next(this->begin(), i), this->next(this->begin(), i + 1));
146 return true;
147 }
148 PyObject* item(Py_ssize_t i) const override
149 {
150 if (!this->checkIndex(i))
151 {
152 return 0;
153 }
154 return pyBuildSimpleObject(*this->next(this->begin(), i));
155 }
156 PyObject* slice(Py_ssize_t low, Py_ssize_t high, Py_ssize_t step) const override
157 {
158 const Py_ssize_t size = this->length();
159 low = num::clamp(low, Py_ssize_t(0), size);
160 high = num::clamp(high, low, size);
161 step = std::max(step, Py_ssize_t(1));
162 const Py_ssize_t n = (high - low + step - 1) / step;
163 TPyObjPtr s(PyList_New(n));
164 TConstIterator first = this->next(this->begin(), low);
165 for (Py_ssize_t i = 0; i < n; ++i)
166 {
167 if (i > 0)
168 {
169 // this odd place of advancing is to avoid stepping beyond end
170 std::advance(first, step);
171 }
172 PyList_SET_ITEM(s.get(), i, pyBuildSimpleObject(*first));
173 }
174 return fromSharedPtrToNakedCast(s);
175 }
176 int assItem(Py_ssize_t i, PyObject* obj) override
177 {
178 if (!this->checkWritable() || !this->checkIndex(i))
179 {
180 return -1;
181 }
182 if (obj == 0)
183 {
184 return assSlice(i, i + 1, 1, 0);
185 }
186 if (pyGetSimpleObject(obj, *this->next(this->begin(), i)) != 0)
187 {
188 return -1;
189 }
190 return 0;
191 }
192 int assSlice(Py_ssize_t low, Py_ssize_t high, Py_ssize_t step, PyObject* other) override
193 {
194 if (!this->checkWritable())
195 {
196 return -1;
197 }
198 const Py_ssize_t size = this->length();
199 low = num::clamp(low, Py_ssize_t(0), size);
200 high = num::clamp(high, low, size);
201 step = std::max(step, Py_ssize_t(1));
202 TConstContainerPtr b;
203 if (other)
204 {
205 if (pyGetSimpleObject(other, b) != 0)
206 {
207 return -1;
208 }
209 if (&this->container() == b.get())
210 {
211 b = TContainerTraits::copy(*b);
212 }
213 }
214 if (step == 1)
215 {
216 const TIterator first = this->next(this->begin(), low);
217 const TIterator last = this->next(first, high - low);
218 TContainerTraits::erase(this->container(), first, last);
219 if (b)
220 {
221 const TConstIterator bFirst = TContainerTraits::begin(*b);
222 const TConstIterator bLast = this->next(bFirst, TContainerTraits::size(*b));
223 TContainerTraits::insert(this->container(), this->next(this->begin(), low), bFirst, bLast);
224 }
225 }
226 else
227 {
228 if (b)
229 {
230 const Py_ssize_t sliceLength = (high - low + step - 1) / step;
231 const Py_ssize_t bLength = TContainerTraits::size(*b);
232 if (sliceLength != bLength)
233 {
234 std::ostringstream buffer;
235 buffer << "cannot assign sequence of length " << bLength << " to an extended slice of length " << sliceLength;
236 PyErr_SetString(PyExc_ValueError, buffer.str().c_str());
237 return -1;
238 }
239 TIterator left = this->next(this->begin(), low);
240 TConstIterator right = TContainerTraits::begin(*b);
241 while (low < high)
242 {
243 *left = *right++;
244 low += step;
245 if (low < high) // make sure you don't step beyond end
246 {
247 left = this->next(left, step);
248 }
249 }
250 }
251 else
252 {
253 while (low < high)
254 {
255 TContainerTraits::erase(this->container(), this->next(this->begin(), low));
256 low += step - 1;
257 --high;
258 }
259 }
260 }
261 return 0;
262 }
263 int contains(PyObject* obj) const override
264 {
265 typename TArgTraits::TStorage value;
266 if (pyGetSimpleObject(obj, value) != 0)
267 {
268 if (PyErr_ExceptionMatches(PyExc_TypeError))
269 {
270 // type is not convertible and hence is not found
271 // this corresponds to the Python behavior
272 PyErr_Clear();
273 return 0;
274 }
275 return -1;
276 }
277 TConstIterator end = this->next(this->begin(), this->length());
278 if (std::find(this->begin(), end, TArgTraits::arg(value)) != end)
279 {
280 return 1;
281 }
282 return 0;
283 }
284 bool inplaceConcat(PyObject* other) override
285 {
286 if (!this->checkWritable())
287 {
288 return false;
289 }
290 TConstContainerPtr b;
291 if (pyGetSimpleObject(other, b) != 0)
292 {
293 return false;
294 }
295 if (&this->container() == b.get())
296 {
297 b = TContainerTraits::copy(*b);
298 }
299 const TIterator end = this->next(this->begin(), this->length());
300 const TConstIterator first = TContainerTraits::begin(*b);
301 const TConstIterator last = this->next(first, TContainerTraits::size(*b));
302 TContainerTraits::insert(this->container(), end, first, last);
303 return true;
304 }
305 bool inplaceRepeat(Py_ssize_t n) override
306 {
307 if (!this->checkWritable())
308 {
309 return false;
310 }
311 TContainerTraits::inplace_repeat(this->container(),n);
312 return true;
313 }
314 private:
315 bool checkIndex(Py_ssize_t& i) const
316 {
317 const Py_ssize_t size = this->length();
318 if (i < 0 || i >= size)
319 {
320 PyErr_SetString(PyExc_IndexError,"list assignment index out of range");
321 return false;
322 }
323 return true;
324 }
325 };
326
327 class Sequence;
328 typedef PyObjectPtr<Sequence>::Type TSequencePtr;
329
330 /** Object for interfacing sequence-like objects with Python
331 */
332 class LASS_PYTHON_DLL Sequence : public PyObjectPlus, util::NonCopyable
333 {
334 PY_HEADER(PyObjectPlus)
335
336 public:
337 template<typename Container> Sequence( const util::SharedPtr<Container>& container )
338 {
339 TPimpl pimpl(new PySequenceContainer<Container>(
340 LASS_ENFORCE_POINTER(container)));
341 init(std::move(pimpl));
342 }
343 template<typename Container> Sequence( const util::SharedPtr<const Container>& container )
344 {
345 TPimpl pimpl(new PySequenceContainer<Container>(
346 LASS_ENFORCE_POINTER(container).template constCast<Container>(), true));
347 init(std::move(pimpl));
348 }
349 template<typename Container> Sequence( const Container& container )
350 {
351 util::SharedPtr<Container> p(ContainerTraits<Container>::copy(container));
352 TPimpl pimpl(new PySequenceContainer<Container>(p, true));
353 init(std::move(pimpl));
354 }
355
356 const TSequencePtr copy() const;
357 void clear();
358 void reserve(Py_ssize_t n);
359 void append(const TPyObjPtr& obj);
360 const TPyObjPtr pop(Py_ssize_t i);
361 const TPyObjPtr pop_back();
362 const TPyObjPtr asList() const;
363 const TPyObjPtr iter() const;
364 std::string repr() const;
365
366 const std::type_info& type() const;
367 void* raw(bool writable) const;
368
369 static Py_ssize_t length(PyObject* self);
370 static PyObject* concat(PyObject* self, PyObject* other);
371 static PyObject* repeat(PyObject* self, Py_ssize_t n);
372 static PyObject* item(PyObject* self, Py_ssize_t i);
373 static int assItem(PyObject* self, Py_ssize_t i, PyObject* obj);
374 static int contains(PyObject* a, PyObject* obj);
375 static PyObject* inplaceConcat(PyObject* self, PyObject* other);
376 static PyObject* inplaceRepeat(PyObject* self, Py_ssize_t n);
377 static PyObject* subscript(PyObject* self, PyObject* key);
378 static int assSubscript(PyObject* self, PyObject* key, PyObject* value);
379
380 private:
381 typedef PySequenceImplBase::TPimpl TPimpl;
382
383 Sequence(TPimpl&& pimpl);
384 void init(TPimpl&& pimpl);
385
386 TPimpl pimpl_;
387 };
388
389 template <>
390 struct ShadowTraits<Sequence>: public ShadowTraitsContainer< Sequence, ShadowTraits<Sequence> >
391 {
392 template <typename Container> static int getObjectImpl(PyObject* obj, util::SharedPtr<Container>& value, bool writable)
393 {
394 if (!PySequence_Check(obj))
395 {
396 PyErr_SetString(PyExc_TypeError, "not a sequence");
397 return 1;
398 }
399
400 // check if we have our own Sequence object, then take a shortcut
401 if (obj->ob_type == Sequence::_lassPyClassDef.type())
402 {
403 const Sequence* const sequence = static_cast<Sequence*>(obj);
404 void* const raw = sequence->raw(writable);
405 if (raw && typeid(value) == sequence->type())
406 {
407 value = *static_cast< util::SharedPtr<Container>* >(raw);
408 return 0;
409 }
410 }
411
412 const util::SharedPtr<Container> result(new Container);
413 const Py_ssize_t size = PySequence_Length(obj);
414 ContainerTraits<Container>::reserve(*result, size);
415 typedef ArgumentTraits<typename Container::value_type> TArgTraits;
416 for (Py_ssize_t i = 0; i < size; ++i)
417 {
418 typename TArgTraits::TStorage temp;
419 TPyObjPtr item( PySequence_ITEM(obj, i) );
420 if (pyGetSimpleObject( item.get() , temp ) != 0)
421 {
422 std::ostringstream buffer;
423 buffer << "sequence element " << i;
424 impl::addMessageHeader(buffer.str().c_str());
425 return 1;
426 }
427 result->emplace_back(TArgTraits::arg(temp));
428 }
429 value = std::move(result);
430 return 0;
431 }
432 };
433}
434
435template <typename ContainerType>
436struct ShadoweeTraitsSequence: meta::True
437{
438 typedef impl::Sequence TShadow;
439 typedef impl::ShadowTraits<impl::Sequence> TShadowTraits;
440 typedef SharedPointerTraits<ContainerType> TPointerTraits;
441};
442
443/** @ingroup Python
444 * @internal
445 */
446template< typename C, typename A>
447struct ShadoweeTraits< std::vector< C, A > >:
448 public ShadoweeTraitsSequence< std::vector< C, A > >
449{
450};
451
452/** @ingroup Python
453 * @internal
454 */
455template< typename C, typename A>
456struct ShadoweeTraits< std::list< C, A > >:
457 public ShadoweeTraitsSequence< std::list< C, A > >
458{
459};
460
461/** @ingroup Python
462 * @internal
463 */
464template< typename C, typename A>
465struct ShadoweeTraits< std::deque< C, A > >:
466 public ShadoweeTraitsSequence< std::deque< C, A > >
467{
468};
469
470}
471}
472
473#endif
474
475#ifdef LASS_GUARDIAN_OF_INCLUSION_STDE_STATIC_VECTOR_H
476
477namespace lass
478{
479namespace python
480{
481/** @ingroup Python
482 * @internal
483 */
484template< typename C, size_t maxsize>
485struct ShadoweeTraits< stde::static_vector< C, maxsize > >:
486 public ShadoweeTraitsSequence< stde::static_vector< C, maxsize > >
487{
488};
489}
490}
491
492#endif
493
494// EOF
Object for interfacing sequence-like objects with Python.
Definition pysequence.h:333
use as base class if derived should not be copyable
const T & clamp(const T &x, const T &min, const T &max)
if x < min return min, else if x > max return max, else return x.
Definition basic_ops.h:226
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