PCB Environment 2
Loading...
Searching...
No Matches
PyObject.hpp
1
2#include "Py.hpp"
3
4namespace py
5{
6
8class Object
9{
10public:
11 Object(PyObject *py) : mPy(py) { }
12
13 // These create a new reference:
14 Object(bool);
15 Object(double);
16 Object(long);
17 Object(const char *);
18 Object(const std::string&);
19 Object(const Point_2&);
20 Object(const Point_25&);
21 Object(const IPoint_3&);
22 Object(const Bbox_2&);
23
24 static Object new_List(uint n, PyObject *v = 0);
25 static Object new_TupleOfLongs(const Point_25 &v, Real s);
26 template<typename T> static Object new_TupleOfLongs(const T *, uint size);
27 template<typename T> static Object new_ListFrom(const std::vector<T>&, auto&&...);
28
29 const Object& incRef() const;
30 const Object& decRef() const;
31
32 PyObject **o() { return &mPy; }
33 PyObject *operator*() const { return mPy; }
34
35 Object at(uint i) const;
36 Object elem(uint i, const char *err = 0) const;
37 Object item(uint i) const;
38 Object item(const char *name) const;
39
40 void setElem(uint i, const Object&) const;
41 void setElem(uint i, PyObject *) const;
42 void setItem(uint i, const Object&) const;
43 void setItem(uint i, PyObject *) const;
44 void setDictItem(uint i, PyObject *) const;
45 void setItem(const char *name, const Object&) const;
46 void refItem(const char *name, const Object&) const;
47 void setItem(const char *name, PyObject *) const;
48 void refItem(const char *name, PyObject *) const;
49
50 bool isNone() const { return mPy == Py_None; }
51 bool isDict() const;
52 bool isList() const;
53 bool isList(uint n) const;
54 bool hasListItem(uint i) const;
55 bool isTuple() const;
56 bool isTuple(uint n) const;
57 bool hasTupleElem(uint i) const;
58 bool isTupleOrList(uint n) const;
59
60 uint listSize(const char *err = "object is not a list") const;
61
62 template<typename T> T asIntegerContainer() const;
63 template<typename T> T asStringContainer() const;
64
65 operator bool() const;
66 // Amazing C++ feature: disable cast to ints
67 template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
68 operator T() const = delete;
69
70 bool isBool() const;
71 bool isBool(bool value) const;
72 bool isFloat() const;
73 bool isLong() const;
74 bool isNumber() const;
75 bool asBool(const char *err = "object is not a boolean") const;
76 bool toBool() const;
77 double asDouble(const char *err = "object is not a float") const;
78 double toDouble(const char *err = "object is not a number") const;
79 long asLong() const;
80 long toLong(const char *err = "object is not a number") const;
81
82 bool isString() const;
83 std::string asString(const char *err = "object is not a string") const;
84 std::string toString() const;
85 std::string_view asStringView(const char *err = "object is not a string") const;
86
87 bool isVector(uint n) const;
88 bool isLongVector(uint n) const;
89 bool isPoint_25() const;
90 IPoint_2 asIPoint_2(const char *err = "object is not a 2D integer point") const;
91 IPoint_3 asIPoint_3(const char *err = "object is not a 3D integer point") const;
92 IVector_2 asIVector_2(const char *err = "object is not a 2D integer vector") const;
93 IVector_3 asIVector_3(const char *err = "object is not a 3D integer vector") const;
94 Vector_2 asVector_2(const char *err = "object is not a 2D vector") const;
95 Point_2 asPoint_2(const char *err = "object is not a 2D point") const;
96 Point_25 asPoint_25(const char *err = "object is not a 2.5D point") const;
97
98 ::Bbox_2 asBbox_2() const;
99 ::IBox_3 asIBox_3() const;
100
101 uint64_t asBitmask(uint size) const;
102private:
103 PyObject *mPy;
104};
105
106class BorrowedRef
107{
108public:
109 BorrowedRef(PyObject *py = 0) : mRef(py) { }
110 Object* operator->() { return &mRef; }
111 Object& operator*() { return mRef; }
112 operator bool() const { return (bool)mRef; }
113private:
114 Object mRef;
115};
116
117class ObjectRef
118{
119public:
120 ObjectRef(PyObject *, uint refs = 0);
121 ~ObjectRef();
122 void reset(PyObject *, uint refs = 0);
123 const Object* operator->() const { return &mRef; }
124 const Object& operator*() const { return mRef; }
125 PyObject *getRef() const;
126private:
127 Object mRef;
128};
129inline ObjectRef::ObjectRef(PyObject *py, uint refs) : mRef(py)
130{
131 while (refs--)
132 mRef.incRef();
133}
134inline ObjectRef::~ObjectRef()
135{
136 mRef.decRef();
137}
138inline PyObject *ObjectRef::getRef() const
139{
140 return *mRef.incRef();
141}
142inline void ObjectRef::reset(PyObject *py, uint refs)
143{
144 if (py && py == *mRef)
145 throw std::runtime_error("objectref reset object must differ");
146 if (refs && !py)
147 throw std::runtime_error("objectref asked to reference 0");
148 while (refs--)
149 Py_INCREF(py);
150 mRef.decRef();
151 *(mRef.o()) = py;
152}
153
154struct DictIterator
155{
156 DictIterator(PyObject *py);
157 bool next();
158 Object d;
159 Object k;
160 Object v;
161 Py_ssize_t pos{0};
162};
163inline DictIterator::DictIterator(PyObject *py) : d(py), k(Py_None), v(Py_None)
164{
165 if (!d.isDict())
166 throw std::invalid_argument("object is not a dictionary");
167}
168inline bool DictIterator::next()
169{
170 return PyDict_Next(*d, &pos, k.o(), v.o());
171}
172
173inline Object::Object(bool b) : mPy(PyBool_FromLong(b))
174{
175}
176
177inline Object::Object(double d) : mPy(PyFloat_FromDouble(d))
178{
179}
180
181inline Object::Object(long l) : mPy(PyLong_FromLong(l))
182{
183}
184
185inline Object::Object(const char *s) : mPy(PyUnicode_FromString(s))
186{
187}
188
189inline Object::Object(const std::string &s) : mPy(PyUnicode_FromStringAndSize(s.c_str(), s.size()))
190{
191}
192
193inline Object::Object(const Point_2 &v) : mPy(PyTuple_New(2))
194{
195 PyTuple_SetItem(mPy, 0, PyFloat_FromDouble(v.x()));
196 PyTuple_SetItem(mPy, 1, PyFloat_FromDouble(v.y()));
197}
198
199inline Object::Object(const Point_25 &v) : mPy(PyTuple_New(3))
200{
201 PyTuple_SetItem(mPy, 0, PyFloat_FromDouble(v.x()));
202 PyTuple_SetItem(mPy, 1, PyFloat_FromDouble(v.y()));
203 PyTuple_SetItem(mPy, 2, PyLong_FromLong(v.z()));
204}
205
206inline Object::Object(const IPoint_3 &v) : mPy(PyTuple_New(3))
207{
208 PyTuple_SetItem(mPy, 0, PyLong_FromLong(v.x));
209 PyTuple_SetItem(mPy, 1, PyLong_FromLong(v.y));
210 PyTuple_SetItem(mPy, 2, PyLong_FromLong(v.z));
211}
212
213inline const Object& Object::incRef() const
214{
215 if (mPy)
216 Py_INCREF(mPy);
217 return *this;
218}
219inline const Object& Object::decRef() const
220{
221 if (mPy)
222 Py_DECREF(mPy);
223 return *this;
224}
225
226inline Object Object::new_List(uint n, PyObject *v)
227{
228 auto py = PyList_New(n);
229 if (v)
230 for (uint i = 0; i < n; ++i)
231 PyList_SetItem(py, i, NewRef(v));
232 return Object(py);
233}
234
235template<typename T> inline Object Object::new_ListFrom(const std::vector<T> &data, auto&&... args)
236{
237 auto py = PyList_New(data.size());
238 for (uint i = 0; i < data.size(); ++i)
239 PyList_SetItem(py, i, data[i].getPy(std::forward<decltype(args)>(args)...));
240 return Object(py);
241}
242
243inline Object Object::new_TupleOfLongs(const Point_25 &v, Real s)
244{
245 auto py = PyTuple_New(3);
246 PyTuple_SetItem(py, 0, PyFloat_FromDouble(v.x() * s));
247 PyTuple_SetItem(py, 1, PyFloat_FromDouble(v.y() * s));
248 PyTuple_SetItem(py, 2, PyLong_FromLong(v.z()));
249 return Object(py);
250}
251template<typename T> Object Object::new_TupleOfLongs(const T *array, uint size)
252{
253 auto py = PyTuple_New(size);
254 for (uint i = 0; i < size; ++i)
255 PyTuple_SetItem(py, i, PyLong_FromLong(array[i]));
256 return Object(py);
257}
258
259inline Object::operator bool() const
260{
261 return mPy && mPy != Py_None;
262}
263
264inline bool Object::isBool() const
265{
266 return mPy && PyBool_Check(mPy);
267}
268inline bool Object::isBool(bool value) const
269{
270 return mPy == (value ? Py_True : Py_False);
271}
272
273inline bool Object::isFloat() const
274{
275 return mPy && PyFloat_Check(mPy);
276}
277
278inline bool Object::isLong() const
279{
280 return mPy && PyLong_Check(mPy);
281}
282
283inline bool Object::isNumber() const
284{
285 return mPy && PyNumber_Check(mPy);
286}
287
288inline bool Object::asBool(const char *err) const
289{
290 if (err && !isBool())
291 throw std::invalid_argument(err);
292 return Py_IsTrue(mPy);
293}
294
295inline bool Object::toBool() const
296{
297 return mPy && Py_IsTrue(mPy);
298}
299
300inline double Object::asDouble(const char *err) const
301{
302 if (!isFloat())
303 throw std::invalid_argument(err);
304 return PyFloat_AsDouble(mPy);
305}
306
307inline double Object::toDouble(const char *err) const
308{
309 if (err && !isNumber())
310 throw std::invalid_argument(err);
311 if (isFloat())
312 return asDouble();
313 auto F = PyNumber_Float(mPy);
314 assert(F);
315 if (!F)
316 return std::numeric_limits<double>::quiet_NaN();
317 auto d = PyFloat_AsDouble(F);
318 Py_DECREF(F);
319 return d;
320}
321
322inline long Object::asLong() const
323{
324 assert(isLong());
325 return PyLong_AsLong(mPy);
326}
327
328inline long Object::toLong(const char *err) const
329{
330 if (err && !PyNumber_Check(mPy))
331 throw std::invalid_argument(err);
332 if (isLong())
333 return asLong();
334 auto L = PyNumber_Long(mPy);
335 assert(L);
336 if (!L)
337 return 0L;
338 auto li = PyLong_AsLong(L);
339 Py_DECREF(L);
340 return li;
341}
342
343inline bool Object::isString() const
344{
345 return mPy && PyUnicode_Check(mPy);
346}
347
348inline std::string Object::asString(const char *err) const
349{
350 if (err && !isString())
351 throw std::invalid_argument(err);
352 assert(isString());
353#if PY_VERSION_HEX >= 0x03030000
354 return std::string(PyUnicode_AsUTF8(mPy));
355#else
356#error "Python version < 3.3 not supported, go upgrade!"
357#endif
358}
359
360inline std::string Object::toString() const
361{
362 if (!mPy)
363 return "";
364 if (isString())
365 return asString();
366 return ObjectRef(PyObject_Str(mPy))->asString();
367}
368
369inline std::string_view Object::asStringView(const char *err) const
370{
371 if (err && !isString())
372 throw std::invalid_argument(err);
373 assert(isString());
374#if PY_VERSION_HEX >= 0x03030000
375 Py_ssize_t size;
376 const char *ucs = PyUnicode_AsUTF8AndSize(mPy, &size);
377 return std::string_view(ucs, size);
378#else
379#error "Python version < 3.3 not supported, go upgrade!"
380#endif
381}
382
383inline bool Object::isDict() const
384{
385 return mPy && PyDict_Check(mPy);
386}
387inline bool Object::isList() const
388{
389 return mPy && PyList_Check(mPy);
390}
391inline bool Object::isList(uint n) const
392{
393 return isList() && PyList_Size(mPy) == n;
394}
395inline bool Object::hasListItem(uint i) const
396{
397 return isList() && PyList_Size(mPy) > i;
398}
399inline bool Object::isTuple() const
400{
401 return mPy && PyTuple_Check(mPy);
402}
403inline bool Object::isTuple(uint n) const
404{
405 return isTuple() && PyTuple_Size(mPy) == n;
406}
407inline bool Object::hasTupleElem(uint i) const
408{
409 return isTuple() && PyTuple_Size(mPy) > i;
410}
411inline bool Object::isTupleOrList(uint n) const
412{
413 return isTuple(n) || isList(n);
414}
415
416template<typename T> T Object::asIntegerContainer() const
417{
418 auto I = PyObject_GetIter(mPy);
419 if (!I)
420 throw std::invalid_argument("object is not an iterable");
421 T res;
422 while (auto v = PyIter_Next(I)) {
423 if (!PyLong_Check(v))
424 throw std::invalid_argument("expected an iterable of integers");
425 res.insert(res.end(), PyLong_AsLong(v));
426 Py_DECREF(v);
427 }
428 Py_DECREF(I);
429 return res;
430}
431template<typename T> T Object::asStringContainer() const
432{
433 auto I = PyObject_GetIter(mPy);
434 if (!I)
435 throw std::invalid_argument("object is not an iterable");
436 T res;
437 while (auto _v = PyIter_Next(I)) {
438 Object v(_v);
439 if (!v.isString())
440 throw std::invalid_argument("expected an iterable of strings");
441 res.insert(res.end(), v.asString());
442 v.decRef();
443 }
444 Py_DECREF(I);
445 return res;
446}
447
448inline Object Object::at(uint i) const
449{
450 if (isList())
451 return item(i);
452 if (!isTuple())
453 throw std::invalid_argument("expected a list or tuple");
454 return elem(i);
455}
456inline Object Object::elem(uint i, const char *err) const
457{
458 if (err && !hasTupleElem(i))
459 throw std::invalid_argument(err);
460 assert(hasTupleElem(i));
461 return PyTuple_GetItem(mPy, i);
462}
463inline Object Object::item(uint i) const
464{
465 assert(hasListItem(i));
466 return PyList_GetItem(mPy, i);
467}
468inline Object Object::item(const char *name) const
469{
470 assert(isDict());
471 return PyDict_GetItemString(mPy, name);
472}
473
474inline void Object::setElem(uint i, const Object &v) const
475{
476 setElem(i, *v);
477}
478inline void Object::setElem(uint i, PyObject *v) const
479{
480 assert(hasTupleElem(i));
481 PyTuple_SetItem(mPy, i, v);
482}
483
484inline void Object::setItem(uint i, const Object &v) const
485{
486 setItem(i, *v);
487}
488inline void Object::setItem(uint i, PyObject *v) const
489{
490 assert(hasListItem(i));
491 PyList_SetItem(mPy, i, v);
492}
493
494inline void Object::setItem(const char *name, const Object &v) const
495{
496 setItem(name, *v);
497}
498inline void Object::setItem(const char *name, PyObject *v) const
499{
500 assert(isDict());
501 assert(v);
502 PyDict_SetItemString(mPy, name, v);
503 Py_DECREF(v);
504}
505inline void Object::setDictItem(uint i, PyObject *v) const
506{
507 assert(isDict());
508 PyDict_SetItem(mPy, PyLong_FromLong(i), v);
509 Py_DECREF(v);
510}
511inline void Object::refItem(const char *name, PyObject *v) const
512{
513 assert(isDict());
514 PyDict_SetItemString(mPy, name, v);
515}
516inline void Object::refItem(const char *name, const Object &v) const
517{
518 refItem(name, *v);
519}
520
521inline bool Object::isVector(uint n) const
522{
523 if (!isTuple(n))
524 return false;
525 for (uint i = 0; i < n; ++i)
526 if (!elem(i).isNumber())
527 return false;
528 return true;
529}
530inline bool Object::isLongVector(uint n) const
531{
532 if (!isTuple(n))
533 return false;
534 for (uint i = 0; i < n; ++i)
535 if (!elem(i).isLong())
536 return false;
537 return true;
538}
539inline bool Object::isPoint_25() const
540{
541 return isTuple(3) && elem(0).isNumber() && elem(1).isNumber() && elem(2).isLong();
542}
543
544inline IPoint_2 Object::asIPoint_2(const char *err) const
545{
546 if (err && !isLongVector(2))
547 throw std::invalid_argument(err);
548 return IPoint_2(elem(0).asLong(), elem(1).asLong());
549}
550
551inline IPoint_3 Object::asIPoint_3(const char *err) const
552{
553 if (err && !isLongVector(3))
554 throw std::invalid_argument(err);
555 return IPoint_3(elem(0).asLong(), elem(1).asLong(), elem(2).asLong());
556}
557
558inline IVector_2 Object::asIVector_2(const char *err) const
559{
560 if (err && !isLongVector(2))
561 throw std::invalid_argument(err);
562 return IVector_2(elem(0).asLong(), elem(1).asLong());
563}
564
565inline IVector_3 Object::asIVector_3(const char *err) const
566{
567 if (err && !isLongVector(3))
568 throw std::invalid_argument(err);
569 return IVector_3(elem(0).asLong(), elem(1).asLong(), elem(2).asLong());
570}
571
572inline Vector_2 Object::asVector_2(const char *err) const
573{
574 if (err && !isVector(2))
575 throw std::invalid_argument(err);
576 return Vector_2(elem(0).toDouble(), elem(1).toDouble());
577}
578
579inline Point_2 Object::asPoint_2(const char *err) const
580{
581 if (err && !isVector(2))
582 throw std::invalid_argument("object is not a 2D point");
583 return Point_2(elem(0).toDouble(), elem(1).toDouble());
584}
585
586inline Point_25 Object::asPoint_25(const char *err) const
587{
588 if (err && !isPoint_25())
589 throw std::invalid_argument(err);
590 return Point_25(elem(0).toDouble(), elem(1).toDouble(), elem(2).asLong());
591}
592
593inline uint Object::listSize(const char *err) const
594{
595 if (err && !PyList_Check(mPy))
596 throw std::invalid_argument(err);
597 return PyList_Size(mPy);
598}
599
600} // namespace py
Definition IPoint2.hpp:37
Definition IPoint3.hpp:36
Definition IPoint2.hpp:10
Definition IPoint3.hpp:8
Definition Geometry.hpp:131
NOTE: This does not do any automatic reference counting.
Definition PyObject.hpp:9
Definition IPoint3.hpp:60