To: vim_dev@googlegroups.com
Subject: Patch 8.1.0201
Fcc: outbox
From: Bram Moolenaar <Bram@moolenaar.net>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
------------

Patch 8.1.0201
Problem:    Newer Python uses "importlib" instead of "imp".
Solution:   Use "importlib" for newer Python versions. (closes #3163)
Files:      src/if_py_both.h, src/testdir/test87.in


*** ../vim-8.1.0200/src/if_py_both.h	2018-07-08 17:18:58.420462346 +0200
--- src/if_py_both.h	2018-07-22 04:05:36.131054785 +0200
***************
*** 88,95 ****
--- 88,99 ----
  static PyObject *vim_module;
  static PyObject *vim_special_path_object;
  
+ #if PY_VERSION_HEX >= 0x030700f0
+ static PyObject *py_find_spec;
+ #else
  static PyObject *py_find_module;
  static PyObject *py_load_module;
+ #endif
  
  static PyObject *VimError;
  
***************
*** 539,544 ****
--- 543,549 ----
      return 0;
  }
  
+ #if PY_VERSION_HEX < 0x030700f0
  typedef struct
  {
      PyObject_HEAD
***************
*** 567,572 ****
--- 572,578 ----
      {"load_module", (PyCFunction)LoaderLoadModule,	METH_VARARGS,	""},
      { NULL,	    NULL,				0,		NULL}
  };
