| | 1 | /* |
| | 2 | * A Deferred implementation in C. Cover most of the Deferred API but try |
| | 3 | * to be faster. |
| | 4 | * |
| | 5 | * TODO: Review failure handling - is everything decref'd properly? |
| | 6 | * TODO: Try to replace IsInstance calls with a fast ptr comparison? |
| | 7 | * |
| | 8 | */ |
| | 9 | |
| | 10 | #include <Python.h> |
| | 11 | #include "structmember.h" |
| | 12 | |
| | 13 | /* Py_VISIT and Py_CLEAR are defined here to be compatible with Python 2.3 */ |
| | 14 | |
| | 15 | #ifndef Py_VISIT |
| | 16 | #define Py_VISIT(op) \ |
| | 17 | do { \ |
| | 18 | if (op) { \ |
| | 19 | int vret = visit((PyObject *)(op), arg); \ |
| | 20 | if (vret) \ |
| | 21 | return vret; \ |
| | 22 | } \ |
| | 23 | } while (0) |
| | 24 | #endif |
| | 25 | |
| | 26 | #ifndef Py_CLEAR |
| | 27 | #define Py_CLEAR(op) \ |
| | 28 | do { \ |
| | 29 | if (op) { \ |
| | 30 | PyObject *tmp = (PyObject *)(op); \ |
| | 31 | (op) = NULL; \ |
| | 32 | Py_DECREF(tmp); \ |
| | 33 | } \ |
| | 34 | } while (0) |
| | 35 | #endif |
| | 36 | |
| | 37 | PyObject * failure_class = NULL; |
| | 38 | PyObject * already_called = NULL; |
| | 39 | PyObject * debuginfo_class = NULL; |
| | 40 | |
| | 41 | typedef struct { |
| | 42 | PyObject_HEAD |
| | 43 | PyObject *result; |
| | 44 | int paused; |
| | 45 | PyObject *callbacks; |
| | 46 | PyObject *debuginfo; |
| | 47 | int called; |
| | 48 | /* Current callback index in the callbacks list to run. This |
| | 49 | * allows clearing the list once per runCallbacks rather than |
| | 50 | * popping every item. It has to be per-deferred, because |
| | 51 | * runCallbacks can be called reentrantly, and must not repeat the |
| | 52 | * previously-called callbacks. */ |
| | 53 | Py_ssize_t callback_index; |
| | 54 | } cdefer_Deferred; |
| | 55 | |
| | 56 | /* Prototypes */ |
| | 57 | |
| | 58 | static PyObject * cdefer_Deferred_new(PyTypeObject *type, PyObject *args, |
| | 59 | PyObject *kwargs); |
| | 60 | |
| | 61 | static void cdefer_Deferred_dealloc(PyObject *o); |
| | 62 | |
| | 63 | static int cdefer_Deferred_traverse(PyObject *o, visitproc visit, void *arg); |
| | 64 | |
| | 65 | static int cdefer_Deferred_clear(PyObject *o); |
| | 66 | |
| | 67 | static int cdefer_Deferred_clear(PyObject *o); |
| | 68 | |
| | 69 | static int cdefer_Deferred___init__(cdefer_Deferred *self, PyObject *args, |
| | 70 | PyObject *kwargs); |
| | 71 | |
| | 72 | static PyObject *cdefer_Deferred__addCallbacks(cdefer_Deferred *self, |
| | 73 | PyObject *callback, PyObject *errback, PyObject *callbackArgs, |
| | 74 | PyObject *callbackKeywords, PyObject *errbackArgs, |
| | 75 | PyObject *errbackKeywords); |
| | 76 | |
| | 77 | static PyObject *cdefer_Deferred_addCallback(cdefer_Deferred *self, |
| | 78 | PyObject *args, PyObject *kwargs); |
| | 79 | |
| | 80 | static PyObject *cdefer_Deferred_addErrback(cdefer_Deferred *self, |
| | 81 | PyObject *args, PyObject *kwargs); |
| | 82 | |
| | 83 | static PyObject *cdefer_Deferred_addBoth(cdefer_Deferred *self, PyObject *args, |
| | 84 | PyObject *kwargs); |
| | 85 | |
| | 86 | static PyObject *cdefer_Deferred_pause(cdefer_Deferred *self, PyObject *args); |
| | 87 | |
| | 88 | static PyObject *cdefer_Deferred_unpause(cdefer_Deferred *self, |
| | 89 | PyObject *args); |
| | 90 | |
| | 91 | static PyObject *cdefer_Deferred_chainDeferred(cdefer_Deferred *self, |
| | 92 | PyObject *args, PyObject *kwargs); |
| | 93 | |
| | 94 | static PyObject *cdefer_Deferred__runCallbacks(cdefer_Deferred *self); |
| | 95 | |
| | 96 | static PyObject *cdefer_Deferred__startRunCallbacks(cdefer_Deferred *self, |
| | 97 | PyObject *result); |
| | 98 | |
| | 99 | static PyObject *cdefer_Deferred_callback(cdefer_Deferred *self, PyObject *args, |
| | 100 | PyObject *kwargs); |
| | 101 | |
| | 102 | static PyObject *cdefer_Deferred_errback(cdefer_Deferred *self, PyObject *args, |
| | 103 | PyObject *kwargs); |
| | 104 | |
| | 105 | static PyObject *cdefer_Deferred__continue(cdefer_Deferred *self, |
| | 106 | PyObject *args, PyObject *kwargs); |
| | 107 | |
| | 108 | static PyTypeObject cdefer_DeferredType; |
| | 109 | |
| | 110 | static PyObject * cdefer_Deferred_new(PyTypeObject *type, PyObject *args, |
| | 111 | PyObject *kwargs) { |
| | 112 | cdefer_Deferred *self; |
| | 113 | self = (cdefer_Deferred *)type->tp_alloc(type, 0); |
| | 114 | return (PyObject *)self; |
| | 115 | } |
| | 116 | |
| | 117 | static void cdefer_Deferred_dealloc(PyObject *o) { |
| | 118 | cdefer_Deferred *self; |
| | 119 | self = (cdefer_Deferred *)o; |
| | 120 | PyObject_GC_UnTrack(self); |
| | 121 | Py_XDECREF(self->result); |
| | 122 | Py_XDECREF(self->debuginfo); |
| | 123 | Py_XDECREF(self->callbacks); |
| | 124 | (*o->ob_type->tp_free)(o); |
| | 125 | } |
| | 126 | |
| | 127 | static int cdefer_Deferred_traverse(PyObject *o, visitproc visit, void *arg) { |
| | 128 | cdefer_Deferred *self; |
| | 129 | self = (cdefer_Deferred *)o; |
| | 130 | Py_VISIT(self->result); |
| | 131 | Py_VISIT(self->debuginfo); |
| | 132 | Py_VISIT(self->callbacks); |
| | 133 | return 0; |
| | 134 | } |
| | 135 | |
| | 136 | static int cdefer_Deferred_clear(PyObject *o) { |
| | 137 | cdefer_Deferred *self; |
| | 138 | self = (cdefer_Deferred *)o; |
| | 139 | Py_CLEAR(self->result); |
| | 140 | Py_CLEAR(self->debuginfo); |
| | 141 | Py_CLEAR(self->callbacks); |
| | 142 | return 0; |
| | 143 | } |
| | 144 | |
| | 145 | static int cdefer_Deferred___init__(cdefer_Deferred *self, PyObject *args, |
| | 146 | PyObject *kwargs) { |
| | 147 | static char *argnames[] = {NULL}; |
| | 148 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", argnames)) { |
| | 149 | return -1; |
| | 150 | } |
| | 151 | self->paused = 0; |
| | 152 | self->callback_index = 0; |
| | 153 | self->callbacks = PyList_New(0); |
| | 154 | if (!self->callbacks) { |
| | 155 | return -1; |
| | 156 | } |
| | 157 | return 0; |
| | 158 | } |
| | 159 | |
| | 160 | static PyObject *cdefer_Deferred__addCallbacks(cdefer_Deferred *self, |
| | 161 | PyObject *callback, PyObject *errback, PyObject *callbackArgs, |
| | 162 | PyObject *callbackKeywords, PyObject *errbackArgs, |
| | 163 | PyObject *errbackKeywords) { |
| | 164 | PyObject *result; |
| | 165 | PyObject *cbs = 0; |
| | 166 | int rc; |
| | 167 | |
| | 168 | if (callback != Py_None) { |
| | 169 | if (!PyCallable_Check(callback)) { |
| | 170 | PyErr_SetNone(PyExc_AssertionError); |
| | 171 | return NULL; |
| | 172 | } |
| | 173 | } |
| | 174 | if (errback != Py_None) { |
| | 175 | if (!PyCallable_Check(errback)) { |
| | 176 | PyErr_SetNone(PyExc_AssertionError); |
| | 177 | return NULL; |
| | 178 | } |
| | 179 | } |
| | 180 | |
| | 181 | cbs = Py_BuildValue("(OOOOOO)", callback, callbackArgs, callbackKeywords, |
| | 182 | errback, errbackArgs, errbackKeywords); |
| | 183 | if (!cbs) { |
| | 184 | return NULL; |
| | 185 | } |
| | 186 | |
| | 187 | rc = PyList_Append(self->callbacks, cbs); |
| | 188 | Py_CLEAR(cbs); |
| | 189 | if (rc == -1) { |
| | 190 | return NULL; |
| | 191 | } |
| | 192 | |
| | 193 | if (self->called) { |
| | 194 | if (cdefer_Deferred__runCallbacks(self) == NULL) { |
| | 195 | return NULL; |
| | 196 | } |
| | 197 | } |
| | 198 | |
| | 199 | result = (PyObject *)self; |
| | 200 | Py_INCREF(result); |
| | 201 | return result; |
| | 202 | } |
| | 203 | |
| | 204 | static char cdefer_Deferred_addCallbacks_doc[] = "Add a pair of callbacks (success and error) to this Deferred.\n\nThese will be executed when the \'master\' callback is run."; |
| | 205 | |
| | 206 | static PyObject *cdefer_Deferred_addCallbacks(cdefer_Deferred *self, |
| | 207 | PyObject *args, PyObject *kwargs) { |
| | 208 | static char *argnames[] = {"callback", "errback", "callbackArgs", |
| | 209 | "callbackKeywords", "errbackArgs", "errbackKeywords", NULL}; |
| | 210 | PyObject *callback; |
| | 211 | PyObject *errback = Py_None; |
| | 212 | PyObject *callbackArgs = Py_None; |
| | 213 | PyObject *callbackKeywords = Py_None; |
| | 214 | PyObject *errbackArgs = Py_None; |
| | 215 | PyObject *errbackKeywords = Py_None; |
| | 216 | PyObject *result; |
| | 217 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOO", argnames, |
| | 218 | &callback, &errback, &callbackArgs, |
| | 219 | &callbackKeywords, &errbackArgs, &errbackKeywords)) { |
| | 220 | return NULL; |
| | 221 | } |
| | 222 | result = cdefer_Deferred__addCallbacks(self, callback, errback, |
| | 223 | callbackArgs, callbackKeywords, errbackArgs, errbackKeywords); |
| | 224 | return result; |
| | 225 | } |
| | 226 | |
| | 227 | static char cdefer_Deferred_addCallback_doc[] = "Convenience method for adding just a callback.\n\nSee L{addCallbacks}."; |
| | 228 | |
| | 229 | /* Returns a NEW reference to the callback/errback arg, and to the |
| | 230 | * cbackArgs, but a BORROWED reference to the keywords. In case of |
| | 231 | * error, no references are returned/touched */ |
| | 232 | static PyObject *extract_cback_args_kw(char *argname, |
| | 233 | PyObject *args, PyObject *kwargs, |
| | 234 | PyObject **cbackArgs, |
| | 235 | PyObject **cbackKeywords) |
| | 236 | { |
| | 237 | PyObject *cback; |
| | 238 | |
| | 239 | if (kwargs) { |
| | 240 | (*cbackKeywords) = kwargs; |
| | 241 | } else { |
| | 242 | (*cbackKeywords) = Py_None; |
| | 243 | } |
| | 244 | if (PyTuple_Size(args) > 0) { |
| | 245 | cback = PyTuple_GET_ITEM(args, 0); |
| | 246 | if (!cback) { |
| | 247 | return NULL; |
| | 248 | } |
| | 249 | (*cbackArgs) = PyTuple_GetSlice(args, 1, PyTuple_Size(args)); |
| | 250 | if (!(*cbackArgs)) { |
| | 251 | return NULL; |
| | 252 | } |
| | 253 | Py_INCREF(cback); |
| | 254 | } else { |
| | 255 | cback = PyDict_GetItemString((*cbackKeywords), argname); |
| | 256 | if (!cback) { |
| | 257 | PyErr_Format(PyExc_TypeError, "addCallback requires '%s' argument'", argname); |
| | 258 | return NULL; |
| | 259 | } |
| | 260 | (*cbackArgs) = Py_None; |
| | 261 | Py_INCREF(Py_None); |
| | 262 | |
| | 263 | /* "callback" in the keyword dict may be the only reference to |
| | 264 | * it, and we delete it from the dict, so we must own a |
| | 265 | * reference too */ |
| | 266 | Py_INCREF(cback); |
| | 267 | |
| | 268 | if (PyDict_DelItemString((*cbackKeywords), argname) == -1) { |
| | 269 | Py_DECREF(cback); |
| | 270 | Py_DECREF(Py_None); |
| | 271 | return NULL; |
| | 272 | } |
| | 273 | } |
| | 274 | return cback; |
| | 275 | } |
| | 276 | |
| | 277 | static PyObject *cdefer_Deferred_addCallback(cdefer_Deferred *self, |
| | 278 | PyObject *args, PyObject *kwargs) { |
| | 279 | PyObject *callback; |
| | 280 | PyObject *callbackArgs; |
| | 281 | PyObject *callbackKeywords; |
| | 282 | PyObject *result; |
| | 283 | callback = extract_cback_args_kw("callback", args, kwargs, &callbackArgs, &callbackKeywords); |
| | 284 | if (!callback) { |
| | 285 | return NULL; |
| | 286 | } |
| | 287 | result = cdefer_Deferred__addCallbacks(self, callback, Py_None, callbackArgs, |
| | 288 | callbackKeywords, Py_None, Py_None); |
| | 289 | Py_DECREF(callback); |
| | 290 | Py_DECREF(callbackArgs); |
| | 291 | return result; |
| | 292 | } |
| | 293 | |
| | 294 | static char cdefer_Deferred_addErrback_doc[] = "Convenience method for adding just an errback.\n\nSee L{addCallbacks}."; |
| | 295 | |
| | 296 | static PyObject *cdefer_Deferred_addErrback(cdefer_Deferred *self, |
| | 297 | PyObject *args, PyObject *kwargs) { |
| | 298 | PyObject *errback; |
| | 299 | PyObject *errbackArgs; |
| | 300 | PyObject *errbackKeywords; |
| | 301 | PyObject *result; |
| | 302 | errback = extract_cback_args_kw("errback", args, kwargs, &errbackArgs, &errbackKeywords); |
| | 303 | if (!errback) { |
| | 304 | return NULL; |
| | 305 | } |
| | 306 | result = cdefer_Deferred__addCallbacks(self, Py_None, errback, Py_None, |
| | 307 | Py_None, errbackArgs, errbackKeywords); |
| | 308 | Py_DECREF(errback); |
| | 309 | Py_DECREF(errbackArgs); |
| | 310 | return result; |
| | 311 | } |
| | 312 | |
| | 313 | static char cdefer_Deferred_addBoth_doc[] = "Convenience method for adding a single callable as both a callback\nand an errback.\n\nSee L{addCallbacks}."; |
| | 314 | |
| | 315 | static PyObject *cdefer_Deferred_addBoth(cdefer_Deferred *self, PyObject *args, |
| | 316 | PyObject *kwargs) { |
| | 317 | PyObject *callback; |
| | 318 | PyObject *callbackArgs; |
| | 319 | PyObject *callbackKeywords; |
| | 320 | PyObject *result; |
| | 321 | callback = extract_cback_args_kw("callback", args, kwargs, &callbackArgs, &callbackKeywords); |
| | 322 | if (!callback) { |
| | 323 | return NULL; |
| | 324 | } |
| | 325 | result = cdefer_Deferred__addCallbacks(self, callback, callback, |
| | 326 | callbackArgs, callbackKeywords, callbackArgs, callbackKeywords); |
| | 327 | Py_DECREF(callback); |
| | 328 | Py_DECREF(callbackArgs); |
| | 329 | return result; |
| | 330 | } |
| | 331 | |
| | 332 | static char cdefer_Deferred_pause_doc[] = "Stop processing on a Deferred until L{unpause}() is called."; |
| | 333 | |
| | 334 | static PyObject *cdefer_Deferred_pause(cdefer_Deferred *self, PyObject *args) { |
| | 335 | PyObject *result; |
| | 336 | self->paused++; |
| | 337 | result = Py_None; |
| | 338 | Py_INCREF(Py_None); |
| | 339 | return result; |
| | 340 | } |
| | 341 | |
| | 342 | static char cdefer_Deferred_unpause_doc[] = "Process all callbacks made since L{pause}() was called."; |
| | 343 | |
| | 344 | static PyObject *cdefer_Deferred_unpause(cdefer_Deferred *self, |
| | 345 | PyObject *args) { |
| | 346 | self->paused--; |
| | 347 | if (!self->paused && self->called) { |
| | 348 | return cdefer_Deferred__runCallbacks(self); |
| | 349 | } |
| | 350 | Py_INCREF(Py_None); |
| | 351 | return Py_None; |
| | 352 | } |
| | 353 | |
| | 354 | static char cdefer_Deferred_chainDeferred_doc[] = "Chain another Deferred to this Deferred.\n\nThis method adds callbacks to this Deferred to call d\'s callback or\nerrback, as appropriate."; |
| | 355 | |
| | 356 | static PyObject *cdefer_Deferred_chainDeferred(cdefer_Deferred *self, |
| | 357 | PyObject *args, PyObject *kwargs) { |
| | 358 | PyObject *d; |
| | 359 | PyObject *callback; |
| | 360 | PyObject *errback; |
| | 361 | PyObject *result; |
| | 362 | static char *argnames[] = {"d", NULL}; |
| | 363 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &d)) { |
| | 364 | return NULL; |
| | 365 | } |
| | 366 | callback = PyObject_GetAttrString(d, "callback"); |
| | 367 | if (!callback) { |
| | 368 | return NULL; |
| | 369 | } |
| | 370 | errback = PyObject_GetAttrString(d, "errback"); |
| | 371 | if (!errback) { |
| | 372 | Py_DECREF(callback); |
| | 373 | return NULL; |
| | 374 | } |
| | 375 | result = cdefer_Deferred__addCallbacks(self, callback, errback, Py_None, |
| | 376 | Py_None, Py_None, Py_None); |
| | 377 | Py_DECREF(callback); |
| | 378 | Py_DECREF(errback); |
| | 379 | return result; |
| | 380 | } |
| | 381 | |
| | 382 | static int cdefer_Deferred__set_debuginfo_fail_result(cdefer_Deferred *self) { |
| | 383 | if (!self->debuginfo) { |
| | 384 | self->debuginfo = PyObject_CallObject(debuginfo_class, NULL); |
| | 385 | if (!self->debuginfo) { |
| | 386 | return -1; |
| | 387 | } |
| | 388 | } |
| | 389 | if (PyObject_SetAttrString(self->debuginfo, "failResult", self->result) == -1) { |
| | 390 | return -1; |
| | 391 | } |
| | 392 | return 0; |
| | 393 | } |
| | 394 | |
| | 395 | static int cdefer_Deferred__clear_debuginfo(cdefer_Deferred *self) { |
| | 396 | if (self->debuginfo) { |
| | 397 | if (PyObject_SetAttrString(self->debuginfo, "failResult", Py_None) == -1) { |
| | 398 | return -1; |
| | 399 | } |
| | 400 | } |
| | 401 | return 0; |
| | 402 | } |
| | 403 | |
| | 404 | |
| | 405 | static PyObject *cdefer_Deferred__runCallbacks(cdefer_Deferred *self) { |
| | 406 | PyObject *cb; |
| | 407 | PyObject *item; |
| | 408 | PyObject *callback; |
| | 409 | PyObject *args; |
| | 410 | PyObject *newArgs; |
| | 411 | PyObject *newArgs2; |
| | 412 | PyObject *kwargs; |
| | 413 | PyObject *_continue; |
| | 414 | PyObject *type, *value, *traceback, *failArgs; |
| | 415 | PyObject *tmp; |
| | 416 | PyObject *result; |
| | 417 | int size; |
| | 418 | int offset; |
| | 419 | |
| | 420 | if (!self->paused) { |
| | 421 | cb = self->callbacks; |
| | 422 | for (;;) { |
| | 423 | size = PyList_GET_SIZE(cb); |
| | 424 | if (size == -1) { |
| | 425 | return NULL; |
| | 426 | } |
| | 427 | if (self->callback_index >= size) { |
| | 428 | break; |
| | 429 | } |
| | 430 | |
| | 431 | item = PyList_GET_ITEM(cb, self->callback_index); |
| | 432 | if (!item) { |
| | 433 | return NULL; |
| | 434 | } |
| | 435 | |
| | 436 | offset = 0; |
| | 437 | if (PyObject_IsInstance(self->result, failure_class)) { |
| | 438 | offset = 3; |
| | 439 | } |
| | 440 | |
| | 441 | callback = PyTuple_GET_ITEM(item, offset + 0); |
| | 442 | if (!callback) { |
| | 443 | return NULL; |
| | 444 | } |
| | 445 | if (callback == Py_None) { |
| | 446 | ++self->callback_index; |
| | 447 | continue; |
| | 448 | } |
| | 449 | |
| | 450 | args = PyTuple_GET_ITEM(item, offset + 1); |
| | 451 | if (!args) { |
| | 452 | return NULL; |
| | 453 | } |
| | 454 | |
| | 455 | kwargs = PyTuple_GET_ITEM(item, offset + 2); |
| | 456 | if (!kwargs) { |
| | 457 | return NULL; |
| | 458 | } |
| | 459 | |
| | 460 | newArgs = Py_BuildValue("(O)", self->result); |
| | 461 | if (!newArgs) { |
| | 462 | return NULL; |
| | 463 | } |
| | 464 | |
| | 465 | if (args != Py_None) { |
| | 466 | newArgs2 = PySequence_InPlaceConcat(newArgs, args); |
| | 467 | if (!newArgs2) { |
| | 468 | return NULL; |
| | 469 | } |
| | 470 | Py_CLEAR(newArgs); |
| | 471 | } else { |
| | 472 | newArgs2 = newArgs; |
| | 473 | newArgs = NULL; |
| | 474 | } |
| | 475 | |
| | 476 | ++self->callback_index; |
| | 477 | if (kwargs == Py_None) { |
| | 478 | tmp = PyObject_Call(callback, newArgs2, NULL); |
| | 479 | } else { |
| | 480 | tmp = PyObject_Call(callback, newArgs2, kwargs); |
| | 481 | } |
| | 482 | Py_DECREF(self->result); |
| | 483 | self->result = tmp; |
| | 484 | |
| | 485 | Py_CLEAR(newArgs2); |
| | 486 | |
| | 487 | if (!self->result) { |
| | 488 | PyErr_Fetch(&type, &value, &traceback); |
| | 489 | PyErr_NormalizeException(&type, &value, &traceback); |
| | 490 | if (!traceback) { |
| | 491 | traceback = Py_None; |
| | 492 | Py_INCREF(traceback); |
| | 493 | } |
| | 494 | |
| | 495 | failArgs = Py_BuildValue("(OOO)", value, type, traceback); |
| | 496 | if (!failArgs) { |
| | 497 | PyErr_Restore(type, value, traceback); |
| | 498 | return NULL; |
| | 499 | } |
| | 500 | self->result = PyObject_CallObject(failure_class, failArgs); |
| | 501 | Py_DECREF(failArgs); |
| | 502 | if (!self->result) { |
| | 503 | PyErr_Restore(type, value, traceback); |
| | 504 | return NULL; |
| | 505 | } |
| | 506 | Py_DECREF(type); |
| | 507 | Py_DECREF(value); |
| | 508 | Py_DECREF(traceback); |
| | 509 | continue; |
| | 510 | } |
| | 511 | Py_INCREF(self->result); |
| | 512 | if (PyObject_TypeCheck(self->result, &cdefer_DeferredType)) { |
| | 513 | if (PyList_SetSlice(cb, 0, self->callback_index, NULL) == -1) { |
| | 514 | return NULL; |
| | 515 | } |
| | 516 | self->callback_index = 0; |
| | 517 | |
| | 518 | result = PyObject_CallMethod((PyObject *)self, "pause", NULL); |
| | 519 | if (!result) { |
| | 520 | return NULL; |
| | 521 | } |
| | 522 | Py_DECREF(result); |
| | 523 | |
| | 524 | _continue = PyObject_GetAttrString((PyObject *)self, |
| | 525 | "_continue"); |
| | 526 | if (!_continue) { |
| | 527 | return NULL; |
| | 528 | } |
| | 529 | |
| | 530 | result = cdefer_Deferred__addCallbacks( |
| | 531 | (cdefer_Deferred *)self->result, _continue, |
| | 532 | _continue, Py_None, Py_None, Py_None, Py_None); |
| | 533 | /* The reference was either copied/incref'd or not |
| | 534 | * (when errored) in addCallbacks, either way, we own |
| | 535 | * one too, and don't need it anymore. */ |
| | 536 | Py_DECREF(_continue); |
| | 537 | |
| | 538 | if (!result) { |
| | 539 | return NULL; |
| | 540 | } |
| | 541 | Py_DECREF(result); |
| | 542 | |
| | 543 | goto endLabel; |
| | 544 | } |
| | 545 | } |
| | 546 | if (PyList_SetSlice(cb, 0, PyList_GET_SIZE(cb), NULL) == -1) { |
| | 547 | return NULL; |
| | 548 | } |
| | 549 | self->callback_index = 0; |
| | 550 | } |
| | 551 | endLabel:; |
| | 552 | if (PyObject_IsInstance(self->result, failure_class)) { |
| | 553 | result = PyObject_CallMethod((PyObject *)self->result, |
| | 554 | "cleanFailure", NULL); |
| | 555 | if (!result) { |
| | 556 | return NULL; |
| | 557 | } |
| | 558 | Py_DECREF(result); |
| | 559 | if (cdefer_Deferred__set_debuginfo_fail_result(self) == -1) { |
| | 560 | return NULL; |
| | 561 | } |
| | 562 | } else { |
| | 563 | if (cdefer_Deferred__clear_debuginfo(self) == -1) { |
| | 564 | return NULL; |
| | 565 | } |
| | 566 | } |
| | 567 | Py_INCREF(Py_None); |
| | 568 | return Py_None; |
| | 569 | } |
| | 570 | |
| | 571 | static PyObject *cdefer_Deferred__startRunCallbacks(cdefer_Deferred *self, |
| | 572 | PyObject *result) { |
| | 573 | if (self->called) { |
| | 574 | PyErr_SetNone(already_called); |
| | 575 | return NULL; |
| | 576 | } |
| | 577 | self->called = 1; |
| | 578 | Py_XDECREF(self->result); |
| | 579 | self->result = result; |
| | 580 | Py_INCREF(self->result); |
| | 581 | return cdefer_Deferred__runCallbacks(self); |
| | 582 | } |
| | 583 | |
| | 584 | static char cdefer_Deferred_callback_doc[] = "Run all success callbacks that have been added to this Deferred.\n\nEach callback will have its result passed as the first\nargument to the next; this way, the callbacks act as a\n\'processing chain\'. Also, if the success-callback returns a Failure\nor raises an Exception, processing will continue on the *error*-\ncallback chain."; |
| | 585 | |
| | 586 | static PyObject *cdefer_Deferred_callback(cdefer_Deferred *self, PyObject *args, |
| | 587 | PyObject *kwargs) { |
| | 588 | PyObject *result; |
| | 589 | static char *argnames[] = {"result", NULL}; |
| | 590 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &result)) { |
| | 591 | return NULL; |
| | 592 | } |
| | 593 | return cdefer_Deferred__startRunCallbacks(self, result); |
| | 594 | } |
| | 595 | |
| | 596 | static char cdefer_Deferred_errback_doc[] = "Run all error callbacks that have been added to this Deferred.\n\nEach callback will have its result passed as the first\nargument to the next; this way, the callbacks act as a\n\'processing chain\'. Also, if the error-callback returns a non-Failure\nor doesn\'t raise an Exception, processing will continue on the\n*success*-callback chain.\n\nIf the argument that\'s passed to me is not a Failure instance,\nit will be embedded in one. If no argument is passed, a Failure\ninstance will be created based on the current traceback stack.\n\nPassing a string as `fail\' is deprecated, and will be punished with\na warning message."; |
| | 597 | |
| | 598 | static PyObject *cdefer_Deferred_errback(cdefer_Deferred *self, PyObject *args, |
| | 599 | PyObject *kwargs) { |
| | 600 | PyObject *fail; |
| | 601 | PyObject *tpl; |
| | 602 | PyObject *tmp; |
| | 603 | PyObject *result; |
| | 604 | static char *argnames[] = {"fail", NULL}; |
| | 605 | fail = Py_None; |
| | 606 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", argnames, &fail)) { |
| | 607 | return NULL; |
| | 608 | } |
| | 609 | |
| | 610 | if (PyObject_IsInstance(fail, failure_class)) { |
| | 611 | /* Make "fail" belong to us even if we don't create a Failure |
| | 612 | * wrapper (If we do, the wrapper belongs to us) */ |
| | 613 | Py_INCREF(fail); |
| | 614 | } else { |
| | 615 | tpl = Py_BuildValue("(O)", fail); |
| | 616 | if (!tpl) { |
| | 617 | return NULL; |
| | 618 | } |
| | 619 | tmp = PyObject_CallObject(failure_class, tpl); |
| | 620 | Py_DECREF(tpl); |
| | 621 | if (!tmp) { |
| | 622 | return NULL; |
| | 623 | } |
| | 624 | fail = tmp; |
| | 625 | } |
| | 626 | result = cdefer_Deferred__startRunCallbacks(self, fail); |
| | 627 | Py_DECREF(fail); |
| | 628 | return result; |
| | 629 | } |
| | 630 | |
| | 631 | static PyObject *cdefer_Deferred__continue(cdefer_Deferred *self, |
| | 632 | PyObject *args, PyObject *kwargs) { |
| | 633 | PyObject *result; |
| | 634 | static char *argnames[] = {"result", NULL}; |
| | 635 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &result)) { |
| | 636 | return NULL; |
| | 637 | } |
| | 638 | Py_XDECREF(self->result); |
| | 639 | self->result = result; |
| | 640 | Py_INCREF(self->result); |
| | 641 | return PyObject_CallMethod((PyObject *)self, "unpause", NULL); |
| | 642 | } |
| | 643 | |
| | 644 | static struct PyMethodDef cdefer_Deferred_methods[] = { |
| | 645 | {"addCallbacks", (PyCFunction)cdefer_Deferred_addCallbacks, |
| | 646 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addCallbacks_doc}, |
| | 647 | {"addCallback", (PyCFunction)cdefer_Deferred_addCallback, |
| | 648 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addCallback_doc}, |
| | 649 | {"addErrback", (PyCFunction)cdefer_Deferred_addErrback, |
| | 650 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addErrback_doc}, |
| | 651 | {"addBoth", (PyCFunction)cdefer_Deferred_addBoth, |
| | 652 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addBoth_doc}, |
| | 653 | {"chainDeferred", (PyCFunction)cdefer_Deferred_chainDeferred, |
| | 654 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_chainDeferred_doc}, |
| | 655 | {"callback", (PyCFunction)cdefer_Deferred_callback, |
| | 656 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_callback_doc}, |
| | 657 | {"errback", (PyCFunction)cdefer_Deferred_errback, |
| | 658 | METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_errback_doc}, |
| | 659 | {"pause", (PyCFunction)cdefer_Deferred_pause, |
| | 660 | METH_VARARGS, cdefer_Deferred_pause_doc}, |
| | 661 | {"unpause", (PyCFunction)cdefer_Deferred_unpause, |
| | 662 | METH_VARARGS, cdefer_Deferred_unpause_doc}, |
| | 663 | {"_continue", (PyCFunction)cdefer_Deferred__continue, |
| | 664 | METH_VARARGS|METH_KEYWORDS, ""}, |
| | 665 | {0, 0, 0, 0} |
| | 666 | }; |
| | 667 | |
| | 668 | static struct PyMemberDef cdefer_Deferred_members[] = { |
| | 669 | {"result", T_OBJECT_EX, offsetof(cdefer_Deferred, result), 0, 0}, |
| | 670 | {"paused", T_INT, offsetof(cdefer_Deferred, paused), READONLY, 0}, |
| | 671 | {"called", T_INT, offsetof(cdefer_Deferred, called), READONLY, 0}, |
| | 672 | {"callbacks", T_OBJECT, offsetof(cdefer_Deferred, callbacks), READONLY, 0}, |
| | 673 | {0, 0, 0, 0, 0} |
| | 674 | }; |
| | 675 | |
| | 676 | static PyTypeObject cdefer_DeferredType = { |
| | 677 | PyObject_HEAD_INIT(0) |
| | 678 | 0, /*ob_size*/ |
| | 679 | "cdefer.Deferred", /*tp_name*/ |
| | 680 | sizeof(cdefer_Deferred), /*tp_basicsize*/ |
| | 681 | 0, /*tp_itemsize*/ |
| | 682 | (destructor)cdefer_Deferred_dealloc, /*tp_dealloc*/ |
| | 683 | 0, /*tp_print*/ |
| | 684 | 0, /*tp_getattr*/ |
| | 685 | 0, /*tp_setattr*/ |
| | 686 | 0, /*tp_compare*/ |
| | 687 | 0, /*tp_repr*/ |
| | 688 | 0, /*tp_as_number*/ |
| | 689 | 0, /*tp_as_sequence*/ |
| | 690 | 0, /*tp_as_mapping*/ |
| | 691 | 0, /*tp_hash */ |
| | 692 | 0, /*tp_call*/ |
| | 693 | 0, /*tp_str*/ |
| | 694 | 0, /*tp_getattro*/ |
| | 695 | 0, /*tp_setattro*/ |
| | 696 | 0, /*tp_as_buffer*/ |
| | 697 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ |
| | 698 | "This is a callback which will be put off until later.\n\nWhy do we want this? Well, in cases where a function in a threaded\nprogram would block until it gets a result, for Twisted it should\nnot block. Instead, it should return a Deferred.\n\nThis can be implemented for protocols that run over the network by\nwriting an asynchronous protocol for twisted.internet. For methods\nthat come from outside packages that are not under our control, we use\nthreads (see for example L{twisted.enterprise.adbapi}).\n\nFor more information about Deferreds, see doc/howto/defer.html or\nU{http://www.twistedmatrix.com/documents/howto/defer}.", /*tp_doc*/ |
| | 699 | (traverseproc)cdefer_Deferred_traverse, /*tp_traverse*/ |
| | 700 | (inquiry)cdefer_Deferred_clear, /*tp_clear*/ |
| | 701 | 0, /*tp_richcompare*/ |
| | 702 | 0, /*tp_weaklistoffset*/ |
| | 703 | 0, /*tp_iter*/ |
| | 704 | 0, /*tp_iternext*/ |
| | 705 | cdefer_Deferred_methods, /*tp_methods*/ |
| | 706 | cdefer_Deferred_members, /*tp_members*/ |
| | 707 | 0, /*tp_getset*/ |
| | 708 | 0, /*tp_base*/ |
| | 709 | 0, /*tp_dict*/ |
| | 710 | 0, /*tp_descr_get*/ |
| | 711 | 0, /*tp_descr_set*/ |
| | 712 | 0, /*tp_dictoffset*/ |
| | 713 | (initproc)cdefer_Deferred___init__, /*tp_init*/ |
| | 714 | 0, /*tp_alloc*/ |
| | 715 | cdefer_Deferred_new, /*tp_new*/ |
| | 716 | PyObject_GC_Del, /*tp_free*/ |
| | 717 | 0, /*tp_is_gc*/ |
| | 718 | 0, /*tp_bases*/ |
| | 719 | 0, /*tp_mro*/ |
| | 720 | 0, /*tp_cache*/ |
| | 721 | 0, /*tp_subclasses*/ |
| | 722 | 0, /*tp_weaklist*/ |
| | 723 | }; |
| | 724 | |
| | 725 | static PyMethodDef cdefer_methods[] = { |
| | 726 | {NULL} /* Sentinel */ |
| | 727 | }; |
| | 728 | |
| | 729 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ |
| | 730 | #define PyMODINIT_FUNC void |
| | 731 | #endif |
| | 732 | PyMODINIT_FUNC initcdefer(void) { |
| | 733 | PyObject * m = NULL; |
| | 734 | PyObject * f = NULL; |
| | 735 | PyObject * d = NULL; |
| | 736 | |
| | 737 | if (PyType_Ready(&cdefer_DeferredType) < 0) { |
| | 738 | return; |
| | 739 | } |
| | 740 | |
| | 741 | m = Py_InitModule3("cdefer", cdefer_methods, |
| | 742 | "cdefer"); |
| | 743 | |
| | 744 | if (!m) { |
| | 745 | return; |
| | 746 | } |
| | 747 | |
| | 748 | Py_INCREF(&cdefer_DeferredType); |
| | 749 | PyModule_AddObject(m, "Deferred", (PyObject *)&cdefer_DeferredType); |
| | 750 | |
| | 751 | f = PyImport_ImportModule("twisted.python.failure"); |
| | 752 | if (!f) { |
| | 753 | goto Error; |
| | 754 | } |
| | 755 | |
| | 756 | failure_class = PyObject_GetAttrString(f, "Failure"); |
| | 757 | if (!failure_class) { |
| | 758 | goto Error; |
| | 759 | } |
| | 760 | |
| | 761 | d = PyImport_ImportModule("twisted.internet.defer"); |
| | 762 | if (!d) { |
| | 763 | goto Error; |
| | 764 | } |
| | 765 | already_called = PyObject_GetAttrString(d, "AlreadyCalledError"); |
| | 766 | if (!already_called) { |
| | 767 | goto Error; |
| | 768 | } |
| | 769 | |
| | 770 | debuginfo_class = PyObject_GetAttrString(d, "DebugInfo"); |
| | 771 | if(!debuginfo_class) { |
| | 772 | goto Error; |
| | 773 | } |
| | 774 | |
| | 775 | return; |
| | 776 | Error: |
| | 777 | Py_XDECREF(f); |
| | 778 | Py_XDECREF(failure_class); |
| | 779 | Py_XDECREF(d); |
| | 780 | Py_XDECREF(already_called); |
| | 781 | Py_XDECREF(debuginfo_class); |
| | 782 | } |
| | 783 | |