To: vim_dev@googlegroups.com Subject: Patch 8.2.4317 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4317 Problem: MS-Windows: Vim exits when Python 3 initialisation fails. Solution: Hook into the exit() function to recover from the failure. (Ken Takata, closes #9710) Files: runtime/doc/if_pyth.txt, src/if_python3.c, src/os_win32.c, src/errors.h, src/proto/os_win32.pro *** ../vim-8.2.4316/runtime/doc/if_pyth.txt 2020-04-14 19:15:45.284566193 +0100 --- runtime/doc/if_pyth.txt 2022-02-07 13:29:13.758512584 +0000 *************** *** 63,68 **** --- 63,73 ---- There is no need to import sys, it's done by default. + *python-environment* + Environment variables set in Vim are not always available in Python. This + depends on how Vim and Python were build. Also see + https://docs.python.org/3/library/os.html#os.environ + Note: Python is very sensitive to the indenting. Make sure the "class" line and "EOF" do not have any indent. *************** *** 829,834 **** --- 834,852 ---- Raising SystemExit exception in python isn't endorsed way to quit vim, use: > :py vim.command("qall!") < + *E1266* + This error can occur when python 3 cannot load the required modules. This + means that your python 3 is not correctly installed or there are some mistakes + in your settings. Please check the following items: + 1. Make sure that python 3 is correctly installed. Also check the version of + python. + 2. Check the 'pythonthreedll' option. + 3. Check the 'pythonthreehome' option. + 4. Check the PATH environment variable if you don't set 'pythonthreedll'. + On MS-Windows, you can use where.exe to check which dll will be loaded. + E.g. > + where.exe python310.dll + 5. Check the PYTHONPATH and PYTHONHOME environment variables. *has-python* You can test what Python version is available with: > *** ../vim-8.2.4316/src/if_python3.c 2022-01-05 17:49:10.877225131 +0000 --- src/if_python3.c 2022-02-07 13:41:01.885775623 +0000 *************** *** 112,123 **** typedef PySliceObject PySliceObject_T; #endif #if defined(DYNAMIC_PYTHON3) || defined(PROTO) # ifndef MSWIN # include # define FARPROC void* - # define HINSTANCE void* # if defined(PY_NO_RTLD_GLOBAL) && defined(PY3_NO_RTLD_GLOBAL) # define load_dll(n) dlopen((n), RTLD_LAZY) # else --- 112,129 ---- typedef PySliceObject PySliceObject_T; #endif + #ifndef MSWIN + # define HINSTANCE void * + #endif + #if defined(DYNAMIC_PYTHON3) || defined(MSWIN) + static HINSTANCE hinstPy3 = 0; // Instance of python.dll + #endif + #if defined(DYNAMIC_PYTHON3) || defined(PROTO) # ifndef MSWIN # include # define FARPROC void* # if defined(PY_NO_RTLD_GLOBAL) && defined(PY3_NO_RTLD_GLOBAL) # define load_dll(n) dlopen((n), RTLD_LAZY) # else *************** *** 459,466 **** static void(*py3_PyObject_GC_UnTrack)(void *); static int (*py3_PyType_IsSubtype)(PyTypeObject *, PyTypeObject *); - static HINSTANCE hinstPy3 = 0; // Instance of python.dll - // Imported exception objects static PyObject *p3imp_PyExc_AttributeError; static PyObject *p3imp_PyExc_IndexError; --- 465,470 ---- *************** *** 1032,1044 **** { FILE *(*py__acrt_iob_func)(unsigned) = NULL; FILE *(*pyfreopen)(const char *, const char *, FILE *) = NULL; ! HINSTANCE hinst; - # ifdef DYNAMIC_PYTHON3 - hinst = hinstPy3; - # else - hinst = GetModuleHandle(PYTHON3_DLL); - # endif if (hinst == NULL || is_stdin_readable()) return; --- 1036,1043 ---- { FILE *(*py__acrt_iob_func)(unsigned) = NULL; FILE *(*pyfreopen)(const char *, const char *, FILE *) = NULL; ! HINSTANCE hinst = hinstPy3; if (hinst == NULL || is_stdin_readable()) return; *************** *** 1063,1068 **** --- 1062,1118 ---- # define reset_stdin() #endif + // Python 3.2 or later will abort inside Py_Initialize() when mandatory + // modules cannot be loaded (e.g. 'pythonthreehome' is wrongly set.). + // Install a hook to python dll's exit() and recover from it. + #if defined(MSWIN) && (PY_VERSION_HEX >= 0x030200f0) + # define HOOK_EXIT + # include + + static jmp_buf exit_hook_jump_buf; + static void *orig_exit = NULL; + + /* + * Function that replaces exit() while calling Py_Initialize(). + */ + static void + hooked_exit(int ret) + { + // Recover from exit. + longjmp(exit_hook_jump_buf, 1); + } + + /* + * Install a hook to python dll's exit(). + */ + static void + hook_py_exit(void) + { + HINSTANCE hinst = hinstPy3; + + if (hinst == NULL || orig_exit != NULL) + return; + + orig_exit = hook_dll_import_func(hinst, "exit", (void *)hooked_exit); + } + + /* + * Remove the hook installed by hook_py_exit(). + */ + static void + restore_py_exit(void) + { + HINSTANCE hinst = hinstPy3; + + if (hinst == NULL) + return; + + if (orig_exit != NULL) + hook_dll_import_func(hinst, "exit", orig_exit); + orig_exit = NULL; + } + #endif + static int Python3_Init(void) { *************** *** 1095,1102 **** PyImport_AppendInittab("vim", Py3Init_vim); reset_stdin(); ! Py_Initialize(); #if PY_VERSION_HEX < 0x03090000 // Initialise threads. This is deprecated since Python 3.9. --- 1145,1175 ---- PyImport_AppendInittab("vim", Py3Init_vim); + #if !defined(DYNAMIC_PYTHON3) && defined(MSWIN) + hinstPy3 = GetModuleHandle(PYTHON3_DLL); + #endif reset_stdin(); ! ! #ifdef HOOK_EXIT ! // Catch exit() called in Py_Initialize(). ! hook_py_exit(); ! if (setjmp(exit_hook_jump_buf) == 0) ! #endif ! { ! Py_Initialize(); ! #ifdef HOOK_EXIT ! restore_py_exit(); ! #endif ! } ! #ifdef HOOK_EXIT ! else ! { ! // exit() was called in Py_Initialize(). ! restore_py_exit(); ! emsg(_(e_critical_error_in_python3_initialization_check_your_installation)); ! goto fail; ! } ! #endif #if PY_VERSION_HEX < 0x03090000 // Initialise threads. This is deprecated since Python 3.9. *** ../vim-8.2.4316/src/os_win32.c 2022-02-04 10:45:34.944240854 +0000 --- src/os_win32.c 2022-02-07 13:29:13.758512584 +0000 *************** *** 572,585 **** } #endif ! #if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO) /* * Get related information about 'funcname' which is imported by 'hInst'. * If 'info' is 0, return the function address. * If 'info' is 1, return the module name which the function is imported from. */ static void * ! get_imported_func_info(HINSTANCE hInst, const char *funcname, int info) { PBYTE pImage = (PBYTE)hInst; PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst; --- 572,589 ---- } #endif ! #if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) \ ! || defined(FEAT_PYTHON3) || defined(PROTO) /* * Get related information about 'funcname' which is imported by 'hInst'. * If 'info' is 0, return the function address. * If 'info' is 1, return the module name which the function is imported from. + * If 'info' is 2, hook the function with 'ptr', and return the original + * function address. */ static void * ! get_imported_func_info(HINSTANCE hInst, const char *funcname, int info, ! const void *ptr) { PBYTE pImage = (PBYTE)hInst; PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst; *************** *** 611,622 **** --- 615,637 ---- + (UINT_PTR)(pINT->u1.AddressOfData)); if (strcmp((char *)pImpName->Name, funcname) == 0) { + void *original; + DWORD old, new = PAGE_READWRITE; + switch (info) { case 0: return (void *)pIAT->u1.Function; case 1: return (void *)(pImage + pImpDesc->Name); + case 2: + original = (void *)pIAT->u1.Function; + VirtualProtect(&pIAT->u1.Function, sizeof(void *), + new, &old); + pIAT->u1.Function = (UINT_PTR)ptr; + VirtualProtect(&pIAT->u1.Function, sizeof(void *), + old, &new); + return original; default: return NULL; } *************** *** 634,640 **** { char *modulename; ! modulename = (char *)get_imported_func_info(hInst, funcname, 1); if (modulename != NULL) return GetModuleHandleA(modulename); return NULL; --- 649,655 ---- { char *modulename; ! modulename = (char *)get_imported_func_info(hInst, funcname, 1, NULL); if (modulename != NULL) return GetModuleHandleA(modulename); return NULL; *************** *** 646,652 **** void * get_dll_import_func(HINSTANCE hInst, const char *funcname) { ! return get_imported_func_info(hInst, funcname, 0); } #endif --- 661,677 ---- void * get_dll_import_func(HINSTANCE hInst, const char *funcname) { ! return get_imported_func_info(hInst, funcname, 0, NULL); ! } ! ! /* ! * Hook the function named 'funcname' which is imported by 'hInst' DLL, ! * and return the original function address. ! */ ! void * ! hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook) ! { ! return get_imported_func_info(hInst, funcname, 2, hook); } #endif *** ../vim-8.2.4316/src/errors.h 2022-02-03 20:09:15.507340313 +0000 --- src/errors.h 2022-02-07 13:35:16.546142365 +0000 *************** *** 3224,3226 **** --- 3224,3230 ---- EXTERN char e_cannot_use_partial_here[] INIT(= N_("E1265: Cannot use a partial here")); #endif + #if defined(FEAT_PYTHON3) && defined(MSWIN) + EXTERN char e_critical_error_in_python3_initialization_check_your_installation[] + INIT(= N_("E1266: Critical error in python3 initialization, check your python3 installation")); + #endif *** ../vim-8.2.4316/src/proto/os_win32.pro 2022-01-24 11:23:59.859900461 +0000 --- src/proto/os_win32.pro 2022-02-07 13:29:13.758512584 +0000 *************** *** 3,8 **** --- 3,9 ---- int mch_is_gui_executable(void); HINSTANCE find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname); void *get_dll_import_func(HINSTANCE hInst, const char *funcname); + void *hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook); int dyn_libintl_init(void); void dyn_libintl_end(void); void PlatformId(void); *** ../vim-8.2.4316/src/version.c 2022-02-07 10:45:12.803027327 +0000 --- src/version.c 2022-02-07 13:53:13.068980795 +0000 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 4317, /**/ -- hundred-and-one symptoms of being an internet addict: 20. When looking at a pageful of someone else's links, you notice all of them are already highlighted in purple. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///