Bug Summary

File:/tmp/pyrefcon/scipy/scipy/_lib/_uarray/_uarray_dispatch.cxx
Warning:line 351, column 37
PyObject ownership leak with reference count of 1

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name _uarray_dispatch.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -analyzer-output=html -analyzer-checker=python -analyzer-disable-checker=deadcode -analyzer-config prune-paths=true,suppress-c++-stdlib=true,suppress-null-return-paths=false,crosscheck-with-z3=true,model-path=/opt/pyrefcon/lib/pyrefcon/models/models -analyzer-config experimental-enable-naive-ctu-analysis=true,ctu-dir=/tmp/pyrefcon/scipy/csa-scan,ctu-index-name=/tmp/pyrefcon/scipy/csa-scan/externalDefMap.txt,ctu-invocation-list=/tmp/pyrefcon/scipy/csa-scan/invocations.yaml,display-ctu-progress=false -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debug-info-kind=limited -dwarf-version=4 -debugger-tuning=gdb -fcoverage-compilation-dir=/tmp/pyrefcon/scipy -resource-dir /opt/pyrefcon/lib/clang/13.0.0 -isystem /opt/pyrefcon/lib/pyrefcon/models/python3.8 -D NDEBUG -D _FORTIFY_SOURCE=2 -I /usr/lib/python3/dist-packages/numpy/core/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /opt/pyrefcon/lib/clang/13.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-unused-result -Wsign-compare -Wall -Wformat -Werror=format-security -Wformat -Werror=format-security -Wdate-time -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/tmp/pyrefcon/scipy -ferror-limit 19 -fvisibility hidden -fwrapv -pthread -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/pyrefcon/scipy/csa-scan/reports -x c++ scipy/_lib/_uarray/_uarray_dispatch.cxx

scipy/_lib/_uarray/_uarray_dispatch.cxx

