2009-11-25 16 views
24

से पाइथन ट्रेसबैक तक पहुंचने में मुझे सी एपीआई का उपयोग करके पाइथन ट्रेसबैक चलने का उचित तरीका पता लगाने में कुछ परेशानी हो रही है। मैं एक ऐसा एप्लीकेशन लिख रहा हूं जो पायथन दुभाषिया को एम्बेड करता है। मैं मनमाने ढंग से पायथन कोड निष्पादित करने में सक्षम होना चाहता हूं, और यदि यह अपवाद उठाता है, तो इसे अपने स्वयं के एप्लिकेशन-विशिष्ट C++ अपवाद में अनुवादित करने के लिए। अभी के लिए, केवल फ़ाइल नाम और लाइन नंबर निकालने के लिए पर्याप्त है जहां पायथन अपवाद उठाया गया था।सी एपीआई

PyObject* pyresult = PyObject_CallObject(someCallablePythonObject, someArgs); 
if (!pyresult) 
{ 
    PyObject* excType, *excValue, *excTraceback; 
    PyErr_Fetch(&excType, &excValue, &excTraceback); 
    PyErr_NormalizeException(&excType, &excValue, &excTraceback); 

    PyTracebackObject* traceback = (PyTracebackObject*)traceback; 
    // Advance to the last frame (python puts the most-recent call at the end) 
    while (traceback->tb_next != NULL) 
     traceback = traceback->tb_next; 

    // At this point I have access to the line number via traceback->tb_lineno, 
    // but where do I get the file name from? 

    // ...  
} 

अजगर स्रोत कोड में चारों ओर खुदाई, मैं वे _frame संरचना, जो लग रहा है के माध्यम से वर्तमान फ्रेम के दोनों फ़ाइल नाम और मॉड्यूल का नाम का उपयोग को देखने जैसे कि यह एक privately- है: यह क्या मैं अब तक किया है परिभाषित संरचना। मेरा अगला विचार प्रोग्रामिक रूप से पायथन 'ट्रेसबैक' मॉड्यूल लोड करना था और सी एपीआई के साथ अपने कार्यों को कॉल करना था। क्या यह साईं है? क्या सी से पाइथन ट्रेसबैक तक पहुंचने का कोई बेहतर तरीका है?

+0

[PyErr_Fetch] (https://docs.python.org/2/c-api/exceptions.html#c.PyErr_Fetch) स्मृति रिसाव का उत्पादन (पर निर्भर करता है कार्यान्वयन यह महत्वपूर्ण हो सकता है) – alex

+0

"PyTracebackObject * traceback = (PyTracebackObject *) ट्रेसबैक का उद्देश्य क्या है;"? मुझे लगता है कि आपका मतलब है "PyTracebackObject * traceback = (PyTracebackObject *) excTraceback;"। – aquirdturtle

उत्तर

8

मुझे पता चला है कि _frame वास्तव में frameobject.h हेडर में पाइथन के साथ शामिल किया गया है। इस के साथ साथ साथ अजगर सी कार्यान्वयन में traceback.c को देख सुसज्जित होकर, हमने:

#include <Python.h> 
#include <frameobject.h> 

PyTracebackObject* traceback = get_the_traceback(); 

int line = traceback->tb_lineno; 
const char* filename = PyString_AsString(traceback->tb_frame->f_code->co_filename); 

लेकिन यह अभी भी मेरे लिए वास्तव में गंदा लगता है।

3

एक प्रिंसिपल जिसे मैंने सी एक्सटेंशन में उपयोगी पाया है, वह प्रत्येक भाषा का उपयोग करना है जहां यह सबसे उपयुक्त है। तो यदि आपके पास ऐसा करने का कोई काम है जो पाइथन में सर्वोत्तम रूप से कार्यान्वित किया जाएगा, तो पाइथन में कार्यान्वित किया जाएगा, और यदि इसे सी में सबसे अच्छा कार्यान्वित किया जाएगा, तो इसे सी में करें। दो कारणों से पाइथन में ट्रेसबैक का सबसे अच्छा प्रदर्शन किया जाता है: पहला, क्योंकि पायथन यह करने के लिए उपकरण है, और दूसरा, क्योंकि यह गति-महत्वपूर्ण नहीं है।

मैं जानकारी आप ट्रैस बैक से की जरूरत को निकालने के लिए एक अजगर समारोह लिखते थे, तो सी

से इसे कहते

तुम भी अब तक जा सकते हैं के रूप में अपने प्रतिदेय निष्पादन के लिए एक अजगर आवरण लिखने के लिए। इसके बजाय someCallablePythonObject लागू की, अपने अजगर कार्य करने के लिए एक तर्क के रूप में यह पारित:

def invokeSomeCallablePythonObject(obj, args): 
    try: 
     result = obj(*args) 
     ok = True 
    except: 
     # Do some mumbo-jumbo with the traceback, etc. 
     result = myTraceBackMunger(...) 
     ok = False 
    return ok, result 

फिर अपने सी कोड में, काम करने के लिए इस अजगर फ़ंक्शन को कॉल करें। यहां कुंजी यह तय करने के लिए है कि सी-पायथन के किनारे आपके कोड को विभाजित करने के लिए विभाजित हैं।

+0

यकीन नहीं है कि मैं समझता हूं कि यह कैसे मदद करता है। मैं एक विस्तार मॉड्यूल नहीं लिख रहा हूं, बल्कि दुभाषिया को एम्बेड कर रहा हूं। तो अपने समाधान को लागू करने के लिए (यदि मैं आपको सही समझता हूं) मुझे पाइथन कोड का ब्लॉब लिखना होगा और इसे मेरे सी ++ कोड में एक स्ट्रिंग के रूप में स्टोर करना होगा। फिर किसी बिंदु पर मुझे कोड संकलित करना होगा, इसके बाहर एक फ़ंक्शन बनाना होगा, फिर फ़ंक्शन को PyObject_CallObject के माध्यम से कॉल करें। यह सी – cwick

+0

में मूल स्टैक फ्रेम संरचनाओं की जांच करने की तुलना में काम के एक टन की तरह लगता है। उनकी सलाह के आधार पर व्यावहारिक रूप से भाषाओं के बीच विभाजित करने की यह सलाह बहुत समझ में आता है, लेकिन मुझे प्रक्रिया के लिए मनमानी पायथन निष्पादित करने की कोशिश करने से सावधान रहना कुछ अन्य मनमानी पायथन निष्पादित करने से त्रुटि स्थिति। – caps

9

यह एक पुराना सवाल है, लेकिन भविष्य के संदर्भ के लिए, आप थ्रेड स्टेट ऑब्जेक्ट से वर्तमान स्टैक फ्रेम प्राप्त कर सकते हैं और फिर फ्रेम को पिछड़ा चल सकते हैं। जब तक आप भविष्य के लिए राज्य को संरक्षित नहीं करना चाहते हैं तब तक एक ट्रेसबैक ऑब्जेक्ट आवश्यक नहीं है।

उदाहरण के लिए:

PyThreadState *tstate = PyThreadState_GET(); 
if (NULL != tstate && NULL != tstate->frame) { 
    PyFrameObject *frame = tstate->frame; 

    printf("Python stack trace:\n"); 
    while (NULL != frame) { 
     // int line = frame->f_lineno; 
     /* 
     frame->f_lineno will not always return the correct line number 
     you need to call PyCode_Addr2Line(). 
     */ 
     int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); 
     const char *filename = PyString_AsString(frame->f_code->co_filename); 
     const char *funcname = PyString_AsString(frame->f_code->co_name); 
     printf(" %s(%d): %s\n", filename, line, funcname); 
     frame = frame->f_back; 
    } 
} 
9

