FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Simplify tkagg C extension. by anntzer · Pull Request #10936 · matplotlib/matplotlib · GitHub

Repository navigation

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .cpp  (1) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
116 changes: 38 additions & 78 deletions src/_tkagg.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

#10680 is merged now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

What error is set here instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

good catch... fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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);
}

Back | FazBrowse Home | New Git URL