yt-project / libyt

In-situ analysis with yt
https://libyt.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
9 stars 3 forks source link

Bug: Cannot Use `"` When Calling Python Function using API #72

Closed cindytsai closed 1 year ago

cindytsai commented 1 year ago

Bug: Cannot Use " When Calling Python Function using API

This is caused by I'm wrapping commands using ", then call exec to run the job. (Don't know why I'm doing this, probably because this is the least changed made compare to the old version. Actually, the previous method works just fine, my bad decision :upside_down_face:. Totally forgot to consider this while fixing another bug. )

Won't affect API.

Method 1 -- Use Triple Quotes

To Fix

libyt Dev Notes

Method 2 -- Change to PyEval_EvalCode

After rethinking and testing for some time, I don't think we need it. Because it is unlikely that people will pass in triple quotes in function arguments, and there is always an alternatives.

Besides, while I was implementing it, I get some unwanted results. (See comment) So I decided to move this for later updates and enhancements.

Move implementing method 2 to

To Fix

Test

Test again ... :smiling_face_with_tear:

Check

cindytsai commented 1 year ago

:yarn: I think we don't necessarily need to use PyEval_EvalCode at here, since there is alternatives and nobody will pass """ as function arguments. So I'm just putting some tests and code snippets while trying to use PyEval_EvalCode to solve the problem.

This is just a code snippet trying to extract traceback stack error message using Python C API PrintPyError_C_API, or let python handles it PrintError_Python. (I didn't free some of their reference, since this is just a snippet.)

Notes

Using PyEval_EvalCode to run compiled object, and use the following to print the error msg:

Code

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <string>
#include <iostream>

// global
PyObject *g_py_namespace;
PyObject *g_err_msg_dict;

static void PrintCommand(const std::string& command)
{
    std::cout << "===============================" << std::endl;
    std::cout << "COMMAND : " << command << std::endl;
}

static void PrintPyError_C_API()
{
    // fetch error
    PyObject *py_exc, *py_val, *py_traceback;
    PyErr_Fetch(&py_exc, &py_val, &py_traceback); // steal ref

    // importing traceback
    PyObject *py_module_name = PyUnicode_FromString("traceback");
    PyObject *py_module_traceback = PyImport_Import(py_module_name); // new ref
    Py_DECREF(py_module_name);
    std::cout << "#FLAG --> Import " << PyUnicode_AsUTF8(PyObject_GetAttrString(py_module_traceback, "__name__")) << std::endl;

    // getting traceback.format_exception()
    PyObject *py_format_exception = PyObject_GetAttrString(py_module_traceback, "format_exception"); // new ref
    if (py_format_exception && PyCallable_Check(py_format_exception))
    {
        // call traceback.format_exception()
        PyObject *py_args = PyTuple_New(3); // new ref
        PyTuple_SetItem(py_args, 0, py_exc);
        PyTuple_SetItem(py_args, 1, py_val);
        PyTuple_SetItem(py_args, 2, py_traceback);

        PyObject *py_err_value = PyObject_CallObject(py_format_exception, py_args);  // new ref

        std::cout << "+++++++++++++++++++++++++++++" << std::endl;
        if (py_err_value != NULL) 
        {
            std::cout << "py_err_value is not NULL" << std::endl;
        }
        else 
        {
            std::cout << "py_err_value is NULL" << std::endl;
        }
        std::cout << "+++++++++++++++++++++++++++++" << std::endl;
    }

}

static void PrintPyError_Python()
{
    std::cout << "++++++++++++++++++++++++++++++" << std::endl;
    PyRun_SimpleString("print('#FLAG --> globals = ', globals());");
    const char *command = "import traceback; print(traceback.format_exc());";
    PyRun_SimpleString(command);
    std::cout << "++++++++++++++++++++++++++++++" << std::endl;
}

static void RunPython(const std::string& code_str) 
{
    PrintCommand(code_str);
    PyObject *py_src = Py_CompileString(code_str.c_str(), "<libyt API>", Py_single_input);
    PyObject *py_dum;
    if (py_src != NULL) {
        py_dum = PyEval_EvalCode(py_src, g_py_namespace, g_py_namespace);
        if (PyErr_Occurred()) 
        {
            PrintPyError_Python();
        }
    }
    else 
    {
        PrintPyError_Python();
    }

    std::cout << "=============================" << std::endl;
}

int main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();

    // Getting sys.modules using Python C API
    PyRun_SimpleString("import sys; sys.path.insert(0, '.'); import inline_script;");
    PyObject *py_sys = PyImport_AddModule("sys"); // borrowed ref
    PyObject *py_sys_module = PyDict_GetItemString(PyModule_GetDict(py_sys), "modules"); // both are borrowed ref
    g_py_namespace = PyModule_GetDict(PyDict_GetItemString(py_sys_module, "inline_script")); // borrowed ref

    // Compile and run code under this namespace [temporary: this is only for reproducing error msg]
    std::string code_str;

    // Handling Errors -- inside inline script itself
    // It will get the wrong line number if the imported script has been changed.
    // int temp_input;
    // std::cin >> temp_input;
    code_str = std::string("func()");
    RunPython(code_str);

    PyRun_SimpleString("print('HERE')");

    // Exit python
    Py_Finalize();

    return 0;
}
Output
===============================
COMMAND : func()
I'm inside inline_script.py
++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++
=============================
Traceback (most recent call last):
  File "<libyt API>", line 1, in <module>
  File "/home/cindytsai/Documents/GitHub/EmbeddedPython38/LoadModule/./inline_script.py", line 3, in func
    print(nothing)
NameError: name 'nothing' is not defined
Desire Output
===============================
COMMAND : func()
I'm inside inline_script.py
++++++++++++++++++++++++++++++
(Error Msg)
++++++++++++++++++++++++++++++
=============================
cindytsai commented 1 year ago

Fixed. #73