मैं सी से अजगर में बुला पसंद करते हैं:

err = PyErr_Occurred(); 
if (err != NULL) { 
    PyObject *ptype, *pvalue, *ptraceback; 
    PyObject *pystr, *module_name, *pyth_module, *pyth_func; 
    char *str; 

    PyErr_Fetch(&ptype, &pvalue, &ptraceback); 
    pystr = PyObject_Str(pvalue); 
    str = PyString_AsString(pystr); 
    error_description = strdup(str); 

    /* See if we can get a full traceback */ 
    module_name = PyString_FromString("traceback"); 
    pyth_module = PyImport_Import(module_name); 
    Py_DECREF(module_name); 

    if (pyth_module == NULL) { 
     full_backtrace = NULL; 
     return; 
    } 

    pyth_func = PyObject_GetAttrString(pyth_module, "format_exception"); 
    if (pyth_func && PyCallable_Check(pyth_func)) { 
     PyObject *pyth_val; 

     pyth_val = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL); 

     pystr = PyObject_Str(pyth_val); 
     str = PyString_AsString(pystr); 
     full_backtrace = strdup(str); 
     Py_DECREF(pyth_val); 
    } 
} 
+0

आप यहां कुछ Py_DECREFs खो रहे हैं ... 'pystrject'tr' को प्रत्येक कॉल के बाद' pystr' को कम करने की आवश्यकता है, और 'pyth_module' को भी कम करने की आवश्यकता है। –

+0

स्पष्टीकरण के लिए, यह कोड नेड बैचहोल्डर के उत्तर द्वारा प्रस्तावित myTraceBackMunger() का कार्यान्वयन है। मुद्दा यह है कि, हालांकि एक अपवाद हो सकता है, पाइथन दुभाषिया का उपयोग अभी भी किया जा सकता है, और इसका उपयोग किया जाना चाहिए क्योंकि यह ट्रेसबैक का संचालन उच्च स्तर पर है, आपको विवरणों को समझने की आवश्यकता नहीं है। हालांकि, यदि आप सी का उपयोग करना चाहते हैं, तो जेसन मैककैम्पबेल का जवाब सबसे सरल जैसा लगता है, न कि ट्रेसबैक ऑब्जेक्ट का उपयोग करके, केवल फ्रेम के अंतर्निहित ढेर। – bootchk

+0

