Ticket #2245: cdefer.6.patch

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

Add real debugging support, the previous approach was completely broken

  • 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