File: | /tmp/pyrefcon/scipy/scipy/_lib/_uarray/_uarray_dispatch.cxx |
Warning: | line 91, column 24 PyObject ownership leak with reference count of 1 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
16 | namespace { | |||
17 | ||||
18 | /** Handle to a python object that automatically DECREFs */ | |||
19 | class py_ref { | |||
20 | explicit py_ref(PyObject * object): obj_(object) {} | |||
21 | ||||
22 | public: | |||
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 | ||||
81 | private: | |||
82 | PyObject * obj_; | |||
83 | }; | |||
84 | ||||
85 | PyObject * py_get(const py_ref & ref) { return ref.get(); } | |||
86 | PyObject * py_get(PyObject * obj) { return obj; } | |||
87 | ||||
88 | /** Make tuple from variadic set of PyObjects */ | |||
89 | template <typename... Ts> | |||
90 | py_ref py_make_tuple(const Ts &... args) { | |||
91 | return py_ref::steal(PyTuple_Pack(sizeof...(args), py_get(args)...)); | |||
| ||||
92 | } | |||
93 | ||||
94 | py_ref py_bool(bool input) { return py_ref::ref(input ? Py_True((PyObject *) &_Py_TrueStruct) : Py_False((PyObject *) &_Py_FalseStruct)); } | |||
95 | ||||
96 | template <typename T, size_t N> | |||
97 | constexpr size_t array_size(const T (&array)[N]) { | |||
98 | return N; | |||
99 | } | |||
100 | ||||
101 | struct 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 | ||||
117 | struct global_backends { | |||
118 | backend_options global; | |||
119 | std::vector<py_ref> registered; | |||
120 | bool try_global_backend_last = false; | |||
121 | }; | |||
122 | ||||
123 | struct local_backends { | |||
124 | std::vector<py_ref> skipped; | |||
125 | std::vector<backend_options> preferred; | |||
126 | }; | |||
127 | ||||
128 | using global_state_t = std::unordered_map<std::string, global_backends>; | |||
129 | using local_state_t = std::unordered_map<std::string, local_backends>; | |||
130 | ||||
131 | static py_ref BackendNotImplementedError; | |||
132 | static global_state_t global_domain_map; | |||
133 | thread_local global_state_t * current_global_state = &global_domain_map; | |||
134 | thread_local global_state_t thread_local_domain_map; | |||
135 | thread_local local_state_t local_domain_map; | |||
136 | ||||
137 | /** Constant Python string identifiers | |||
138 | ||||
139 | Using these with PyObject_GetAttr is faster than PyObject_GetAttrString which | |||
140 | has to create a new python string internally. | |||
141 | */ | |||
142 | struct { | |||
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 | ||||
170 | bool 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 | ||||
185 | std::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 | ||||
203 | Py_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 | ||||
223 | enum class LoopReturn { Continue, Break, Error }; | |||
224 | ||||
225 | template <typename Func> | |||
226 | LoopReturn 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 | ||||
264 | template <typename Func> | |||
265 | LoopReturn 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 | ||||
275 | bool 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 | ||||
282 | struct 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)); | |||
352 | if (!iterator) | |||
353 | throw std::invalid_argument(""); | |||
354 | ||||
355 | py_ref item; | |||
356 | while ((item = py_ref::steal(PyIter_Next(iterator.get())))) { | |||
357 | output.push_back(item_convertor(item.get())); | |||
358 | } | |||
359 | ||||
360 | if (PyErr_Occurred()) | |||
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( | |||
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); | |||
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. */ | |||
539 | void 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 | */ | |||
552 | int 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 | ||||
565 | int globals_clear(PyObject * /* self */) { | |||
566 | global_domain_map.clear(); | |||
567 | return 0; | |||
568 | } | |||
569 | ||||
570 | PyObject * 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 | ||||
599 | PyObject * 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 | ||||
620 | void 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 | ||||
640 | PyObject * clear_backends(PyObject * /* self */, PyObject * args) { | |||
641 | PyObject * domain = nullptr; | |||
642 | int registered = true, global = false; | |||
643 | if (!PyArg_ParseTuple(args, "O|pp", &domain, ®istered, &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 */ | |||
657 | template <typename T> | |||
658 | class context_helper { | |||
659 | public: | |||
660 | using BackendLists = SmallDynamicArray<std::vector<T> *>; | |||
661 | // using BackendLists = std::vector<std::vector<T> *>; | |||
662 | private: | |||
663 | T new_backend_; | |||
664 | BackendLists backend_lists_; | |||
665 | ||||
666 | public: | |||
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 | ||||
734 | struct 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 | ||||
832 | struct 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 | ||||
920 | const 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 | ||||
930 | const 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 | ||||
940 | template <typename Callback> | |||
941 | LoopReturn 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 | ||||
1023 | template <typename Callback> | |||
1024 | LoopReturn 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 | ||||
1041 | struct py_func_args { | |||
1042 | py_ref args, kwargs; | |||
1043 | }; | |||
1044 | ||||
1045 | struct 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 | ||||
1128 | bool is_default(PyObject * value, PyObject * def) { | |||
1129 | // TODO: richer comparison for builtin types? (if cheap) | |||
1130 | return (value == def); | |||
1131 | } | |||
1132 | ||||
1133 | ||||
1134 | py_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 | ||||
1155 | py_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 | ||||
1171 | py_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 | ||||
1228 | PyObject * Function_call(Function * self, PyObject * args, PyObject * kwargs) { | |||
1229 | return self->call(args, kwargs); | |||
1230 | } | |||
1231 | ||||
1232 | class py_errinf { | |||
1233 | py_ref type_, value_, traceback_; | |||
1234 | ||||
1235 | public: | |||
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 | ||||
1249 | private: | |||
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 | ||||
1269 | PyObject * 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 | ||||
1381 | PyObject * 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 */ | |||
1391 | PyObject * 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 */ | |||
1403 | int 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 */ | |||
1415 | int 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 | ||||
1425 | PyObject * Function::get_extractor(Function * self) { | |||
1426 | Py_INCREF(self->extractor_.get())_Py_INCREF(((PyObject*)(self->extractor_.get()))); | |||
1427 | return self->extractor_.get(); | |||
1428 | } | |||
1429 | ||||
1430 | PyObject * Function::get_replacer(Function * self) { | |||
1431 | Py_INCREF(self->replacer_.get())_Py_INCREF(((PyObject*)(self->replacer_.get()))); | |||
1432 | return self->replacer_.get(); | |||
1433 | } | |||
1434 | ||||
1435 | PyObject * 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 | ||||
1440 | PyObject * Function::get_domain(Function * self) { | |||
1441 | return PyUnicode_FromStringAndSize( | |||
1442 | self->domain_key_.c_str(), self->domain_key_.size()); | |||
1443 | } | |||
1444 | ||||
1445 | ||||
1446 | PyMethodDef 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 | ||||
1453 | PyTypeObject 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 | ||||
1494 | PyObject * 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 | ||||
1507 | PyObject * 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 | ||||
1536 | PyObject * 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 | |||
1596 | static char dict__[] = "__dict__"; | |||
1597 | static char arg_extractor[] = "arg_extractor"; | |||
1598 | static char arg_replacer[] = "arg_replacer"; | |||
1599 | static char default_[] = "default"; | |||
1600 | static char domain[] = "domain"; | |||
1601 | PyGetSetDef 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 | ||||
1610 | PyTypeObject 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 | ||||
1653 | PyMethodDef 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 | ||||
1661 | PyTypeObject 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 | ||||
1703 | PyMethodDef 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 | ||||
1712 | PyTypeObject 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 | ||||
1754 | PyMethodDef 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 | ||||
1764 | PyModuleDef 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 | ||||
1782 | extern "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 | } |
1 | #ifndef PyTuple_Pack |
2 | struct _object; |
3 | typedef struct _object PyObject; |
4 | PyObject* clang_analyzer_PyObject_New_Reference(); |
5 | PyObject* PyTuple_Pack(Py_ssize_t n, ...) { |
6 | return clang_analyzer_PyObject_New_Reference(); |
7 | } |
8 | #else |
9 | #warning "API PyTuple_Pack is defined as a macro." |
10 | #endif |