1#include "small_dynamic_array.h"
2#include "vectorcall.h"
3
4#include <Python.h>
5
6#include <algorithm>
7#include <cstddef>
8#include <new>
9#include <stdexcept>
10#include <string>
11#include <unordered_map>
12#include <utility>
13#include <vector>
14
15
16namespace {
17
18/** Handle to a python object that automatically DECREFs */
19class py_ref {
20 explicit py_ref(PyObject * object): obj_(object) {}
21
22public:
23 py_ref() noexcept: obj_(nullptr) {}
24 py_ref(std::nullptr_t) noexcept: py_ref() {}
25
26 py_ref(const py_ref & other) noexcept: obj_(other.obj_) { Py_XINCREF(obj_)_Py_XINCREF(((PyObject*)(obj_))); }
27 py_ref(py_ref && other) noexcept: obj_(other.obj_) { other.obj_ = nullptr; }
28
29 /** Construct from new reference (No INCREF) */
30 static py_ref steal(PyObject * object) { return py_ref(object); }
31
32 /** Construct from borrowed reference (and INCREF) */
33 static py_ref ref(PyObject * object) {
34 Py_XINCREF(object)_Py_XINCREF(((PyObject*)(object)));
35 return py_ref(object);
36 }
37
38 ~py_ref() { Py_XDECREF(obj_)_Py_XDECREF(((PyObject*)(obj_))); }
39
40 py_ref & operator=(const py_ref & other) noexcept {
41 py_ref(other).swap(*this);
42 return *this;
43 }
44
45 py_ref & operator=(py_ref && other) noexcept {
46 py_ref(std::move(other)).swap(*this);
47 return *this;
48 }
49
50 friend bool operator==(const py_ref & lhs, const py_ref & rhs) {
51 return lhs.obj_ == rhs.obj_;
52 }
53 friend bool operator==(PyObject * lhs, const py_ref & rhs) {
54 return lhs == rhs.obj_;
55 }
56 friend bool operator==(const py_ref & lhs, PyObject * rhs) {
57 return lhs.obj_ == rhs;
58 }
59 friend bool operator!=(const py_ref & lhs, const py_ref & rhs) {
60 return lhs.obj_ != rhs.obj_;
61 }
62 friend bool operator!=(PyObject * lhs, const py_ref & rhs) {
63 return lhs != rhs.obj_;
64 }
65 friend bool operator!=(const py_ref & lhs, PyObject * rhs) {
66 return lhs.obj_ != rhs;
67 }
68
69 void swap(py_ref & other) noexcept { std::swap(other.obj_, obj_); }
70
71 explicit operator bool() const { return obj_ != nullptr; }
72
73 PyObject * get() const { return obj_; }
74 PyObject * release() {
75 PyObject * t = obj_;
76 obj_ = nullptr;
77 return t;
78 }
79 void reset() { Py_CLEAR(obj_)do { PyObject *_py_tmp = ((PyObject*)(obj_)); if (_py_tmp != __null
) { (obj_) = __null; _Py_DECREF(((PyObject*)(_py_tmp))); } } while
(0)
; }
80
81private:
82 PyObject * obj_;
83};
84
85PyObject * py_get(const py_ref & ref) { return ref.get(); }
86PyObject * py_get(PyObject * obj) { return obj; }
87
88/** Make tuple from variadic set of PyObjects */
89template <typename... Ts>
90py_ref py_make_tuple(const Ts &... args) {
91 return py_ref::steal(PyTuple_Pack(sizeof...(args), py_get(args)...));
92}
93
94py_ref py_bool(bool input) { return py_ref::ref(input ? Py_True((PyObject *) &_Py_TrueStruct) : Py_False((PyObject *) &_Py_FalseStruct)); }
95
96template <typename T, size_t N>
97constexpr size_t array_size(const T (&array)[N]) {
98 return N;
99}
100
101struct backend_options {
102 py_ref backend;
103 bool coerce = false;
104 bool only = false;
105
106 bool operator==(const backend_options & other) const {
107 return (
108 backend == other.backend && coerce == other.coerce &&
109 only == other.only);
110 }
111
112 bool operator!=(const backend_options & other) const {
113 return !(*this == other);
114 }
115};
116
117struct global_backends {
118 backend_options global;
119 std::vector<py_ref> registered;
120 bool try_global_backend_last = false;
121};
122
123struct local_backends {
124 std::vector<py_ref> skipped;
125 std::vector<backend_options> preferred;
126};
127
128using global_state_t = std::unordered_map<std::string, global_backends>;
129using local_state_t = std::unordered_map<std::string, local_backends>;
130
131static py_ref BackendNotImplementedError;
132static global_state_t global_domain_map;
133thread_local global_state_t * current_global_state = &global_domain_map;
134thread_local global_state_t thread_local_domain_map;
135thread_local local_state_t local_domain_map;
136
137/** Constant Python string identifiers
138
139Using these with PyObject_GetAttr is faster than PyObject_GetAttrString which
140has to create a new python string internally.
141 */
142struct {
143 py_ref ua_convert;
144 py_ref ua_domain;
145 py_ref ua_function;
146
147 bool init() {
148 ua_convert = py_ref::steal(PyUnicode_InternFromString("__ua_convert__"));
149 if (!ua_convert)
150 return false;
151
152 ua_domain = py_ref::steal(PyUnicode_InternFromString("__ua_domain__"));
153 if (!ua_domain)
154 return false;
155
156 ua_function = py_ref::steal(PyUnicode_InternFromString("__ua_function__"));
157 if (!ua_function)
158 return false;
159
160 return true;
161 }
162
163 void clear() {
164 ua_convert.reset();
165 ua_domain.reset();
166 ua_function.reset();
167 }
168} identifiers;
169
170bool domain_validate(PyObject * domain) {
171 if (!PyUnicode_Check(domain)((((((PyObject*)(domain))->ob_type))->tp_flags & ((
1UL << 28))) != 0)
) {
172 PyErr_SetString(PyExc_TypeError, "__ua_domain__ must be a string");
173 return false;
174 }
175
176 auto size = PyUnicode_GetLength(domain);
177 if (size == 0) {
178 PyErr_SetString(PyExc_ValueError, "__ua_domain__ must be non-empty");
179 return false;
180 }
181
182 return true;
183}
184
185std::string domain_to_string(PyObject * domain) {
186 if (!domain_validate(domain)) {
187 return {};
188 }
189
190 Py_ssize_t size;
191 const char * str = PyUnicode_AsUTF8AndSize(domain, &size);
192 if (!str)
193 return {};
194
195 if (size == 0) {
196 PyErr_SetString(PyExc_ValueError, "__ua_domain__ must be non-empty");
197 return {};
198 }
199
200 return std::string(str, size);
201}
202
203Py_ssize_t backend_get_num_domains(PyObject * backend) {
204 auto domain =
205 py_ref::steal(PyObject_GetAttr(backend, identifiers.ua_domain.get()));
206 if (!domain)
207 return -1;
208
209 if (PyUnicode_Check(domain.get())((((((PyObject*)(domain.get()))->ob_type))->tp_flags &
((1UL << 28))) != 0)
) {
210 return 1;
211 }
212
213 if (!PySequence_Check(domain.get())) {
214 PyErr_SetString(
215 PyExc_TypeError,
216 "__ua_domain__ must be a string or sequence of strings");
217 return -1;
218 }
219
220 return PySequence_Size(domain.get());
221}
222
223enum class LoopReturn { Continue, Break, Error };
224
225template <typename Func>
226LoopReturn backend_for_each_domain(PyObject * backend, Func f) {
227 auto domain =
228 py_ref::steal(PyObject_GetAttr(backend, identifiers.ua_domain.get()));
229 if (!domain)
230 return LoopReturn::Error;
231
232 if (PyUnicode_Check(domain.get())((((((PyObject*)(domain.get()))->ob_type))->tp_flags &
((1UL << 28))) != 0)
) {
233 return f(domain.get());
234 }
235
236 if (!PySequence_Check(domain.get())) {
237 PyErr_SetString(
238 PyExc_TypeError,
239 "__ua_domain__ must be a string or sequence of strings");
240 return LoopReturn::Error;
241 }
242
243 auto size = PySequence_Size(domain.get());
244 if (size < 0)
245 return LoopReturn::Error;
246 if (size == 0) {
247 PyErr_SetString(PyExc_ValueError, "__ua_domain__ lists must be non-empty");
248 return LoopReturn::Error;
249 }
250
251 for (Py_ssize_t i = 0; i < size; ++i) {
252 auto dom = py_ref::steal(PySequence_GetItem(domain.get(), i));
253 if (!dom)
254 return LoopReturn::Error;
255
256 auto res = f(dom.get());
257 if (res != LoopReturn::Continue) {
258 return res;
259 }
260 }
261 return LoopReturn::Continue;
262}
263
264template <typename Func>
265LoopReturn backend_for_each_domain_string(PyObject * backend, Func f) {
266 return backend_for_each_domain(backend, [&](PyObject * domain) {
267 auto domain_string = domain_to_string(domain);
268 if (domain_string.empty()) {
269 return LoopReturn::Error;
270 }
271 return f(domain_string);
272 });
273}
274
275bool backend_validate_ua_domain(PyObject * backend) {
276 const auto res = backend_for_each_domain(backend, [&](PyObject * domain) {
277 return domain_validate(domain) ? LoopReturn::Continue : LoopReturn::Error;
278 });
279 return (res != LoopReturn::Error);
280}
281
282struct BackendState {
283 PyObject_HEADPyObject ob_base;
284 global_state_t globals;
285 local_state_t locals;
286 bool use_thread_local_globals = true;
287
288 static void dealloc(BackendState * self) {
289 self->~BackendState();
290 Py_TYPE(self)(((PyObject*)(self))->ob_type)->tp_free(self);
291 }
292
293 static PyObject * new_(
294 PyTypeObject * type, PyObject * args, PyObject * kwargs) {
295 auto self = reinterpret_cast<BackendState *>(type->tp_alloc(type, 0));
296 if (self == nullptr)
297 return nullptr;
298
299 // Placement new
300 self = new (self) BackendState;
301 return reinterpret_cast<PyObject *>(self);
302 }
303
304 static PyObject * pickle_(BackendState * self) {
305 try {
306 py_ref py_global = BackendState::convert_py(self->globals);
307 py_ref py_locals = BackendState::convert_py(self->locals);
308 py_ref py_use_local_globals =
309 BackendState::convert_py(self->use_thread_local_globals);
310
311 return py_make_tuple(py_global, py_locals, py_use_local_globals)
312 .release();
313 } catch (std::runtime_error &) {
314 return nullptr;
315 }
316 }
317
318 static PyObject * unpickle_(PyObject * cls, PyObject * args) {
319 try {
320 PyObject *py_locals, *py_global;
321 py_ref ref =
322 py_ref::steal(Q_PyObject_Vectorcall(cls, nullptr, 0, nullptr));
323 BackendState * output = reinterpret_cast<BackendState *>(ref.get());
324 if (output == nullptr)
325 return nullptr;
326
327 int use_thread_local_globals;
328 if (!PyArg_ParseTuple(
329 args, "OOp", &py_global, &py_locals, &use_thread_local_globals))
330 return nullptr;
331 local_state_t locals = convert_local_state(py_locals);
332 global_state_t globals = convert_global_state(py_global);
333
334 output->locals = std::move(locals);
335 output->globals = std::move(globals);
336 output->use_thread_local_globals = use_thread_local_globals;
337
338 return ref.release();
339 } catch (std::invalid_argument &) {
340 return nullptr;
341 } catch (std::bad_alloc &) {
342 PyErr_NoMemory();
343 return nullptr;
344 }
345 }
346
347 template <typename T, typename Convertor>
348 static std::vector<T> convert_iter(
349 PyObject * input, Convertor item_convertor) {
350 std::vector<T> output;
351 py_ref iterator = py_ref::steal(PyObject_GetIter(input));
4
Calling 'PyObject_GetIter'
6
Returning from 'PyObject_GetIter'
11
PyObject ownership leak with reference count of 1
352 if (!iterator)
7
Taking false branch
353 throw std::invalid_argument("");
354
355 py_ref item;
356 while ((item = py_ref::steal(PyIter_Next(iterator.get())))) {
8
Loop condition is false. Execution continues on line 360
357 output.push_back(item_convertor(item.get()));
358 }
359
360 if (PyErr_Occurred())
9
Assuming the condition is true
10
Taking true branch
361 throw std::invalid_argument("");
362
363 return output;
364 }
365
366 template <
367 typename K, typename V, typename KeyConvertor, typename ValueConvertor>
368 static std::unordered_map<K, V> convert_dict(
369 PyObject * input, KeyConvertor key_convertor,
370 ValueConvertor value_convertor) {
371 std::unordered_map<K, V> output;
372
373 if (!PyDict_Check(input)((((((PyObject*)(input))->ob_type))->tp_flags & ((1UL
<< 29))) != 0)
)
374 throw std::invalid_argument("");
375
376 PyObject *key, *value;
377 Py_ssize_t pos = 0;
378
379 while (PyDict_Next(input, &pos, &key, &value)) {
380 output[key_convertor(key)] = value_convertor(value);
381 }
382
383 if (PyErr_Occurred())
384 throw std::invalid_argument("");
385
386 return output;
387 }
388
389 static std::string convert_domain(PyObject * input) {
390 std::string output = domain_to_string(input);
391 if (output.empty())
392 throw std::invalid_argument("");
393
394 return output;
395 }
396
397 static backend_options convert_backend_options(PyObject * input) {
398 backend_options output;
399 int coerce, only;
400 PyObject * py_backend;
401 if (!PyArg_ParseTuple(input, "Opp", &py_backend, &coerce, &only))
402 throw std::invalid_argument("");
403
404 if (py_backend != Py_None(&_Py_NoneStruct)) {
405 output.backend = py_ref::ref(py_backend);
406 }
407 output.coerce = coerce;
408 output.only = only;
409
410 return output;
411 }
412
413 static py_ref convert_backend(PyObject * input) { return py_ref::ref(input); }
414
415 static local_backends convert_local_backends(PyObject * input) {
416 PyObject *py_skipped, *py_preferred;
417 if (!PyArg_ParseTuple(input, "OO", &py_skipped, &py_preferred))
418 throw std::invalid_argument("");
419
420 local_backends output;
421 output.skipped =
422 convert_iter<py_ref>(py_skipped, BackendState::convert_backend);
423 output.preferred = convert_iter<backend_options>(
424 py_preferred, BackendState::convert_backend_options);
425
426 return output;
427 }
428
429 static global_backends convert_global_backends(PyObject * input) {
430 PyObject *py_global, *py_registered;
431 int try_global_backend_last;
432 if (!PyArg_ParseTuple(
1
Assuming the condition is false
2
Taking false branch
433 input, "OOp", &py_global, &py_registered, &try_global_backend_last))
434 throw std::invalid_argument("");
435
436 global_backends output;
437 output.global = BackendState::convert_backend_options(py_global);
438 output.registered =
439 convert_iter<py_ref>(py_registered, BackendState::convert_backend);
3
Calling 'BackendState::convert_iter'
440 output.try_global_backend_last = try_global_backend_last;
441
442 return output;
443 }
444
445 static global_state_t convert_global_state(PyObject * input) {
446 return convert_dict<std::string, global_backends>(
447 input, BackendState::convert_domain,
448 BackendState::convert_global_backends);
449 }
450
451 static local_state_t convert_local_state(PyObject * input) {
452 return convert_dict<std::string, local_backends>(
453 input, BackendState::convert_domain,
454 BackendState::convert_local_backends);
455 }
456
457 static py_ref convert_py(py_ref input) { return input; }
458
459 static py_ref convert_py(bool input) { return py_bool(input); }
460
461 static py_ref convert_py(backend_options input) {
462 if (!input.backend) {
463 input.backend = py_ref::ref(Py_None(&_Py_NoneStruct));
464 }
465 py_ref output = py_make_tuple(
466 input.backend, py_bool(input.coerce), py_bool(input.only));
467 if (!output)
468 throw std::runtime_error("");
469 return output;
470 }
471
472 static py_ref convert_py(const std::string & input) {
473 py_ref output =
474 py_ref::steal(PyUnicode_FromStringAndSize(input.c_str(), input.size()));
475 if (!output)
476 throw std::runtime_error("");
477 return output;
478 }
479
480 template <typename T>
481 static py_ref convert_py(const std::vector<T> & input) {
482 py_ref output = py_ref::steal(PyList_New(input.size()));
483
484 if (!output)
485 throw std::runtime_error("");
486
487 for (size_t i = 0; i < input.size(); i++) {
488 py_ref element = convert_py(input[i]);
489 PyList_SET_ITEM(output.get(), i, element.release())PyList_SetItem(output.get(), i, element.release());
490 }
491
492 return output;
493 }
494
495 static py_ref convert_py(const local_backends & input) {
496 py_ref py_skipped = BackendState::convert_py(input.skipped);
497 py_ref py_preferred = BackendState::convert_py(input.preferred);
498 py_ref output = py_make_tuple(py_skipped, py_preferred);
499
500 if (!output)
501 throw std::runtime_error("");
502
503 return output;
504 }
505
506 static py_ref convert_py(const global_backends & input) {
507 py_ref py_globals = BackendState::convert_py(input.global);
508 py_ref py_registered = BackendState::convert_py(input.registered);
509 py_ref output = py_make_tuple(
510 py_globals, py_registered, py_bool(input.try_global_backend_last));
511
512 if (!output)
513 throw std::runtime_error("");
514
515 return output;
516 }
517
518 template <typename K, typename V>
519 static py_ref convert_py(const std::unordered_map<K, V> & input) {
520 py_ref output = py_ref::steal(PyDict_New());
521
522 if (!output)
523 throw std::runtime_error("");
524
525 for (const auto & kv : input) {
526 py_ref py_key = convert_py(kv.first);
527 py_ref py_value = convert_py(kv.second);
528
529 if (PyDict_SetItem(output.get(), py_key.get(), py_value.get()) < 0) {
530 throw std::runtime_error("");
531 }
532 }
533
534 return output;
535 }
536};
537
538/** Clean up global python references when the module is finalized. */
539void globals_free(void * /* self */) {
540 global_domain_map.clear();
541 BackendNotImplementedError.reset();
542 identifiers.clear();
543}
544
545/** Allow GC to break reference cycles between the module and global backends.
546 *
547 * NOTE: local state and "thread local globals" can't be visited because we
548 * can't access locals from another thread. However, since those are only
549 * set through context managers they should always be unset before module
550 * cleanup.
551 */
552int globals_traverse(PyObject * self, visitproc visit, void * arg) {
553 for (const auto & kv : global_domain_map) {
554 const auto & globals = kv.second;
555 PyObject * backend = globals.global.backend.get();
556 Py_VISIT(backend)do { if (backend) { int vret = visit(((PyObject*)(backend)), arg
); if (vret) return vret; } } while (0)
;
557 for (const auto & reg : globals.registered) {
558 backend = reg.get();
559 Py_VISIT(backend)do { if (backend) { int vret = visit(((PyObject*)(backend)), arg
); if (vret) return vret; } } while (0)
;
560 }
561 }
562 return 0;
563}
564
565int globals_clear(PyObject * /* self */) {
566 global_domain_map.clear();
567 return 0;
568}
569
570PyObject * set_global_backend(PyObject * /* self */, PyObject * args) {
571 PyObject * backend;
572 int only = false, coerce = false, try_last = false;
573 if (!PyArg_ParseTuple(args, "O|ppp", &backend, &coerce, &only, &try_last))
574 return nullptr;
575
576 if (!backend_validate_ua_domain(backend)) {
577 return nullptr;
578 }
579
580 const auto res =
581 backend_for_each_domain_string(backend, [&](const std::string & domain) {
582 backend_options options;
583 options.backend = py_ref::ref(backend);
584 options.coerce = coerce;
585 options.only = only;
586
587 auto & domain_globals = (*current_global_state)[domain];
588 domain_globals.global = options;
589 domain_globals.try_global_backend_last = try_last;
590 return LoopReturn::Continue;
591 });
592
593 if (res == LoopReturn::Error)
594 return nullptr;
595
596 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
597}
598
599PyObject * register_backend(PyObject * /* self */, PyObject * args) {
600 PyObject * backend;
601 if (!PyArg_ParseTuple(args, "O", &backend))
602 return nullptr;
603
604 if (!backend_validate_ua_domain(backend)) {
605 return nullptr;
606 }
607
608 const auto ret =
609 backend_for_each_domain_string(backend, [&](const std::string & domain) {
610 (*current_global_state)[domain].registered.push_back(
611 py_ref::ref(backend));
612 return LoopReturn::Continue;
613 });
614 if (ret == LoopReturn::Error)
615 return nullptr;
616
617 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
618}
619
620void clear_single(const std::string & domain, bool registered, bool global) {
621 auto domain_globals = current_global_state->find(domain);
622 if (domain_globals == current_global_state->end())
623 return;
624
625 if (registered && global) {
626 current_global_state->erase(domain_globals);
627 return;
628 }
629
630 if (registered) {
631 domain_globals->second.registered.clear();
632 }
633
634 if (global) {
635 domain_globals->second.global.backend.reset();
636 domain_globals->second.try_global_backend_last = false;
637 }
638}
639
640PyObject * clear_backends(PyObject * /* self */, PyObject * args) {
641 PyObject * domain = nullptr;
642 int registered = true, global = false;
643 if (!PyArg_ParseTuple(args, "O|pp", &domain, &registered, &global))
644 return nullptr;
645
646 if (domain == Py_None(&_Py_NoneStruct) && registered && global) {
647 current_global_state->clear();
648 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
649 }
650
651 auto domain_str = domain_to_string(domain);
652 clear_single(domain_str, registered, global);
653 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
654}
655
656/** Common functionality of set_backend and skip_backend */
657template <typename T>
658class context_helper {
659public:
660 using BackendLists = SmallDynamicArray<std::vector<T> *>;
661 // using BackendLists = std::vector<std::vector<T> *>;
662private:
663 T new_backend_;
664 BackendLists backend_lists_;
665
666public:
667 const T & get_backend() const { return new_backend_; }
668
669 context_helper() {}
670
671 bool init(BackendLists && backend_lists, T new_backend) {
672 static_assert(std::is_nothrow_move_assignable<BackendLists>::value, "");
673 backend_lists_ = std::move(backend_lists);
674 new_backend_ = std::move(new_backend);
675 return true;
676 }
677
678 bool init(std::vector<T> & backends, T new_backend) {
679 try {
680 backend_lists_ = BackendLists(1, &backends);
681 } catch (std::bad_alloc &) {
682 PyErr_NoMemory();
683 return false;
684 }
685 new_backend_ = std::move(new_backend);
686 return true;
687 }
688
689 bool enter() {
690 auto first = backend_lists_.begin();
691 auto last = backend_lists_.end();
692 auto cur = first;
693 try {
694 for (; cur < last; ++cur) {
695 (*cur)->push_back(new_backend_);
696 }
697 } catch (std::bad_alloc &) {
698 for (; first < cur; ++first) {
699 (*first)->pop_back();
700 }
701 PyErr_NoMemory();
702 return false;
703 }
704 return true;
705 }
706
707 bool exit() {
708 bool success = true;
709
710 for (auto * backends : backend_lists_) {
711 if (backends->empty()) {
712 PyErr_SetString(
713 PyExc_SystemExit, "__exit__ call has no matching __enter__");
714 success = false;
715 continue;
716 }
717
718 if (backends->back() != new_backend_) {
719 PyErr_SetString(
720 PyExc_RuntimeError,
721 "Found invalid context state while in __exit__. "
722 "__enter__ and __exit__ may be unmatched");
723 success = false;
724 }
725
726 backends->pop_back();
727 }
728
729 return success;
730 }
731};
732
733
734struct SetBackendContext {
735 PyObject_HEADPyObject ob_base;
736
737 context_helper<backend_options> ctx_;
738
739 static void dealloc(SetBackendContext * self) {
740 PyObject_GC_UnTrack(self);
741 self->~SetBackendContext();
742 Py_TYPE(self)(((PyObject*)(self))->ob_type)->tp_free(self);
743 }
744
745 static PyObject * new_(
746 PyTypeObject * type, PyObject * args, PyObject * kwargs) {
747 auto self = reinterpret_cast<SetBackendContext *>(type->tp_alloc(type, 0));
748 if (self == nullptr)
749 return nullptr;
750
751 // Placement new
752 self = new (self) SetBackendContext;
753 return reinterpret_cast<PyObject *>(self);
754 }
755
756 static int init(
757 SetBackendContext * self, PyObject * args, PyObject * kwargs) {
758 static const char * kwlist[] = {"backend", "coerce", "only", nullptr};
759 PyObject * backend = nullptr;
760 int coerce = false;
761 int only = false;
762
763 if (!PyArg_ParseTupleAndKeywords(
764 args, kwargs, "O|pp", (char **)kwlist, &backend, &coerce, &only))
765 return -1;
766
767 if (!backend_validate_ua_domain(backend)) {
768 return -1;
769 }
770
771 auto num_domains = backend_get_num_domains(backend);
772 if (num_domains < 0) {
773 return -1;
774 }
775
776 try {
777 decltype(ctx_)::BackendLists backend_lists(num_domains);
778 int idx = 0;
779
780 const auto ret = backend_for_each_domain_string(
781 backend, [&](const std::string & domain) {
782 backend_lists[idx] = &local_domain_map[domain].preferred;
783 ++idx;
784 return LoopReturn::Continue;
785 });
786
787 if (ret == LoopReturn::Error) {
788 return -1;
789 }
790
791 backend_options opt;
792 opt.backend = py_ref::ref(backend);
793 opt.coerce = coerce;
794 opt.only = only;
795
796 if (!self->ctx_.init(std::move(backend_lists), opt)) {
797 return -1;
798 }
799 } catch (std::bad_alloc &) {
800 PyErr_NoMemory();
801 return -1;
802 }
803
804 return 0;
805 }
806
807 static PyObject * enter__(SetBackendContext * self, PyObject * /* args */) {
808 if (!self->ctx_.enter())
809 return nullptr;
810 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
811 }
812
813 static PyObject * exit__(SetBackendContext * self, PyObject * /*args*/) {
814 if (!self->ctx_.exit())
815 return nullptr;
816 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
817 }
818
819 static int traverse(SetBackendContext * self, visitproc visit, void * arg) {
820 Py_VISIT(self->ctx_.get_backend().backend.get())do { if (self->ctx_.get_backend().backend.get()) { int vret
= visit(((PyObject*)(self->ctx_.get_backend().backend.get
())), arg); if (vret) return vret; } } while (0)
;
821 return 0;
822 }
823
824 static PyObject * pickle_(SetBackendContext * self, PyObject * /*args*/) {
825 const backend_options & opt = self->ctx_.get_backend();
826 return py_make_tuple(opt.backend, py_bool(opt.coerce), py_bool(opt.only))
827 .release();
828 }
829};
830
831
832struct SkipBackendContext {
833 PyObject_HEADPyObject ob_base;
834
835 context_helper<py_ref> ctx_;
836
837 static void dealloc(SkipBackendContext * self) {
838 PyObject_GC_UnTrack(self);
839 self->~SkipBackendContext();
840 Py_TYPE(self)(((PyObject*)(self))->ob_type)->tp_free(self);
841 }
842
843 static PyObject * new_(
844 PyTypeObject * type, PyObject * args, PyObject * kwargs) {
845 auto self = reinterpret_cast<SkipBackendContext *>(type->tp_alloc(type, 0));
846 if (self == nullptr)
847 return nullptr;
848
849 // Placement new
850 self = new (self) SkipBackendContext;
851 return reinterpret_cast<PyObject *>(self);
852 }
853
854 static int init(
855 SkipBackendContext * self, PyObject * args, PyObject * kwargs) {
856 static const char * kwlist[] = {"backend", nullptr};
857 PyObject * backend;
858
859 if (!PyArg_ParseTupleAndKeywords(
860 args, kwargs, "O", (char **)kwlist, &backend))
861 return -1;
862
863 if (!backend_validate_ua_domain(backend)) {
864 return -1;
865 }
866
867 auto num_domains = backend_get_num_domains(backend);
868 if (num_domains < 0) {
869 return -1;
870 }
871
872 try {
873 decltype(ctx_)::BackendLists backend_lists(num_domains);
874 int idx = 0;
875
876 const auto ret = backend_for_each_domain_string(
877 backend, [&](const std::string & domain) {
878 backend_lists[idx] = &local_domain_map[domain].skipped;
879 ++idx;
880 return LoopReturn::Continue;
881 });
882
883 if (ret == LoopReturn::Error) {
884 return -1;
885 }
886
887 if (!self->ctx_.init(std::move(backend_lists), py_ref::ref(backend))) {
888 return -1;
889 }
890 } catch (std::bad_alloc &) {
891 PyErr_NoMemory();
892 return -1;
893 }
894
895 return 0;
896 }
897
898 static PyObject * enter__(SkipBackendContext * self, PyObject * /* args */) {
899 if (!self->ctx_.enter())
900 return nullptr;
901 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
902 }
903
904 static PyObject * exit__(SkipBackendContext * self, PyObject * /*args*/) {
905 if (!self->ctx_.exit())
906 return nullptr;
907 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
908 }
909
910 static int traverse(SkipBackendContext * self, visitproc visit, void * arg) {
911 Py_VISIT(self->ctx_.get_backend().get())do { if (self->ctx_.get_backend().get()) { int vret = visit
(((PyObject*)(self->ctx_.get_backend().get())), arg); if (
vret) return vret; } } while (0)
;
912 return 0;
913 }
914
915 static PyObject * pickle_(SkipBackendContext * self, PyObject * /*args*/) {
916 return py_make_tuple(self->ctx_.get_backend()).release();
917 }
918};
919
920const local_backends & get_local_backends(const std::string & domain_key) {
921 static const local_backends null_local_backends;
922 auto itr = local_domain_map.find(domain_key);
923 if (itr == local_domain_map.end()) {
924 return null_local_backends;
925 }
926 return itr->second;
927}
928
929
930const global_backends & get_global_backends(const std::string & domain_key) {
931 static const global_backends null_global_backends;
932 const auto & cur_globals = *current_global_state;
933 auto itr = cur_globals.find(domain_key);
934 if (itr == cur_globals.end()) {
935 return null_global_backends;
936 }
937 return itr->second;
938}
939
940template <typename Callback>
941LoopReturn for_each_backend_in_domain(
942 const std::string & domain_key, Callback call) {
943 const local_backends & locals = get_local_backends(domain_key);
944
945 auto & skip = locals.skipped;
946 auto & pref = locals.preferred;
947
948 auto should_skip = [&](PyObject * backend) -> int {
949 bool success = true;
950 auto it = std::find_if(skip.begin(), skip.end(), [&](const py_ref & be) {
951 auto result = PyObject_RichCompareBool(be.get(), backend, Py_EQ2);
952 success = (result >= 0);
953 return (result != 0);
954 });
955
956 if (!success) {
957 return -1;
958 }
959
960 return (it != skip.end());
961 };
962
963 LoopReturn ret = LoopReturn::Continue;
964 for (int i = pref.size() - 1; i >= 0; --i) {
965 auto options = pref[i];
966 int skip_current = should_skip(options.backend.get());
967 if (skip_current < 0)
968 return LoopReturn::Error;
969 if (skip_current)
970 continue;
971
972 ret = call(options.backend.get(), options.coerce);
973 if (ret != LoopReturn::Continue)
974 return ret;
975
976 if (options.only || options.coerce)
977 return LoopReturn::Break;
978 }
979
980 auto & globals = get_global_backends(domain_key);
981 auto try_global_backend = [&] {
982 auto & options = globals.global;
983 if (!options.backend)
984 return LoopReturn::Continue;
985
986 int skip_current = should_skip(options.backend.get());
987 if (skip_current < 0)
988 return LoopReturn::Error;
989 if (skip_current > 0)
990 return LoopReturn::Continue;
991
992 return call(options.backend.get(), options.coerce);
993 };
994
995 if (!globals.try_global_backend_last) {
996 ret = try_global_backend();
997 if (ret != LoopReturn::Continue)
998 return ret;
999
1000 if (globals.global.only || globals.global.coerce)
1001 return LoopReturn::Break;
1002 }
1003
1004 for (size_t i = 0; i < globals.registered.size(); ++i) {
1005 py_ref backend = globals.registered[i];
1006 int skip_current = should_skip(backend.get());
1007 if (skip_current < 0)
1008 return LoopReturn::Error;
1009 if (skip_current)
1010 continue;
1011
1012 ret = call(backend.get(), false);
1013 if (ret != LoopReturn::Continue)
1014 return ret;
1015 }
1016
1017 if (!globals.try_global_backend_last) {
1018 return ret;
1019 }
1020 return try_global_backend();
1021}
1022
1023template <typename Callback>
1024LoopReturn for_each_backend(std::string domain, Callback call) {
1025 do {
1026 auto ret = for_each_backend_in_domain(domain, call);
1027 if (ret != LoopReturn::Continue) {
1028 return ret;
1029 }
1030
1031 auto dot_pos = domain.rfind('.');
1032 if (dot_pos == std::string::npos) {
1033 return ret;
1034 }
1035
1036 domain.resize(dot_pos);
1037 } while (!domain.empty());
1038 return LoopReturn::Continue;
1039}
1040
1041struct py_func_args {
1042 py_ref args, kwargs;
1043};
1044
1045struct Function {
1046 PyObject_HEADPyObject ob_base;
1047 py_ref extractor_, replacer_; // functions to handle dispatchables
1048 std::string domain_key_; // associated __ua_domain__ in UTF8
1049 py_ref def_args_, def_kwargs_; // default arguments
1050 py_ref def_impl_; // default implementation
1051 py_ref dict_; // __dict__
1052
1053 PyObject * call(PyObject * args, PyObject * kwargs);
1054
1055 py_func_args replace_dispatchables(
1056 PyObject * backend, PyObject * args, PyObject * kwargs,
1057 PyObject * coerce);
1058
1059 py_ref canonicalize_args(PyObject * args);
1060 py_ref canonicalize_kwargs(PyObject * kwargs);
1061
1062 static void dealloc(Function * self) {
1063 PyObject_GC_UnTrack(self);
1064 self->~Function();
1065 Py_TYPE(self)(((PyObject*)(self))->ob_type)->tp_free(self);
1066 }
1067
1068 static PyObject * new_(
1069 PyTypeObject * type, PyObject * args, PyObject * kwargs) {
1070 auto self = reinterpret_cast<Function *>(type->tp_alloc(type, 0));
1071 if (self == nullptr)
1072 return nullptr;
1073
1074 // Placement new
1075 self = new (self) Function;
1076 return reinterpret_cast<PyObject *>(self);
1077 }
1078
1079 static int init(Function * self, PyObject * args, PyObject * /*kwargs*/) {
1080 PyObject *extractor, *replacer;
1081 PyObject * domain;
1082 PyObject *def_args, *def_kwargs;
1083 PyObject * def_impl;
1084
1085 if (!PyArg_ParseTuple(
1086 args, "OOO!O!O!O", &extractor, &replacer, &PyUnicode_Type, &domain,
1087 &PyTuple_Type, &def_args, &PyDict_Type, &def_kwargs, &def_impl)) {
1088 return -1;
1089 }
1090
1091 if (!PyCallable_Check(extractor) ||
1092 (replacer != Py_None(&_Py_NoneStruct) && !PyCallable_Check(replacer))) {
1093 PyErr_SetString(
1094 PyExc_TypeError, "Argument extractor and replacer must be callable");
1095 return -1;
1096 }
1097
1098 if (def_impl != Py_None(&_Py_NoneStruct) && !PyCallable_Check(def_impl)) {
1099 PyErr_SetString(
1100 PyExc_TypeError, "Default implementation must be Callable or None");
1101 return -1;
1102 }
1103
1104 self->domain_key_ = domain_to_string(domain);
1105 if (PyErr_Occurred())
1106 return -1;
1107
1108 self->extractor_ = py_ref::ref(extractor);
1109 self->replacer_ = py_ref::ref(replacer);
1110 self->def_args_ = py_ref::ref(def_args);
1111 self->def_kwargs_ = py_ref::ref(def_kwargs);
1112 self->def_impl_ = py_ref::ref(def_impl);
1113
1114 return 0;
1115 }
1116
1117 static PyObject * repr(Function * self);
1118 static PyObject * descr_get(PyObject * self, PyObject * obj, PyObject * type);
1119 static int traverse(Function * self, visitproc visit, void * arg);
1120 static int clear(Function * self);
1121 static PyObject * get_extractor(Function * self);
1122 static PyObject * get_replacer(Function * self);
1123 static PyObject * get_domain(Function * self);
1124 static PyObject * get_default(Function * self);
1125};
1126
1127
1128bool is_default(PyObject * value, PyObject * def) {
1129 // TODO: richer comparison for builtin types? (if cheap)
1130 return (value == def);
1131}
1132
1133
1134py_ref Function::canonicalize_args(PyObject * args) {
1135 const auto arg_size = PyTuple_GET_SIZE(args)(((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject
*)(args))))->ob_size)
;
1136 const auto def_size = PyTuple_GET_SIZE(def_args_.get())(((PyVarObject*)(((static_cast<void> (0)), (PyTupleObject
*)(def_args_.get()))))->ob_size)
;
1137
1138 if (arg_size > def_size)
1139 return py_ref::ref(args);
1140
1141 Py_ssize_t mismatch = 0;
1142 for (Py_ssize_t i = arg_size - 1; i >= 0; --i) {
1143 auto val = PyTuple_GET_ITEM(args, i)(((static_cast<void> (0)), (PyTupleObject *)(args))->
ob_item[i])
;
1144 auto def = PyTuple_GET_ITEM(def_args_.get(), i)(((static_cast<void> (0)), (PyTupleObject *)(def_args_.
get()))->ob_item[i])
;
1145 if (!is_default(val, def)) {
1146 mismatch = i + 1;
1147 break;
1148 }
1149 }
1150
1151 return py_ref::steal(PyTuple_GetSlice(args, 0, mismatch));
1152}
1153
1154
1155py_ref Function::canonicalize_kwargs(PyObject * kwargs) {
1156 if (kwargs == nullptr)
1157 return py_ref::steal(PyDict_New());
1158
1159 PyObject *key, *def_value;
1160 Py_ssize_t pos = 0;
1161 while (PyDict_Next(def_kwargs_.get(), &pos, &key, &def_value)) {
1162 auto val = PyDict_GetItem(kwargs, key);
1163 if (val && is_default(val, def_value)) {
1164 PyDict_DelItem(kwargs, key);
1165 }
1166 }
1167 return py_ref::ref(kwargs);
1168}
1169
1170
1171py_func_args Function::replace_dispatchables(
1172 PyObject * backend, PyObject * args, PyObject * kwargs, PyObject * coerce) {
1173 auto has_ua_convert = PyObject_HasAttr(backend, identifiers.ua_convert.get());
1174 if (!has_ua_convert) {
1175 return {py_ref::ref(args), py_ref::ref(kwargs)};
1176 }
1177
1178 auto dispatchables =
1179 py_ref::steal(PyObject_Call(extractor_.get(), args, kwargs));
1180 if (!dispatchables)
1181 return {};
1182
1183 PyObject * convert_args[] = {backend, dispatchables.get(), coerce};
1184 auto res = py_ref::steal(Q_PyObject_VectorcallMethod(
1185 identifiers.ua_convert.get(), convert_args,
1186 array_size(convert_args) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET((size_t)1 << (8 * sizeof(size_t) - 1)), nullptr));
1187 if (!res) {
1188 return {};
1189 }
1190
1191 if (res == Py_NotImplemented(&_Py_NotImplementedStruct)) {
1192 return {std::move(res), nullptr};
1193 }
1194
1195 auto replaced_args = py_ref::steal(PySequence_Tuple(res.get()));
1196 if (!replaced_args)
1197 return {};
1198
1199 PyObject * replacer_args[] = {nullptr, args, kwargs, replaced_args.get()};
1200 res = py_ref::steal(Q_PyObject_Vectorcall(
1201 replacer_.get(), &replacer_args[1],
1202 (array_size(replacer_args) - 1) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET((size_t)1 << (8 * sizeof(size_t) - 1)),
1203 nullptr));
1204 if (!res)
1205 return {};
1206
1207 if (!PyTuple_Check(res.get())((((((PyObject*)(res.get()))->ob_type))->tp_flags &
((1UL << 26))) != 0)
|| PyTuple_Size(res.get()) != 2) {
1208 PyErr_SetString(
1209 PyExc_TypeError,
1210 "Argument replacer must return a 2-tuple (args, kwargs)");
1211 return {};
1212 }
1213
1214 auto new_args = py_ref::ref(PyTuple_GET_ITEM(res.get(), 0)(((static_cast<void> (0)), (PyTupleObject *)(res.get())
)->ob_item[0])
);
1215 auto new_kwargs = py_ref::ref(PyTuple_GET_ITEM(res.get(), 1)(((static_cast<void> (0)), (PyTupleObject *)(res.get())
)->ob_item[1])
);
1216
1217 new_kwargs = canonicalize_kwargs(new_kwargs.get());
1218
1219 if (!PyTuple_Check(new_args.get())((((((PyObject*)(new_args.get()))->ob_type))->tp_flags &
((1UL << 26))) != 0)
|| !PyDict_Check(new_kwargs.get())((((((PyObject*)(new_kwargs.get()))->ob_type))->tp_flags
& ((1UL << 29))) != 0)
) {
1220 PyErr_SetString(PyExc_ValueError, "Invalid return from argument_replacer");
1221 return {};
1222 }
1223
1224 return {std::move(new_args), std::move(new_kwargs)};
1225}
1226
1227
1228PyObject * Function_call(Function * self, PyObject * args, PyObject * kwargs) {
1229 return self->call(args, kwargs);
1230}
1231
1232class py_errinf {
1233 py_ref type_, value_, traceback_;
1234
1235public:
1236 static py_errinf fetch() {
1237 PyObject *type, *value, *traceback;
1238 PyErr_Fetch(&type, &value, &traceback);
1239 py_errinf err;
1240 err.set(type, value, traceback);
1241 return err;
1242 }
1243
1244 py_ref get_exception() {
1245 normalize();
1246 return value_;
1247 }
1248
1249private:
1250 void set(PyObject * type, PyObject * value, PyObject * traceback) {
1251 type_ = py_ref::steal(type);
1252 value_ = py_ref::steal(value);
1253 traceback_ = py_ref::steal(traceback);
1254 }
1255
1256 void normalize() {
1257 auto type = type_.release();
1258 auto value = value_.release();
1259 auto traceback = value_.release();
1260 PyErr_NormalizeException(&type, &value, &traceback);
1261 if (traceback) {
1262 PyException_SetTraceback(value, traceback);
1263 }
1264 set(type, value, traceback);
1265 }
1266};
1267
1268
1269PyObject * Function::call(PyObject * args_, PyObject * kwargs_) {
1270 auto args = canonicalize_args(args_);
1271 auto kwargs = canonicalize_kwargs(kwargs_);
1272
1273 py_ref result;
1274 std::vector<std::pair<py_ref, py_errinf>> errors;
1275
1276
1277 auto ret =
1278 for_each_backend(domain_key_, [&, this](PyObject * backend, bool coerce) {
1279 auto new_args = replace_dispatchables(
1280 backend, args.get(), kwargs.get(), coerce ? Py_True((PyObject *) &_Py_TrueStruct) : Py_False((PyObject *) &_Py_FalseStruct));
1281 if (new_args.args == Py_NotImplemented(&_Py_NotImplementedStruct))
1282 return LoopReturn::Continue;
1283 if (new_args.args == nullptr)
1284 return LoopReturn::Error;
1285
1286 PyObject * args[] = {backend, reinterpret_cast<PyObject *>(this),
1287 new_args.args.get(), new_args.kwargs.get()};
1288 result = py_ref::steal(Q_PyObject_VectorcallMethod(
1289 identifiers.ua_function.get(), args,
1290 array_size(args) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET((size_t)1 << (8 * sizeof(size_t) - 1)), nullptr));
1291
1292 // raise BackendNotImplemeted is equivalent to return NotImplemented
1293 if (!result &&
1294 PyErr_ExceptionMatches(BackendNotImplementedError.get())) {
1295 errors.push_back({py_ref::ref(backend), py_errinf::fetch()});
1296 result = py_ref::ref(Py_NotImplemented(&_Py_NotImplementedStruct));
1297 }
1298
1299 // Try the default with this backend
1300 if (result == Py_NotImplemented(&_Py_NotImplementedStruct) && def_impl_ != Py_None(&_Py_NoneStruct)) {
1301 backend_options opt;
1302 opt.backend = py_ref::ref(backend);
1303 opt.coerce = coerce;
1304 opt.only = true;
1305 context_helper<backend_options> ctx;
1306 try {
1307 if (!ctx.init(
1308 local_domain_map[domain_key_].preferred, std::move(opt)))
1309 return LoopReturn::Error;
1310 } catch (std::bad_alloc &) {
1311 PyErr_NoMemory();
1312 return LoopReturn::Error;
1313 }
1314
1315 if (!ctx.enter())
1316 return LoopReturn::Error;
1317
1318 result = py_ref::steal(PyObject_Call(
1319 def_impl_.get(), new_args.args.get(), new_args.kwargs.get()));
1320
1321 if (PyErr_Occurred() &&
1322 PyErr_ExceptionMatches(BackendNotImplementedError.get())) {
1323 errors.push_back({py_ref::ref(backend), py_errinf::fetch()});
1324 result = py_ref::ref(Py_NotImplemented(&_Py_NotImplementedStruct));
1325 }
1326
1327 if (!ctx.exit())
1328 return LoopReturn::Error;
1329 }
1330
1331 if (!result)
1332 return LoopReturn::Error;
1333
1334 if (result == Py_NotImplemented(&_Py_NotImplementedStruct))
1335 return LoopReturn::Continue;
1336
1337 return LoopReturn::Break; // Backend called successfully
1338 });
1339
1340 if (ret == LoopReturn::Error)
1341 return nullptr;
1342
1343 if (result && result != Py_NotImplemented(&_Py_NotImplementedStruct))
1344 return result.release();
1345
1346 // Last resort, try calling default implementation directly
1347 // Only call if no backend was marked only or coerce
1348 if (ret == LoopReturn::Continue && def_impl_ != Py_None(&_Py_NoneStruct)) {
1349 result =
1350 py_ref::steal(PyObject_Call(def_impl_.get(), args.get(), kwargs.get()));
1351 if (!result) {
1352 if (!PyErr_ExceptionMatches(BackendNotImplementedError.get()))
1353 return nullptr;
1354
1355 errors.push_back({py_ref::ref(Py_None(&_Py_NoneStruct)), py_errinf::fetch()});
1356 result = py_ref::ref(Py_NotImplemented(&_Py_NotImplementedStruct));
1357 } else if (result != Py_NotImplemented(&_Py_NotImplementedStruct))
1358 return result.release();
1359 }
1360
1361
1362 // All backends and defaults failed, construct the exception
1363 auto exception_tuple = py_ref::steal(PyTuple_New(errors.size() + 1));
1364 PyTuple_SET_ITEM(PyTuple_SetItem(exception_tuple.get(), 0, PyUnicode_FromString
( "No selected backends had an implementation for this function."
))
1365 exception_tuple.get(), 0,PyTuple_SetItem(exception_tuple.get(), 0, PyUnicode_FromString
( "No selected backends had an implementation for this function."
))
1366 PyUnicode_FromString(PyTuple_SetItem(exception_tuple.get(), 0, PyUnicode_FromString
( "No selected backends had an implementation for this function."
))
1367 "No selected backends had an implementation for this function."))PyTuple_SetItem(exception_tuple.get(), 0, PyUnicode_FromString
( "No selected backends had an implementation for this function."
))
;
1368 for (size_t i = 0; i < errors.size(); ++i) {
1369 auto pair =
1370 py_make_tuple(errors[i].first, errors[i].second.get_exception());
1371 if (!pair)
1372 return nullptr;
1373
1374 PyTuple_SET_ITEM(exception_tuple.get(), i + 1, pair.release())PyTuple_SetItem(exception_tuple.get(), i + 1, pair.release());
1375 }
1376 PyErr_SetObject(BackendNotImplementedError.get(), exception_tuple.get());
1377 return nullptr;
1378}
1379
1380
1381PyObject * Function::repr(Function * self) {
1382 if (self->dict_)
1383 if (auto name = PyDict_GetItemString(self->dict_.get(), "__name__"))
1384 return PyUnicode_FromFormat("<uarray multimethod '%S'>", name);
1385
1386 return PyUnicode_FromString("<uarray multimethod>");
1387}
1388
1389
1390/** Implements the descriptor protocol to allow binding to class instances */
1391PyObject * Function::descr_get(
1392 PyObject * self, PyObject * obj, PyObject * type) {
1393 if (!obj) {
1394 Py_INCREF(self)_Py_INCREF(((PyObject*)(self)));
1395 return self;
1396 }
1397
1398 return PyMethod_New(self, obj);
1399}
1400
1401
1402/** Make members visible to the garbage collector */
1403int Function::traverse(Function * self, visitproc visit, void * arg) {
1404 Py_VISIT(self->extractor_.get())do { if (self->extractor_.get()) { int vret = visit(((PyObject
*)(self->extractor_.get())), arg); if (vret) return vret; }
} while (0)
;
1405 Py_VISIT(self->replacer_.get())do { if (self->replacer_.get()) { int vret = visit(((PyObject
*)(self->replacer_.get())), arg); if (vret) return vret; }
} while (0)
;
1406 Py_VISIT(self->def_args_.get())do { if (self->def_args_.get()) { int vret = visit(((PyObject
*)(self->def_args_.get())), arg); if (vret) return vret; }
} while (0)
;
1407 Py_VISIT(self->def_kwargs_.get())do { if (self->def_kwargs_.get()) { int vret = visit(((PyObject
*)(self->def_kwargs_.get())), arg); if (vret) return vret;
} } while (0)
;
1408 Py_VISIT(self->def_impl_.get())do { if (self->def_impl_.get()) { int vret = visit(((PyObject
*)(self->def_impl_.get())), arg); if (vret) return vret; }
} while (0)
;
1409 Py_VISIT(self->dict_.get())do { if (self->dict_.get()) { int vret = visit(((PyObject*
)(self->dict_.get())), arg); if (vret) return vret; } } while
(0)
;
1410 return 0;
1411}
1412
1413
1414/** Break reference cycles when being GCed */
1415int Function::clear(Function * self) {
1416 self->extractor_.reset();
1417 self->replacer_.reset();
1418 self->def_args_.reset();
1419 self->def_kwargs_.reset();
1420 self->def_impl_.reset();
1421 self->dict_.reset();
1422 return 0;
1423}
1424
1425PyObject * Function::get_extractor(Function * self) {
1426 Py_INCREF(self->extractor_.get())_Py_INCREF(((PyObject*)(self->extractor_.get())));
1427 return self->extractor_.get();
1428}
1429
1430PyObject * Function::get_replacer(Function * self) {
1431 Py_INCREF(self->replacer_.get())_Py_INCREF(((PyObject*)(self->replacer_.get())));
1432 return self->replacer_.get();
1433}
1434
1435PyObject * Function::get_default(Function * self) {
1436 Py_INCREF(self->def_impl_.get())_Py_INCREF(((PyObject*)(self->def_impl_.get())));
1437 return self->def_impl_.get();
1438}
1439
1440PyObject * Function::get_domain(Function * self) {
1441 return PyUnicode_FromStringAndSize(
1442 self->domain_key_.c_str(), self->domain_key_.size());
1443}
1444
1445
1446PyMethodDef BackendState_Methods[] = {
1447 {"_pickle", (PyCFunction)BackendState::pickle_, METH_NOARGS0x0004, nullptr},
1448 {"_unpickle", (PyCFunction)BackendState::unpickle_,
1449 METH_VARARGS0x0001 | METH_CLASS0x0010, nullptr},
1450 {NULL__null} /* Sentinel */
1451};
1452
1453PyTypeObject BackendStateType = {
1454 PyVarObject_HEAD_INIT(NULL, 0){ { 1, __null }, 0 }, /* boilerplate */
1455 "uarray._BackendState", /* tp_name */
1456 sizeof(BackendState), /* tp_basicsize */
1457 0, /* tp_itemsize */
1458 (destructor)BackendState::dealloc, /* tp_dealloc */
1459 0, /* tp_print */
1460 0, /* tp_getattr */
1461 0, /* tp_setattr */
1462 0, /* tp_reserved */
1463 0, /* tp_repr */
1464 0, /* tp_as_number */
1465 0, /* tp_as_sequence */
1466 0, /* tp_as_mapping */
1467 0, /* tp_hash */
1468 0, /* tp_call */
1469 0, /* tp_str */
1470 0, /* tp_getattro */
1471 0, /* tp_setattro */
1472 0, /* tp_as_buffer */
1473 Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0), /* tp_flags */
1474 0, /* tp_doc */
1475 0, /* tp_traverse */
1476 0, /* tp_clear */
1477 0, /* tp_richcompare */
1478 0, /* tp_weaklistoffset */
1479 0, /* tp_iter */
1480 0, /* tp_iternext */
1481 BackendState_Methods, /* tp_methods */
1482 0, /* tp_members */
1483 0, /* tp_getset */
1484 0, /* tp_base */
1485 0, /* tp_dict */
1486 0, /* tp_descr_get */
1487 0, /* tp_descr_set */
1488 0, /* tp_dictoffset */
1489 0, /* tp_init */
1490 0, /* tp_alloc */
1491 BackendState::new_, /* tp_new */
1492};
1493
1494PyObject * get_state(PyObject * /* self */, PyObject * /* args */) {
1495 py_ref ref = py_ref::steal(Q_PyObject_Vectorcall(
1496 reinterpret_cast<PyObject *>(&BackendStateType), nullptr, 0, nullptr));
1497 BackendState * output = reinterpret_cast<BackendState *>(ref.get());
1498
1499 output->locals = local_domain_map;
1500 output->use_thread_local_globals =
1501 (current_global_state != &global_domain_map);
1502 output->globals = *current_global_state;
1503
1504 return ref.release();
1505}
1506
1507PyObject * set_state(PyObject * /* self */, PyObject * args) {
1508 PyObject * arg;
1509 int reset_allowed = false;
1510 if (!PyArg_ParseTuple(args, "O|p", &arg, &reset_allowed))
1511 return nullptr;
1512
1513 if (!PyObject_IsInstance(
1514 arg, reinterpret_cast<PyObject *>(&BackendStateType))) {
1515 PyErr_SetString(
1516 PyExc_TypeError, "state must be a uarray._BackendState object.");
1517 return nullptr;
1518 }
1519
1520 BackendState * state = reinterpret_cast<BackendState *>(arg);
1521 local_domain_map = state->locals;
1522 bool use_thread_local_globals =
1523 (!reset_allowed) || state->use_thread_local_globals;
1524 current_global_state =
1525 use_thread_local_globals ? &thread_local_domain_map : &global_domain_map;
1526
1527 if (use_thread_local_globals)
1528 thread_local_domain_map = state->globals;
1529 else
1530 thread_local_domain_map.clear();
1531
1532
1533 Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (&
_Py_NoneStruct)
;
1534}
1535
1536PyObject * determine_backend(PyObject * /*self*/, PyObject * args) {
1537 PyObject *domain_object, *dispatchables;
1538 int coerce;
1539 if (!PyArg_ParseTuple(
1540 args, "OOp:determine_backend", &domain_object, &dispatchables,
1541 &coerce))
1542 return nullptr;
1543
1544 auto domain = domain_to_string(domain_object);
1545 if (domain.empty())
1546 return nullptr;
1547
1548 auto dispatchables_tuple = py_ref::steal(PySequence_Tuple(dispatchables));
1549 if (!dispatchables_tuple)
1550 return nullptr;
1551
1552 py_ref selected_backend;
1553 auto result = for_each_backend_in_domain(
1554 domain, [&](PyObject * backend, bool coerce_backend) {
1555 auto has_ua_convert =
1556 PyObject_HasAttr(backend, identifiers.ua_convert.get());
1557
1558 if (!has_ua_convert) {
1559 // If no __ua_convert__, assume it won't accept the type
1560 return LoopReturn::Continue;
1561 }
1562
1563 PyObject * convert_args[] = {backend, dispatchables_tuple.get(),
1564 (coerce && coerce_backend) ? Py_True((PyObject *) &_Py_TrueStruct)
1565 : Py_False((PyObject *) &_Py_FalseStruct)};
1566
1567 auto res = py_ref::steal(Q_PyObject_VectorcallMethod(
1568 identifiers.ua_convert.get(), convert_args,
1569 array_size(convert_args) | Q_PY_VECTORCALL_ARGUMENTS_OFFSET((size_t)1 << (8 * sizeof(size_t) - 1)),
1570 nullptr));
1571 if (!res) {
1572 return LoopReturn::Error;
1573 }
1574
1575 if (res == Py_NotImplemented(&_Py_NotImplementedStruct)) {
1576 return LoopReturn::Continue;
1577 }
1578
1579 // __ua_convert__ succeeded, so select this backend
1580 selected_backend = py_ref::ref(backend);
1581 return LoopReturn::Break;
1582 });
1583
1584 if (result != LoopReturn::Continue)
1585 return selected_backend.release();
1586
1587 // All backends failed, raise an error
1588 PyErr_SetString(
1589 BackendNotImplementedError.get(),
1590 "No backends could accept input of this type.");
1591 return nullptr;
1592}
1593
1594
1595// getset takes mutable char * in python < 3.7
1596static char dict__[] = "__dict__";
1597static char arg_extractor[] = "arg_extractor";
1598static char arg_replacer[] = "arg_replacer";
1599static char default_[] = "default";
1600static char domain[] = "domain";
1601PyGetSetDef Function_getset[] = {
1602 {dict__, PyObject_GenericGetDict, PyObject_GenericSetDict},
1603 {arg_extractor, (getter)Function::get_extractor, NULL__null},
1604 {arg_replacer, (getter)Function::get_replacer, NULL__null},
1605 {default_, (getter)Function::get_default, NULL__null},
1606 {domain, (getter)Function::get_domain, NULL__null},
1607 {NULL__null} /* Sentinel */
1608};
1609
1610PyTypeObject FunctionType = {
1611 PyVarObject_HEAD_INIT(NULL, 0){ { 1, __null }, 0 }, /* boilerplate */
1612 /* tp_name= */ "uarray._Function",
1613 /* tp_basicsize= */ sizeof(Function),
1614 /* tp_itemsize= */ 0,
1615 /* tp_dealloc= */ (destructor)Function::dealloc,
1616 /* tp_print= */ 0,
1617 /* tp_getattr= */ 0,
1618 /* tp_setattr= */ 0,
1619 /* tp_reserved= */ 0,
1620 /* tp_repr= */ (reprfunc)Function::repr,
1621 /* tp_as_number= */ 0,
1622 /* tp_as_sequence= */ 0,
1623 /* tp_as_mapping= */ 0,
1624 /* tp_hash= */ 0,
1625 /* tp_call= */ (ternaryfunc)Function_call,
1626 /* tp_str= */ 0,
1627 /* tp_getattro= */ PyObject_GenericGetAttr,
1628 /* tp_setattro= */ PyObject_GenericSetAttr,
1629 /* tp_as_buffer= */ 0,
1630 /* tp_flags= */
1631 (Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0) | Py_TPFLAGS_HAVE_GC(1UL << 14) | Q_Py_TPFLAGS_METHOD_DESCRIPTOR(1UL << 17)),
1632 /* tp_doc= */ 0,
1633 /* tp_traverse= */ (traverseproc)Function::traverse,
1634 /* tp_clear= */ (inquiry)Function::clear,
1635 /* tp_richcompare= */ 0,
1636 /* tp_weaklistoffset= */ 0,
1637 /* tp_iter= */ 0,
1638 /* tp_iternext= */ 0,
1639 /* tp_methods= */ 0,
1640 /* tp_members= */ 0,
1641 /* tp_getset= */ Function_getset,
1642 /* tp_base= */ 0,
1643 /* tp_dict= */ 0,
1644 /* tp_descr_get= */ Function::descr_get,
1645 /* tp_descr_set= */ 0,
1646 /* tp_dictoffset= */ offsetof(Function, dict_)__builtin_offsetof(Function, dict_),
1647 /* tp_init= */ (initproc)Function::init,
1648 /* tp_alloc= */ 0,
1649 /* tp_new= */ Function::new_,
1650};
1651
1652
1653PyMethodDef SetBackendContext_Methods[] = {
1654 {"__enter__", (PyCFunction)SetBackendContext::enter__, METH_NOARGS0x0004,
1655 nullptr},
1656 {"__exit__", (PyCFunction)SetBackendContext::exit__, METH_VARARGS0x0001, nullptr},
1657 {"_pickle", (PyCFunction)SetBackendContext::pickle_, METH_NOARGS0x0004, nullptr},
1658 {NULL__null} /* Sentinel */
1659};
1660
1661PyTypeObject SetBackendContextType = {
1662 PyVarObject_HEAD_INIT(NULL, 0){ { 1, __null }, 0 }, /* boilerplate */
1663 "uarray._SetBackendContext", /* tp_name */
1664 sizeof(SetBackendContext), /* tp_basicsize */
1665 0, /* tp_itemsize */
1666 (destructor)SetBackendContext::dealloc, /* tp_dealloc */
1667 0, /* tp_print */
1668 0, /* tp_getattr */
1669 0, /* tp_setattr */
1670 0, /* tp_reserved */
1671 0, /* tp_repr */
1672 0, /* tp_as_number */
1673 0, /* tp_as_sequence */
1674 0, /* tp_as_mapping */
1675 0, /* tp_hash */
1676 0, /* tp_call */
1677 0, /* tp_str */
1678 0, /* tp_getattro */
1679 0, /* tp_setattro */
1680 0, /* tp_as_buffer */
1681 (Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0) | Py_TPFLAGS_HAVE_GC(1UL << 14)), /* tp_flags */
1682 0, /* tp_doc */
1683 (traverseproc)SetBackendContext::traverse, /* tp_traverse */
1684 0, /* tp_clear */
1685 0, /* tp_richcompare */
1686 0, /* tp_weaklistoffset */
1687 0, /* tp_iter */
1688 0, /* tp_iternext */
1689 SetBackendContext_Methods, /* tp_methods */
1690 0, /* tp_members */
1691 0, /* tp_getset */
1692 0, /* tp_base */
1693 0, /* tp_dict */
1694 0, /* tp_descr_get */
1695 0, /* tp_descr_set */
1696 0, /* tp_dictoffset */
1697 (initproc)SetBackendContext::init, /* tp_init */
1698 0, /* tp_alloc */
1699 SetBackendContext::new_, /* tp_new */
1700};
1701
1702
1703PyMethodDef SkipBackendContext_Methods[] = {
1704 {"__enter__", (PyCFunction)SkipBackendContext::enter__, METH_NOARGS0x0004,
1705 nullptr},
1706 {"__exit__", (PyCFunction)SkipBackendContext::exit__, METH_VARARGS0x0001,
1707 nullptr},
1708 {"_pickle", (PyCFunction)SkipBackendContext::pickle_, METH_NOARGS0x0004, nullptr},
1709 {NULL__null} /* Sentinel */
1710};
1711
1712PyTypeObject SkipBackendContextType = {
1713 PyVarObject_HEAD_INIT(NULL, 0){ { 1, __null }, 0 }, /* boilerplate */
1714 "uarray._SkipBackendContext", /* tp_name */
1715 sizeof(SkipBackendContext), /* tp_basicsize */
1716 0, /* tp_itemsize */
1717 (destructor)SkipBackendContext::dealloc, /* tp_dealloc */
1718 0, /* tp_print */
1719 0, /* tp_getattr */
1720 0, /* tp_setattr */
1721 0, /* tp_reserved */
1722 0, /* tp_repr */
1723 0, /* tp_as_number */
1724 0, /* tp_as_sequence */
1725 0, /* tp_as_mapping */
1726 0, /* tp_hash */
1727 0, /* tp_call */
1728 0, /* tp_str */
1729 0, /* tp_getattro */
1730 0, /* tp_setattro */
1731 0, /* tp_as_buffer */
1732 (Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0) | Py_TPFLAGS_HAVE_GC(1UL << 14)), /* tp_flags */
1733 0, /* tp_doc */
1734 (traverseproc)SkipBackendContext::traverse, /* tp_traverse */
1735 0, /* tp_clear */
1736 0, /* tp_richcompare */
1737 0, /* tp_weaklistoffset */
1738 0, /* tp_iter */
1739 0, /* tp_iternext */
1740 SkipBackendContext_Methods, /* tp_methods */
1741 0, /* tp_members */
1742 0, /* tp_getset */
1743 0, /* tp_base */
1744 0, /* tp_dict */
1745 0, /* tp_descr_get */
1746 0, /* tp_descr_set */
1747 0, /* tp_dictoffset */
1748 (initproc)SkipBackendContext::init, /* tp_init */
1749 0, /* tp_alloc */
1750 SkipBackendContext::new_, /* tp_new */
1751};
1752
1753
1754PyMethodDef method_defs[] = {
1755 {"set_global_backend", set_global_backend, METH_VARARGS0x0001, nullptr},
1756 {"register_backend", register_backend, METH_VARARGS0x0001, nullptr},
1757 {"clear_backends", clear_backends, METH_VARARGS0x0001, nullptr},
1758 {"determine_backend", determine_backend, METH_VARARGS0x0001, nullptr},
1759 {"get_state", get_state, METH_NOARGS0x0004, nullptr},
1760 {"set_state", set_state, METH_VARARGS0x0001, nullptr},
1761 {NULL__null} /* Sentinel */
1762};
1763
1764PyModuleDef uarray_module = {PyModuleDef_HEAD_INIT{ { 1, __null }, __null, 0, __null, },
1765 /* m_name= */ "uarray._uarray",
1766 /* m_doc= */ nullptr,
1767 /* m_size= */ -1,
1768 /* m_methods= */ method_defs,
1769 /* m_slots= */ nullptr,
1770 /* m_traverse= */ globals_traverse,
1771 /* m_clear= */ globals_clear,
1772 /* m_free= */ globals_free};
1773
1774} // namespace
1775
1776#if defined(WIN32) || defined(_WIN32)
1777# define MODULE_EXPORT__attribute__((visibility("default"))) __declspec(dllexport)
1778#else
1779# define MODULE_EXPORT__attribute__((visibility("default"))) __attribute__((visibility("default")))
1780#endif
1781
1782extern "C" MODULE_EXPORT__attribute__((visibility("default"))) PyObject * PyInit__uarray(void) {
1783
1784 auto m = py_ref::steal(PyModule_Create(&uarray_module)PyModule_Create2(&uarray_module, 1013));
1785 if (!m)
1786 return nullptr;
1787
1788 if (PyType_Ready(&FunctionType) < 0)
1789 return nullptr;
1790 Py_INCREF(&FunctionType)_Py_INCREF(((PyObject*)(&FunctionType)));
1791 PyModule_AddObject(m.get(), "_Function", (PyObject *)&FunctionType);
1792
1793 if (PyType_Ready(&SetBackendContextType) < 0)
1794 return nullptr;
1795 Py_INCREF(&SetBackendContextType)_Py_INCREF(((PyObject*)(&SetBackendContextType)));
1796 PyModule_AddObject(
1797 m.get(), "_SetBackendContext", (PyObject *)&SetBackendContextType);
1798
1799 if (PyType_Ready(&SkipBackendContextType) < 0)
1800 return nullptr;
1801 Py_INCREF(&SkipBackendContextType)_Py_INCREF(((PyObject*)(&SkipBackendContextType)));
1802 PyModule_AddObject(
1803 m.get(), "_SkipBackendContext", (PyObject *)&SkipBackendContextType);
1804
1805 if (PyType_Ready(&BackendStateType) < 0)
1806 return nullptr;
1807 Py_INCREF(&BackendStateType)_Py_INCREF(((PyObject*)(&BackendStateType)));
1808 PyModule_AddObject(m.get(), "_BackendState", (PyObject *)&BackendStateType);
1809
1810 BackendNotImplementedError = py_ref::steal(PyErr_NewExceptionWithDoc(
1811 "uarray.BackendNotImplementedError",
1812 "An exception that is thrown when no compatible"
1813 " backend is found for a method.",
1814 PyExc_NotImplementedError, nullptr));
1815 if (!BackendNotImplementedError)
1816 return nullptr;
1817 Py_INCREF(BackendNotImplementedError.get())_Py_INCREF(((PyObject*)(BackendNotImplementedError.get())));
1818 PyModule_AddObject(
1819 m.get(), "BackendNotImplementedError", BackendNotImplementedError.get());
1820
1821 if (!identifiers.init())
1822 return nullptr;
1823
1824 return m.release();
1825}

/opt/pyrefcon/lib/pyrefcon/models/models/PyObject_GetIter.model

1#ifndef PyObject_GetIter
2struct _object;
3typedef struct _object PyObject;
4PyObject* clang_analyzer_PyObject_New_Reference();
5PyObject* PyObject_GetIter(PyObject *o) {
6 return clang_analyzer_PyObject_New_Reference();
5
Setting reference count to 1
7}
8#else
9#warning "API PyObject_GetIter is defined as a macro."
10#endif