From c397ef1f267580dd03137c00730c56dad3a751f3 Mon Sep 17 00:00:00 2001 From: Kelvin Rodriguez Date: Thu, 13 Dec 2018 16:35:32 -0700 Subject: [PATCH] load now throw exceptions with python tracebacks --- src/ale.cpp | 67 +++++++++++++++------------ tests/ctests/AleTest.cpp | 98 ++-------------------------------------- 2 files changed, 41 insertions(+), 124 deletions(-) diff --git a/src/ale.cpp b/src/ale.cpp index 52d77d3..ac9c81e 100644 --- a/src/ale.cpp +++ b/src/ale.cpp @@ -262,30 +262,37 @@ namespace ale { str = PyBytes_AS_STRING(PyUnicode_AsUTF8String(pystr)); error_description = strdup(str); - /* See if we can get a full traceback */ + // See if we can get a full traceback module_name = PyUnicode_FromString("traceback"); pyth_module = PyImport_Import(module_name); Py_DECREF(module_name); if (pyth_module == NULL) { - return "Pyth_Module Empty"; + throw runtime_error("getPyTraceback - Failed to import Python traceback Library"); } pyth_func = PyObject_GetAttrString(pyth_module, "format_exception"); - if (pyth_func && PyCallable_Check(pyth_func)) { - PyObject *pyth_val; + PyObject *pyth_val; + pyth_val = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL); - pyth_val = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL); + pystr = PyObject_Str(pyth_val); + str = PyBytes_AS_STRING(PyUnicode_AsUTF8String(pystr)); + full_backtrace = strdup(str); + Py_DECREF(pyth_val); - pystr = PyObject_Str(pyth_val); - str = PyBytes_AS_STRING(PyUnicode_AsUTF8String(pystr)); - full_backtrace = strdup(str); - Py_DECREF(pyth_val); - return std::string(full_backtrace); - } - return "End of if statement"; + std::string join_cmd = "trace = ''.join(list(" + std::string(full_backtrace) + "))"; + PyRun_SimpleString(join_cmd.c_str()); + + PyObject *evalModule = PyImport_AddModule( (char*)"__main__" ); + PyObject *evalDict = PyModule_GetDict( evalModule ); + PyObject *evalVal = PyDict_GetItemString( evalDict, "trace" ); + full_backtrace = PyBytes_AS_STRING(PyUnicode_AsUTF8String(evalVal)); + + return std::string(error_description) + "\n" + std::string(full_backtrace); } - return "No Pyerror"; + + // no traceback to return + return ""; } std::string load(std::string filename) { @@ -300,9 +307,7 @@ namespace ale { // Import the file as a Python module. PyObject *pModule = PyImport_Import(PyUnicode_FromString("ale")); if(!pModule) { - std::cout << "Error in import module " << std::endl; - - throw getPyTraceback(); + throw runtime_error(getPyTraceback()); } // Create a dictionary for the contents of the module. PyObject *pDict = PyModule_GetDict(pModule); @@ -310,15 +315,17 @@ namespace ale { // Get the add method from the dictionary. PyObject *pFunc = PyDict_GetItemString(pDict, "loads"); if(!pFunc) { - std::cout << "Error in getting func " << std::endl; - return getPyTraceback(); + // import errors do not set a PyError flag, need to use a custom + // error message instead. + throw runtime_error("Failed to import ale.loads function from Python." + "This Usually indicates an error in the Ale Python Library." + "Check if Installed correctly and the function ale.loads exists."); } // Create a Python tuple to hold the arguments to the method. PyObject *pArgs = PyTuple_New(1); if(!pArgs) { - std::cout << "Error in creating args " << std::endl; - return getPyTraceback(); + throw runtime_error(getPyTraceback()); } // Set the Python int as the first and second arguments to the method. @@ -328,21 +335,23 @@ namespace ale { // Call the function with the arguments. PyObject* pResult = PyObject_CallObject(pFunc, pArgs); if(!pResult) { - std::cout << "Error in call " << std::endl; - return getPyTraceback(); + throw invalid_argument(getPyTraceback()); } std::string cResult; - PyObject *temp_bytes = PyUnicode_AsUTF8String(pResult); // Owned reference - if (temp_bytes != NULL) { - char *temp_str = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer - cResult = temp_str; // copy into std::string - Py_DECREF(temp_str); - } else { - return getPyTraceback(); + // use PyObject_Str to ensure return is always a string + PyObject *pResultStr = PyObject_Str(pResult); + PyObject *temp_bytes = PyUnicode_AsUTF8String(pResultStr); // Owned reference + if(!temp_bytes){ + throw invalid_argument(getPyTraceback()); } + char *temp_str = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer + cResult = temp_str; // copy into std::string + Py_DECREF(temp_str); + + Py_DECREF(pResultStr); Py_DECREF(temp_bytes); Py_DECREF(pArgs); Py_DECREF(pModule); diff --git a/tests/ctests/AleTest.cpp b/tests/ctests/AleTest.cpp index 3fb2bf6..e3dd247 100644 --- a/tests/ctests/AleTest.cpp +++ b/tests/ctests/AleTest.cpp @@ -251,101 +251,9 @@ TEST(LinearInterpTest, GetRotationDifferentCounts) { vector times = {0, 1, 2}; vector> rots({{1,1,1,1}, {0,0,0,0}, {1,1,1,1}, {0,0,0,0}}); EXPECT_THROW(ale::getRotation(rots, times, 2, ale::linear), invalid_argument); - } -TEST(PyInterfaceTest, PyErrors) { - std::string test_lro_label = - "PDS_VERSION_ID = PDS3\n" - "\n" - "/*FILE CHARACTERISTICS*/\n" - "RECORD_TYPE = FIXED_LENGTH\n" - "RECORD_BYTES = 5064\n" - "FILE_RECORDS = 13313\n" - "LABEL_RECORDS = 1\n" - "^IMAGE = 2\n" - "\n" - "/*DATA IDENTIFICATION*/\n" - "DATA_SET_ID = \"LRO-L-LROC-2-EDR-V1.0\"\n" - "ORIGINAL_PRODUCT_ID = nacl0002fc60\n" - "PRODUCT_ID = M128963531LE\n" - "MISSION_NAME = \"LUNAR RECONNAISSANCE ORBITER\"\n" - "MISSION_PHASE_NAME = \"NOMINAL MISSION\"\n" - "INSTRUMENT_HOST_NAME = \"LUNAR RECONNAISSANCE ORBITER\"\n" - "INSTRUMENT_HOST_ID = LRO\n" - "INSTRUMENT_NAME = \"LUNAR RECONNAISSANCE ORBITER CAMERA\"\n" - "INSTRUMENT_ID = LROC\n" - "LRO:PREROLL_TIME = 2010-05-20T02:57:44.373\n" - "START_TIME = 2010-05-20T02:57:44.720\n" - "STOP_TIME = 2010-05-20T02:57:49.235\n" - "LRO:SPACECRAFT_CLOCK_PREROLL_COUNT = \"1/296017064:22937\"\n" - "SPACECRAFT_CLOCK_START_COUNT = \"1/296017064:45694\"\n" - "SPACECRAFT_CLOCK_STOP_COUNT = \"1/296017069:13866\"\n" - "ORBIT_NUMBER = 4138\n" - "PRODUCER_ID = LRO_LROC_TEAM\n" - "PRODUCT_CREATION_TIME = 2013-09-16T19:57:12\n" - "PRODUCER_INSTITUTION_NAME = \"ARIZONA STATE UNIVERSITY\"\n" - "PRODUCT_TYPE = EDR\n" - "PRODUCT_VERSION_ID = \"v1.8\"\n" - "UPLOAD_ID = \"SC_2010140_0000_A_V01.txt\"\n" - "\n" - "/*DATA DESCRIPTION*/\n" - "TARGET_NAME = \"MOON\"\n" - "RATIONALE_DESC = \"TARGET OF OPPORTUNITY\"\n" - "FRAME_ID = LEFT\n" - "DATA_QUALITY_ID = \"0\"\n" - "DATA_QUALITY_DESC = \"The DATA_QUALITY_ID is set to an 8-bit\n" - " value that encodes the following data quality information for the\n" - " observation. For each bit a value of 0 means FALSE and a value of 1 means\n" - " TRUE. More information about the data quality ID can be found in the LROC\n" - " EDR/CDR SIS, section 3.3 'Label and Header Descriptions'.\n" - " Bit 1: Temperature of focal plane array is out of bounds.\n" - " Bit 2: Threshold for saturated pixels is reached.\n" - " Bit 3: Threshold for under-saturated pixels is reached.\n" - " Bit 4: Observation is missing telemetry packets.\n" - " Bit 5: SPICE information is bad or missing.\n" - " Bit 6: Observation or housekeeping information is bad or missing.\n" - " Bit 7: Spare.\n" - " Bit 8: Spare.\"\n" - "\n" - "/*ENVIRONMENT*/\n" - "LRO:TEMPERATURE_SCS = 4.51 \n" - "LRO:TEMPERATURE_FPA = 17.88 \n" - "LRO:TEMPERATURE_FPGA = -12.33 \n" - "LRO:TEMPERATURE_TELESCOPE = 5.91 \n" - "LRO:TEMPERATURE_SCS_RAW = 2740\n" - "LRO:TEMPERATURE_FPA_RAW = 2107\n" - "LRO:TEMPERATURE_FPGA_RAW = 3418\n" - "LRO:TEMPERATURE_TELESCOPE_RAW = 2675\n" - "\n" - "/*IMAGING PARAMETERS*/\n" - "CROSSTRACK_SUMMING = 1\n" - "BANDWIDTH = 300 \n" - "CENTER_FILTER_WAVELENGTH = 600 \n" - "LINE_EXPOSURE_DURATION = 0.337600 \n" - "LRO:LINE_EXPOSURE_CODE = 0\n" - "LRO:DAC_RESET_LEVEL = 198\n" - "LRO:CHANNEL_A_OFFSET = 60\n" - "LRO:CHANNEL_B_OFFSET = 123\n" - "LRO:COMPAND_CODE = 3\n" - "LRO:LINE_CODE = 13\n" - "LRO:BTERM = (0,16,69,103,128)\n" - "LRO:MTERM = (0.5,0.25,0.125,0.0625,0.03125)\n" - "LRO:XTERM = (0,64,424,536,800)\n" - "LRO:COMPRESSION_FLAG = 1\n" - "LRO:MODE = 7\n" - "\n" - "/*DATA OBJECT*/\n" - "OBJECT = IMAGE\n" - " LINES = 13312\n" - " LINE_SAMPLES = 5064\n" - " SAMPLE_BITS = 8\n" - " SAMPLE_TYPE = LSB_INTEGER\n" - " UNIT = \"RAW_INSTRUMENT_COUNT\"\n" - " MD5_CHECKSUM = \"0fe91f4b2e93083ee0093e7c8d05f3bc\"\n" - "END_OBJECT = IMAGE\n" - "END\n"; - std::string s = ale::load(test_lro_label); - std::cout << s << std::endl; - EXPECT_DOUBLE_EQ(1, 0); +TEST(PyInterfaceTest, LoadInvalidLabel) { + std::string label = "Not a Real Label"; + EXPECT_THROW(ale::load(label), invalid_argument); } -- GitLab