+ #endif
  
  /* Check to see whether a Vim error has been reported, or a keyboard
   * interrupt has been detected.
***************
*** 1163,1168 ****
--- 1169,1205 ----
      return ret;
  }
  
+ #if PY_VERSION_HEX >= 0x030700f0
+     static PyObject *
+ FinderFindSpec(PyObject *self, PyObject *args)
+ {
+     char	*fullname;
+     PyObject	*paths;
+     PyObject	*target = Py_None;
+     PyObject	*spec;
+ 
+     if (!PyArg_ParseTuple(args, "s|O", &fullname, &target))
+ 	return NULL;
+ 
+     if (!(paths = Vim_GetPaths(self)))
+ 	return NULL;
+ 
+     spec = PyObject_CallFunction(py_find_spec, "sNN", fullname, paths, target);
+ 
+     Py_DECREF(paths);
+ 
+     if (!spec)
+     {
+ 	if (PyErr_Occurred())
+ 	    return NULL;
+ 
+ 	Py_INCREF(Py_None);
+ 	return Py_None;
+     }
+ 
+     return spec;
+ }
+ #else
      static PyObject *
  call_load_module(char *name, int len, PyObject *find_module_result)
  {
***************
*** 1305,1310 ****
--- 1342,1348 ----
  
      return (PyObject *) loader;
  }
+ #endif
  
      static PyObject *
  VimPathHook(PyObject *self UNUSED, PyObject *args)
***************
*** 1336,1342 ****
--- 1374,1384 ----
      {"chdir",	    (PyCFunction)VimChdir,	METH_VARARGS|METH_KEYWORDS,	"Change directory"},
      {"fchdir",	    (PyCFunction)VimFchdir,	METH_VARARGS|METH_KEYWORDS,	"Change directory"},
      {"foreach_rtp", VimForeachRTP,		METH_O,				"Call given callable for each path in &rtp"},
+ #if PY_VERSION_HEX >= 0x030700f0
+     {"find_spec",   FinderFindSpec,		METH_VARARGS,			"Internal use only, returns spec object for any input it receives"},
+ #else
      {"find_module", FinderFindModule,		METH_VARARGS,			"Internal use only, returns loader object for any input it receives"},
+ #endif
      {"path_hook",   VimPathHook,		METH_VARARGS,			"Hook function to install in sys.path_hooks"},
      {"_get_paths",  (PyCFunction)Vim_GetPaths,	METH_NOARGS,			"Get &rtp-based additions to sys.path"},
      { NULL,	    NULL,			0,				NULL}
***************
*** 6545,6550 ****
--- 6587,6593 ----
      OptionsType.tp_traverse = (traverseproc)OptionsTraverse;
      OptionsType.tp_clear = (inquiry)OptionsClear;
  
+ #if PY_VERSION_HEX < 0x030700f0
      vim_memset(&LoaderType, 0, sizeof(LoaderType));
      LoaderType.tp_name = "vim.Loader";
      LoaderType.tp_basicsize = sizeof(LoaderObject);
***************
*** 6552,6557 ****
--- 6595,6601 ----
      LoaderType.tp_doc = "vim message object";
      LoaderType.tp_methods = LoaderMethods;
      LoaderType.tp_dealloc = (destructor)LoaderDestructor;
+ #endif
  
  #if PY_MAJOR_VERSION >= 3
      vim_memset(&vimmodule, 0, sizeof(vimmodule));
***************
*** 6583,6589 ****
--- 6627,6635 ----
      PYTYPE_READY(FunctionType);
      PYTYPE_READY(OptionsType);
      PYTYPE_READY(OutputType);
+ #if PY_VERSION_HEX < 0x030700f0
      PYTYPE_READY(LoaderType);
+ #endif
      return 0;
  }
  
***************
*** 6707,6713 ****
--- 6753,6761 ----
      {"List",       (PyObject *)&ListType},
      {"Function",   (PyObject *)&FunctionType},
      {"Options",    (PyObject *)&OptionsType},
+ #if PY_VERSION_HEX < 0x030700f0
      {"_Loader",    (PyObject *)&LoaderType},
+ #endif
  };
  
  #define ADD_OBJECT(m, name, obj) \
***************
*** 6729,6734 ****
--- 6777,6786 ----
      PyObject	*other_module;
      PyObject	*attr;
      PyObject	*imp;
+ #if PY_VERSION_HEX >= 0x030700f0
+     PyObject	*dict;
+     PyObject	*cls;
+ #endif
  
      for (i = 0; i < (int)(sizeof(numeric_constants)
  					   / sizeof(struct numeric_constant));
***************
*** 6801,6806 ****
--- 6853,6880 ----
  
      ADD_OBJECT(m, "VIM_SPECIAL_PATH", vim_special_path_object);
  
+ #if PY_VERSION_HEX >= 0x030700f0
+     if (!(imp = PyImport_ImportModule("importlib.machinery")))
+ 	return -1;
+ 
+     dict = PyModule_GetDict(imp);
+ 
+     if (!(cls = PyDict_GetItemString(dict, "PathFinder")))
+     {
+ 	Py_DECREF(imp);
+ 	return -1;
+     }
+ 
+     if (!(py_find_spec = PyObject_GetAttrString(cls, "find_spec")))
+     {
+ 	Py_DECREF(imp);
+ 	return -1;
+     }
+ 
+     Py_DECREF(imp);
+ 
+     ADD_OBJECT(m, "_find_spec", py_find_spec);
+ #else
      if (!(imp = PyImport_ImportModule("imp")))
  	return -1;
  
***************
*** 6821,6826 ****
--- 6895,6901 ----
  
      ADD_OBJECT(m, "_find_module", py_find_module);
      ADD_OBJECT(m, "_load_module", py_load_module);
+ #endif
  
      return 0;
  }
*** ../vim-8.1.0200/src/testdir/test87.in	2017-03-05 18:33:37.000000000 +0100
--- src/testdir/test87.in	2018-07-22 04:05:36.131054785 +0200
***************
*** 219,224 ****
--- 219,225 ----
  import re
  
  py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
+ py37_exception_repr = re.compile(r'([^\(\),])(\)+)$')
  
  def ee(expr, g=globals(), l=locals()):
      cb = vim.current.buffer
***************
*** 227,243 ****
              exec(expr, g, l)
          except Exception as e:
              if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."):
!                 cb.append(expr + ':' + repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1]))))
              elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0:
!                 cb.append(expr + ':' + repr((e.__class__, ImportError(str(e).replace("'", '')))))
              elif sys.version_info >= (3, 6) and e.__class__ is ModuleNotFoundError:
                  # Python 3.6 gives ModuleNotFoundError, change it to an ImportError
!                 cb.append(expr + ':' + repr((ImportError, ImportError(str(e).replace("'", '')))))
              elif sys.version_info >= (3, 3) and e.__class__ is TypeError:
                  m = py33_type_error_pattern.search(str(e))
                  if m:
                      msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2))
!                     cb.append(expr + ':' + repr((e.__class__, TypeError(msg))))
                  else:
                      msg = repr((e.__class__, e))
                      # Messages changed with Python 3.6, change new to old.
--- 228,244 ----
              exec(expr, g, l)
          except Exception as e:
              if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."):
!                 msg = repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1])))
              elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0:
!                 msg = repr((e.__class__, ImportError(str(e).replace("'", ''))))
              elif sys.version_info >= (3, 6) and e.__class__ is ModuleNotFoundError:
                  # Python 3.6 gives ModuleNotFoundError, change it to an ImportError
!                 msg = repr((ImportError, ImportError(str(e).replace("'", ''))))
              elif sys.version_info >= (3, 3) and e.__class__ is TypeError:
                  m = py33_type_error_pattern.search(str(e))
                  if m:
                      msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2))
!                     msg = repr((e.__class__, TypeError(msg)))
                  else:
                      msg = repr((e.__class__, e))
                      # Messages changed with Python 3.6, change new to old.
***************
*** 249,257 ****
                      oldmsg2 = '''"Can't convert 'int' object to str implicitly"'''
                      if msg.find(newmsg2) > -1:
                          msg = msg.replace(newmsg2, oldmsg2)
-                     cb.append(expr + ':' + msg)
              elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
!                 cb.append(expr + ':' + repr((TypeError, TypeError('expected bytes with no null'))))
              else:
                  msg = repr((e.__class__, e))
                  # Some Python versions say can't, others cannot.
--- 250,257 ----
                      oldmsg2 = '''"Can't convert 'int' object to str implicitly"'''
                      if msg.find(newmsg2) > -1:
                          msg = msg.replace(newmsg2, oldmsg2)
              elif sys.version_info >= (3, 5) and e.__class__ is ValueError and str(e) == 'embedded null byte':
!                 msg = repr((TypeError, TypeError('expected bytes with no null')))
              else:
                  msg = repr((e.__class__, e))
                  # Some Python versions say can't, others cannot.
***************
*** 262,272 ****
                      msg = msg.replace('"cannot ', '\'cannot ')
                  if msg.find(' attributes"') > -1:
                      msg = msg.replace(' attributes"', ' attributes\'')
!                 cb.append(expr + ':' + msg)
          else:
              cb.append(expr + ':NOT FAILED')
      except Exception as e:
!         cb.append(expr + '::' + repr((e.__class__, e)))
  EOF
  :fun New(...)
  :   return ['NewStart']+a:000+['NewEnd']
--- 262,277 ----
                      msg = msg.replace('"cannot ', '\'cannot ')
                  if msg.find(' attributes"') > -1:
                      msg = msg.replace(' attributes"', ' attributes\'')
!             if sys.version_info >= (3, 7):
!                 msg = py37_exception_repr.sub(r'\1,\2', msg)
!             cb.append(expr + ':' + msg)
          else:
              cb.append(expr + ':NOT FAILED')
      except Exception as e:
!         msg = repr((e.__class__, e))
!         if sys.version_info >= (3, 7):
!             msg = py37_exception_repr.sub(r'\1,\2', msg)
!         cb.append(expr + '::' + msg)
  EOF
  :fun New(...)
  :   return ['NewStart']+a:000+['NewEnd']
*** ../vim-8.1.0200/src/version.c	2018-07-20 23:36:21.171368602 +0200
--- src/version.c	2018-07-22 04:24:39.197154869 +0200
***************
*** 791,792 ****
--- 791,794 ----
  {   /* Add new patch number below this line */
+ /**/
+     201,
  /**/

-- 
FIRST HEAD: All right! All right! We'll kill him first and then have tea and
            biscuits.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///