इस कोड के साथ मुझे 'PyObject_CallFunctionObjArgs'' पर कॉल करते समय एक त्रुटि मिली - 'pvalue' में गलत प्रकार था। इसलिए मैंने 'PyErr_NormalizeException' को' PyErr_Fetch' के बाद जोड़ा है क्योंकि Bartosz Kosarzycki ने अपने उत्तर में किया था और यह अब काम करता है। –

2

मैं इस हाल ही में करने के लिए कारण था, जबकि numpy के लिए आवंटन पर नजर लेखन। पिछले उत्तर करीब हैं लेकिन frame->f_lineno हमेशा सही लाइन नंबर नहीं लौटाएंगे - आपको PyFrame_GetLineNumber() पर कॉल करने की आवश्यकता है। यहाँ एक अद्यतन कोड स्निपेट है:

#include "frameobject.h" 
... 

PyFrameObject* frame = PyEval_GetFrame(); 
int lineno = PyFrame_GetLineNumber(frame); 
PyObject *filename = frame->f_code->co_filename; 

पूर्ण धागा राज्य भी PyFrameObject में उपलब्ध है; यदि आप स्टैक चलना चाहते हैं तो f_back पर इसे तब तक चालू रखें जब तक यह नल न हो। फ्रेमोबजेक्ट में पूर्ण डेटा संरचना चेकआउट करें।ज: http://svn.python.org/projects/python/trunk/Include/frameobject.h

भी देखें: https://docs.python.org/2/c-api/reflection.html

1

मैं निकालने के लिए अजगर अपवाद के त्रुटि शरीर निम्नलिखित कोड का इस्तेमाल किया। strExcType अपवाद प्रकार स्टोर करता है और strExcValue अपवाद निकाय को संग्रहीत करता है। मानों का नमूना हैं:

strExcType:"<class 'ImportError'>" 
strExcValue:"ImportError("No module named 'nonexistingmodule'",)" 

सीपीपी कोड:

if(PyErr_Occurred() != NULL) { 
    PyObject *pyExcType; 
    PyObject *pyExcValue; 
    PyObject *pyExcTraceback; 
    PyErr_Fetch(&pyExcType, &pyExcValue, &pyExcTraceback); 
    PyErr_NormalizeException(&pyExcType, &pyExcValue, &pyExcTraceback); 

    PyObject* str_exc_type = PyObject_Repr(pyExcType); 
    PyObject* pyStr = PyUnicode_AsEncodedString(str_exc_type, "utf-8", "Error ~"); 
    const char *strExcType = PyBytes_AS_STRING(pyStr); 

    PyObject* str_exc_value = PyObject_Repr(pyExcValue); 
    PyObject* pyExcValueStr = PyUnicode_AsEncodedString(str_exc_value, "utf-8", "Error ~"); 
    const char *strExcValue = PyBytes_AS_STRING(pyExcValueStr); 

    // When using PyErr_Restore() there is no need to use Py_XDECREF for these 3 pointers 
    //PyErr_Restore(pyExcType, pyExcValue, pyExcTraceback); 

    Py_XDECREF(pyExcType); 
    Py_XDECREF(pyExcValue); 
    Py_XDECREF(pyExcTraceback); 

    Py_XDECREF(str_exc_type); 
    Py_XDECREF(pyStr); 

    Py_XDECREF(str_exc_value); 
    Py_XDECREF(pyExcValueStr); 
} 
0

आप पाइथन ट्रैसबैक tb_printinternal समारोह के लिए इसी तरह उपयोग कर सकते हैं। यह PyTracebackObject सूची से अधिक है। मैंने फ्रेम पर फिर से चलाने के लिए ऊपर दिए गए सुझावों का भी प्रयास किया है, लेकिन यह मेरे लिए काम नहीं करता है (मुझे केवल अंतिम स्टैक फ्रेम दिखाई देता है)। CPython कोड से

अंशः

static int 
tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name) 
{ 
    int err; 
    PyObject *line; 

    if (filename == NULL || name == NULL) 
     return -1; 
    line = PyUnicode_FromFormat(" File \"%U\", line %d, in %U\n", 
           filename, lineno, name); 
    if (line == NULL) 
     return -1; 
    err = PyFile_WriteObject(line, f, Py_PRINT_RAW); 
    Py_DECREF(line); 
    if (err != 0) 
     return err; 
    /* ignore errors since we can't report them, can we? */ 
    if (_Py_DisplaySourceLine(f, filename, lineno, 4)) 
     PyErr_Clear(); 
    return err; 
} 

static int 
tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit) 
{ 
    int err = 0; 
    long depth = 0; 
    PyTracebackObject *tb1 = tb; 
    while (tb1 != NULL) { 
     depth++; 
     tb1 = tb1->tb_next; 
    } 
    while (tb != NULL && err == 0) { 
     if (depth <= limit) { 
      err = tb_displayline(f, 
           tb->tb_frame->f_code->co_filename, 
           tb->tb_lineno, 
           tb->tb_frame->f_code->co_name); 
     } 
     depth--; 
     tb = tb->tb_next; 
     if (err == 0) 
      err = PyErr_CheckSignals(); 
    } 
    return err; 
} 
संबंधित मुद्दे