| File: | /tmp/pyrefcon/scipy/scipy/_lib/_uarray/_uarray_dispatch.cxx | 
| Warning: | line 474, column 23 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 PyUnicode_FromStringAndSize | 
| 2 | struct _object; | 
| 3 | typedef struct _object PyObject; | 
| 4 | PyObject* clang_analyzer_PyObject_New_Reference(); | 
| 5 | PyObject* PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) { | 
| 6 | return clang_analyzer_PyObject_New_Reference(); | 
| 7 | } | 
| 8 | #else | 
| 9 | #warning "API PyUnicode_FromStringAndSize is defined as a macro." | 
| 10 | #endif |