Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Simplify tkagg C extension. #10936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 15, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 38 additions & 78 deletions src/_tkagg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
}
/* get buffer from str which is "height width ptr" */
if (sscanf(argv[2], IMG_FORMAT, &hdata, &wdata, &pdata) != 3) {
TCL_APPEND_RESULT(interp,
TCL_APPEND_RESULT(interp,
"error reading data, expected height width ptr",
(char *)NULL);
return TCL_ERROR;
Expand Down Expand Up @@ -291,8 +291,7 @@ int get_tcl(HMODULE hMod)
if (TCL_CREATE_COMMAND == NULL) { // Maybe not TCL module
return 0;
}
TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(hMod,
"Tcl_AppendResult");
TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(hMod, "Tcl_AppendResult");
return (TCL_APPEND_RESULT == NULL) ? -1 : 1;
}

Expand All @@ -306,20 +305,20 @@ int get_tk(HMODULE hMod)
if (TK_MAIN_WINDOW == NULL) { // Maybe not Tk module
return 0;
}
return ( // -1 if any remaining symbols are NULL
return // -1 if any remaining symbols are NULL
((TK_FIND_PHOTO = (Tk_FindPhoto_t)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be clearer if these were a bunch of separate ifs instead of this long chained thing (if every assignment fit on one line, that would also be clearer.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'll be much simpler to do after #10680 is merged and we later get to remove the old version of the API. Then the only functions we'll have to load are Tk_FindPhoto and Tk_PhotoPutBlock_NoComposite, which also means that the Windows and non-Windows versions will only need to differ by how they call _dfunc.
Not convinced it's worth changing for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear, the endgame is to just do (e.g.)

void *_dfunc(void *lib_handle, const char *func_name);

int _func_loader(void *lib)
{
    // Fill global function pointers from dynamic lib.
    // Return number of successfully loaded functions.
    #define LOAD_TK(name) tk::name = Tk_##name##_t(_dfunc(lib, "Tk_" #name))
    return bool(LOAD_TK(FindPhoto)) + bool(LOAD_TK(PhotoPutBlock_NoComposite));
    #undef LOAD_TK
}

followed by OS-specific versions of _dfunc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#10680 is merged now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the old API is still around (the simplification will only come after matplotlib.backends.tkagg is completely removed).

_dfunc(hMod, "Tk_FindPhoto")) == NULL) ||
((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t)
_dfunc(hMod, "Tk_PhotoPutBlock_NoComposite")) == NULL) ||
((TK_PHOTO_BLANK = (Tk_PhotoBlank_t)
_dfunc(hMod, "Tk_PhotoBlank")) == NULL))
_dfunc(hMod, "Tk_PhotoBlank")) == NULL)
? -1 : 1;
}

int load_tkinter_funcs(void)
void load_tkinter_funcs(void)
{
// Load TCL and Tk functions by searching all modules in current process.
// Return 0 for success, non-zero for failure.
// Sets an error on failure.

HMODULE hMods[1024];
HANDLE hProcess;
Expand All @@ -337,17 +336,17 @@ int load_tkinter_funcs(void)
if (!found_tcl) {
found_tcl = get_tcl(hMods[i]);
if (found_tcl == -1) {
return 1;
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What error is set here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch... fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually no, there is indeed an error being set by _dfunc()

void *_dfunc(void *lib_handle, const char *func_name)
{
    // Load function `func_name` from `lib_handle`.
    // Set Python exception if we can't find `func_name` in `lib_handle`.
    // Returns function pointer or NULL if not present.

    void* func;
    // Reset errors.
    dlerror();
    func = dlsym(lib_handle, func_name);
    if (func == NULL) {
        PyErr_SetString(PyExc_RuntimeError, dlerror());  // here
    }
    return func;
}

(and similarly on Windows).

Left the original version as is.

}
}
if (!found_tk) {
found_tk = get_tk(hMods[i]);
if (found_tk == -1) {
return 1;
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or here?

}
}
if (found_tcl && found_tk) {
return 0;
return;
}
}
}
Expand All @@ -357,7 +356,7 @@ int load_tkinter_funcs(void)
} else {
PyErr_SetString(PyExc_RuntimeError, "Could not find Tk routines");
}
return 1;
return;
}

#else // not Windows
Expand All @@ -367,16 +366,6 @@ int load_tkinter_funcs(void)
* tkinter uses these symbols, and the symbols are therefore visible in the
* tkinter dynamic library (module).
*/
// From module __file__ attribute to char *string for dlopen.
char *fname2char(PyObject *fname)
{
PyObject* bytes;
bytes = PyUnicode_EncodeFSDefault(fname);
if (bytes == NULL) {
return NULL;
}
return PyBytes_AsString(bytes);
}

