Ticket #2245: cdefer.7.patch

File cdefer.7.patch, 9.7 KB (added by Peaker, 9 years ago)

Fix a bug in the way fail objects were passed to Failure in errback()

  • twisted/internet/cdefer/cdefer.c

     
    3737PyObject * failure_class = NULL;
    3838PyObject * already_called = NULL;
    3939PyObject * debuginfo_class = NULL;
     40PyObject * format_stack = NULL;
    4041
    4142typedef struct {
    4243    PyObject_HEAD
     
    5556
    5657/* Prototypes */
    5758
     59static PyObject *cdefer_setDebugging(cdefer_Deferred *self,
     60        PyObject *args, PyObject *kwargs);
     61
     62static PyObject *cdefer_getDebugging(cdefer_Deferred *self,
     63        PyObject *args, PyObject *kwargs);
     64
    5865static PyObject * cdefer_Deferred_new(PyTypeObject *type, PyObject *args,
    5966        PyObject *kwargs);
    6067
     
    105112static PyObject *cdefer_Deferred__continue(cdefer_Deferred *self,
    106113        PyObject *args, PyObject *kwargs);
    107114
     115
     116static int is_debug = 0;
     117
     118static PyObject *cdefer_setDebugging(cdefer_Deferred *self,
     119        PyObject *args, PyObject *kwargs)
     120{
     121    int new_debug;
     122    PyObject *on;
     123    static char *argnames[] = {"on", NULL};
     124    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &on)) {
     125        return NULL;
     126    }
     127    new_debug = PyObject_IsTrue(on);
     128    if (-1 == new_debug) {
     129        return NULL;
     130    }
     131    is_debug = new_debug;
     132    Py_INCREF(Py_None);
     133    return Py_None;
     134}
     135
     136static char cdefer_setDebugging_doc[] = "Enable or disable Deferred debugging.\n\n    When debugging is on, the call stacks from creation and invocation are\n    recorded, and added to any AlreadyCalledErrors we raise.\n";
     137
     138
     139static PyObject *cdefer_getDebugging(cdefer_Deferred *self,
     140        PyObject *args, PyObject *kwargs)
     141{
     142    static char *argnames[] = {NULL};
     143    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", argnames)) {
     144        return NULL;
     145    }
     146    return PyBool_FromLong(is_debug);
     147}
     148
     149static char cdefer_getDebugging_doc[] = "Determine whether Deferred debugging is enabled.\n";
     150
     151
    108152static PyTypeObject cdefer_DeferredType;
    109153
    110154static PyObject * cdefer_Deferred_new(PyTypeObject *type, PyObject *args,
    111         PyObject *kwargs) {
     155                                      PyObject *kwargs)
     156{
    112157    cdefer_Deferred *self;
    113158    self = (cdefer_Deferred *)type->tp_alloc(type, 0);
    114159    return (PyObject *)self;
     
    142187    return 0;
    143188}
    144189
     190static int cdefer_Deferred__set_debug_stack(cdefer_Deferred *self, char *name)
     191{
     192    int rc;
     193    PyObject *stack;
     194
     195    /* Keep the debug info object even if we fail to format stack
     196     * or place it into the dict. */
     197    stack = PyObject_CallObject(format_stack, NULL);
     198    if (!stack) {
     199        return -1;
     200    }
     201    rc = PyObject_SetAttrString(self->debuginfo, name, stack);
     202    /* Unlike other functions of this naming convention (and
     203     * unlike PyDict_GetItemString), PyDict_SetItemString
     204     * copies/creates a new reference, so we shouldn't keep ours
     205     * too. */
     206    Py_DECREF(stack);
     207    if (-1 == rc) {
     208        return -1;
     209    }
     210    return 0;
     211}
     212
    145213static int cdefer_Deferred___init__(cdefer_Deferred *self, PyObject *args,
    146         PyObject *kwargs) {
     214                                    PyObject *kwargs)
     215{
    147216    static char *argnames[] = {NULL};
    148217    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", argnames)) {
    149218        return -1;
    150219    }
     220    if (is_debug) {
     221        self->debuginfo = PyObject_CallObject(debuginfo_class, NULL);
     222        if (!self->debuginfo) {
     223            return -1;
     224        }
     225        if (-1 == cdefer_Deferred__set_debug_stack(self, "creator")) {
     226            return -1;
     227        }
     228    }
     229
    151230    self->paused = 0;
    152231    self->callback_index = 0;
    153232    self->callbacks = PyList_New(0);
     
    411490    PyObject *newArgs2;
    412491    PyObject *kwargs;
    413492    PyObject *_continue;
    414     PyObject *type, *value, *traceback, *failArgs;
     493    PyObject *type, *value, *traceback;
    415494    PyObject *tmp;
    416495    PyObject *result;
    417496    int size;
     
    492571                    Py_INCREF(traceback);
    493572                }
    494573
    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);
     574                self->result = PyObject_CallFunction(failure_class, "OOO", value, type, traceback);
    502575                if (!self->result) {
    503576                    PyErr_Restore(type, value, traceback);
    504577                    return NULL;
     
    569642}
    570643
    571644static PyObject *cdefer_Deferred__startRunCallbacks(cdefer_Deferred *self,
    572         PyObject *result) {
     645                                                    PyObject *result)
     646{
     647    PyObject * already_called_instance;
     648    PyObject * debug_tracebacks;
     649   
     650    if (is_debug && !self->debuginfo) {
     651        self->debuginfo = PyObject_CallObject(debuginfo_class, NULL);
     652        if (!self->debuginfo) {
     653            return NULL;
     654        }
     655    }
     656
    573657    if (self->called) {
     658        if (is_debug) {
     659            debug_tracebacks = PyObject_CallMethod(
     660                self->debuginfo, "_getDebugTracebacks", "s", "\n");
     661            if (!debug_tracebacks) {
     662                return NULL;
     663            }
     664            already_called_instance = PyObject_CallFunction(already_called, "O", debug_tracebacks);
     665            Py_DECREF(debug_tracebacks);
     666            if (!already_called_instance) {
     667                return NULL;
     668            }
     669            PyErr_SetObject(already_called, already_called_instance);
     670            Py_DECREF(already_called_instance);
     671            return NULL;
     672        }
    574673        PyErr_SetNone(already_called);
    575674        return NULL;
    576675    }
     676    if (is_debug) {
     677        if (-1 == cdefer_Deferred__set_debug_stack(self, "invoker")) {
     678            return NULL;
     679        }
     680    }
     681
    577682    self->called = 1;
    578683    Py_XDECREF(self->result);
    579684    self->result = result;
     
    598703static PyObject *cdefer_Deferred_errback(cdefer_Deferred *self, PyObject *args,
    599704        PyObject *kwargs) {
    600705    PyObject *fail;
    601     PyObject *tpl;
    602706    PyObject *tmp;
    603707    PyObject *result;
    604708    static char *argnames[] = {"fail", NULL};
     
    612716         * wrapper (If we do, the wrapper belongs to us) */
    613717        Py_INCREF(fail);
    614718    } 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);
     719        tmp = PyObject_CallFunction(failure_class, "(O)", fail);
    621720        if (!tmp) {
    622721            return NULL;
    623722        }
     
    723822};
    724823
    725824static PyMethodDef cdefer_methods[] = {
     825    {"setDebugging", (PyCFunction)cdefer_setDebugging,
     826     METH_VARARGS|METH_KEYWORDS, cdefer_setDebugging_doc},
     827   
     828    {"getDebugging", (PyCFunction)cdefer_getDebugging,
     829     METH_VARARGS|METH_KEYWORDS, cdefer_getDebugging_doc},
     830   
    726831    {NULL}  /* Sentinel */
    727832};
    728833
     
    733838    PyObject * m = NULL;
    734839    PyObject * f = NULL;
    735840    PyObject * d = NULL;
     841    PyObject * traceback_module = NULL;
    736842
    737843    if (PyType_Ready(&cdefer_DeferredType) < 0) {
    738844        return;
     
    771877    if(!debuginfo_class) {
    772878        goto Error;
    773879    }
    774    
     880
     881    traceback_module = PyImport_ImportModule("traceback");
     882    if (!traceback_module) {
     883        goto Error;
     884    }
     885
     886    format_stack = PyObject_GetAttrString(traceback_module, "format_stack");
     887    if(!format_stack) {
     888        goto Error;
     889    }
     890
    775891    return;
    776892Error:
    777893    Py_XDECREF(f);
     
    779895    Py_XDECREF(d);
    780896    Py_XDECREF(already_called);
    781897    Py_XDECREF(debuginfo_class);
     898    Py_XDECREF(traceback_module);
     899    Py_XDECREF(format_stack);
    782900}
    783901
  • twisted/internet/defer.py

     
    281281        self.unpause()
    282282
    283283    def _startRunCallbacks(self, result):
     284        if self.debug and self._debugInfo is None:
     285            self._debugInfo = DebugInfo()
    284286        if self.called:
    285287            if self.debug:
    286                 if self._debugInfo is None:
    287                     self._debugInfo = DebugInfo()
    288                 extra = "\n" + self._debugInfo._getDebugTracebacks()
     288                extra = self._debugInfo._getDebugTracebacks("\n")
    289289                raise AlreadyCalledError(extra)
    290290            raise AlreadyCalledError
    291291        if self.debug:
    292             if self._debugInfo is None:
    293                 self._debugInfo = DebugInfo()
    294292            self._debugInfo.invoker = traceback.format_stack()[:-2]
    295293        self.called = True
    296294        self.result = result
     
    376374    """Deferred debug helper"""
    377375    failResult = None
    378376
    379     def _getDebugTracebacks(self):
    380         info = ''
     377    def _getDebugTracebacks(self, prefix=''):
     378        info = prefix
    381379        if hasattr(self, "creator"):
    382380            info += " C: Deferred was created:\n C:"
    383381            info += "".join(self.creator).rstrip().replace("\n","\n C:")
     
    439437        return False
    440438
    441439try:
    442     from twisted.internet.cdefer import Deferred as CDeferred
     440    from twisted.internet.cdefer import Deferred, setDebugging, getDebugging
    443441except ImportError:
    444442    pass
    445 else:
    446     PyDeferred = Deferred
    447     PyDeferred.debug = True
    448     # Debugging enabled via use of PyDeferred
    449     def setDebugging(new_value):
    450         global is_debug, Deferred
    451         is_debug = new_value
    452         if is_debug:
    453             Deferred = PyDeferred
    454         else:
    455             Deferred = CDeferred
    456     def getDebugging():
    457         global is_debug
    458         return is_debug
    459443
    460     setDebugging(False)
    461 
    462444class DeferredList(Deferred):
    463445    """I combine a group of deferreds into one callback.
    464446