#include <dlfcn.h>

Expand All @@ -391,8 +380,7 @@ void *_dfunc(void *lib_handle, const char *func_name)
dlerror();
func = dlsym(lib_handle, func_name);
if (func == NULL) {
const char *error = dlerror();
PyErr_SetString(PyExc_RuntimeError, error);
PyErr_SetString(PyExc_RuntimeError, dlerror());
}
return func;
}
Expand All @@ -401,7 +389,7 @@ int _func_loader(void *lib)
{
// Fill global function pointers from dynamic lib.
// Return 1 if any pointer is NULL, 0 otherwise.
return (
return
((TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same split up here, probably.

_dfunc(lib, "Tcl_CreateCommand")) == NULL) ||
((TCL_APPEND_RESULT = (Tcl_AppendResult_t)
Expand All @@ -413,75 +401,50 @@ int _func_loader(void *lib)
((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t)
_dfunc(lib, "Tk_PhotoPutBlock_NoComposite")) == NULL) ||
((TK_PHOTO_BLANK = (Tk_PhotoBlank_t)
_dfunc(lib, "Tk_PhotoBlank")) == NULL));
_dfunc(lib, "Tk_PhotoBlank")) == NULL);
}

int load_tkinter_funcs(void)
void load_tkinter_funcs(void)
{
// Load tkinter global funcs from tkinter compiled module.
// Return 0 for success, non-zero for failure.
int ret = -1;
// Sets an error on failure.
void *main_program, *tkinter_lib;
char *tkinter_libname;
PyObject *pModule = NULL, *pSubmodule = NULL, *pString = NULL;
PyObject *module = NULL, *py_path = NULL, *py_path_b = NULL;
char *path;

// Try loading from the main program namespace first
// Try loading from the main program namespace first.
main_program = dlopen(NULL, RTLD_LAZY);
if (_func_loader(main_program) == 0) {
return 0;
goto exit;
}
// Clear exception triggered when we didn't find symbols above.
PyErr_Clear();

// Now try finding the tkinter compiled module
pModule = PyImport_ImportModule("tkinter");
if (pModule == NULL) {
goto exit;
}
pSubmodule = PyObject_GetAttrString(pModule, "_tkinter");
if (pSubmodule == NULL) {
goto exit;
// Handle PyPy first, as that import will correctly fail on CPython.
module = PyImport_ImportModule("_tkinter.tklib_cffi"); // PyPy
if (!module) {
PyErr_Clear();
module = PyImport_ImportModule("_tkinter"); // CPython
}
pString = PyObject_GetAttrString(pSubmodule, "__file__");
if (pString == NULL) {
if (!(module &&
(py_path = PyObject_GetAttrString(module, "__file__")) &&
(py_path_b = PyUnicode_EncodeFSDefault(py_path)) &&
(path = PyBytes_AsString(py_path_b)))) {
goto exit;
}
tkinter_libname = fname2char(pString);
if (tkinter_libname == NULL) {
tkinter_lib = dlopen(path, RTLD_LAZY);
if (!tkinter_lib) {
PyErr_SetString(PyExc_RuntimeError, dlerror());
goto exit;
}
tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
if (tkinter_lib == NULL) {
/* Perhaps it is a cffi module, like in PyPy? */
pString = PyObject_GetAttrString(pSubmodule, "tklib_cffi");
if (pString == NULL) {
goto fail;
}
pString = PyObject_GetAttrString(pString, "__file__");
if (pString == NULL) {
goto fail;
}
tkinter_libname = fname2char(pString);
if (tkinter_libname == NULL) {
goto fail;
}
tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
}
if (tkinter_lib == NULL) {
goto fail;
}
ret = _func_loader(tkinter_lib);
// dlclose probably safe because tkinter has been imported.
_func_loader(tkinter_lib);
// dlclose is safe because tkinter has been imported.
dlclose(tkinter_lib);
goto exit;
fail:
PyErr_SetString(PyExc_RuntimeError,
"Cannot dlopen tkinter module file");
exit:
Py_XDECREF(pModule);
Py_XDECREF(pSubmodule);
Py_XDECREF(pString);
return ret;
Py_XDECREF(module);
Py_XDECREF(py_path);
Py_XDECREF(py_path_b);
}
#endif // end not Windows

Expand All @@ -491,9 +454,6 @@ static PyModuleDef _tkagg_module = {

PyMODINIT_FUNC PyInit__tkagg(void)
{
PyObject *m;

m = PyModule_Create(&_tkagg_module);

return (load_tkinter_funcs() == 0) ? m : NULL;
load_tkinter_funcs();
return PyErr_Occurred() ? NULL : PyModule_Create(&_tkagg_module);
}