]> granicus.if.org Git - vim/commitdiff
updated for version 7.3.1172 v7.3.1172
authorBram Moolenaar <Bram@vim.org>
Wed, 12 Jun 2013 12:20:36 +0000 (14:20 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 12 Jun 2013 12:20:36 +0000 (14:20 +0200)
Problem:    Python 2: loading modules doesn't work well.
Solution:   Fix the code. Add more tests. (ZyX)

13 files changed:
Filelist
runtime/doc/if_pyth.txt
src/if_py_both.h
src/if_python.c
src/testdir/python2/module.py
src/testdir/python3/module.py
src/testdir/python_after/after.py [new file with mode: 0644]
src/testdir/python_before/before.py [new file with mode: 0644]
src/testdir/test86.in
src/testdir/test86.ok
src/testdir/test87.in
src/testdir/test87.ok
src/version.c

index 1dea3f168bd5d5d9c24b9c372c614cd46cef4b2d..7fd4f69c3d5a0e7a5a3c58f07764424d094af07b 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -87,6 +87,8 @@ SRC_ALL =     \
                src/testdir/python2/*.py \
                src/testdir/python3/*.py \
                src/testdir/pythonx/*.py \
+               src/testdir/python_after/*.py \
+               src/testdir/python_before/*.py \
                src/proto.h \
                src/proto/blowfish.pro \
                src/proto/buffer.pro \
index c54f564209d2a71db3515119c98d44769ce78e4e..b394b87021aed44f79f23095f8af55cb3fedff7f 100644 (file)
@@ -315,52 +315,53 @@ vim.path_hooks in sys.path_hooks python will try to load module from
 {rtp}/python2 (or python3) and {rtp}/pythonx (for both python versions) for 
 each {rtp} found in 'runtimepath'.
 
-Implementation for python 2 is the following: usual importing code with empty 
-lists in place of sys.path_hooks and sys.meta_path. Code is similar to the 
-below, but written in C: >
+Implementation for python 2 is similar to the following, but written in C: >
 
-    # Assuming vim variable is already accessible and is set to the current 
-    # module
+    from imp import find_module, load_module
+    import vim
     import sys
 
-    def find_module(fullname):
-        return vim
-
-    def load_module(fullname):
-        # see vim._get_paths below
-        new_path = _get_paths()
-
-        try:         old_path = sys.path
-        except: pass
-        try:         old_meta_path = sys.meta_path
-        except: pass
-        try:         old_path_hooks = sys.path_hooks
-        except: pass
-
-        sys.meta_path = []
-        sys.path_hooks = sys.meta_path
-        sys.path = new_path
-
-        try:
-            exec ('import ' + fullname + ' as m')  # No actual exec in C code
-            return m
-        finally:
-            e = None
-            try:                        sys.path = old_path
-            except Exception as e: pass
-            try:                        sys.meta_path = old_meta_path
-            except Exception as e: pass
-            try:                        sys.path_hooks = old_path_hooks
-            except Exception as e: pass
-            if e:
-                raise e
-
-    def path_hook(d):
-        if d == VIM_SPECIAL_PATH:
-            return vim
-        raise ImportError
-
-    sys.path_hooks.append(path_hook)
+    class VimModuleLoader(object):
+        def __init__(self, module):
+            self.module = module
+
+        def load_module(self, fullname, path=None):
+            return self.module
+
+    def _find_module(fullname, oldtail, path):
+        idx = oldtail.find('.')
+        if idx > 0:
+            name = oldtail[:idx]
+            tail = oldtail[idx+1:]
+            fmr = find_module(name, path)
+            module = load_module(fullname[:-len(oldtail)] + name, *fmr)
+            return _find_module(fullname, tail, module.__path__)
+        else:
+            fmr = find_module(fullname, path)
+            return load_module(fullname, *fmr)
+
+    # It uses vim module itself in place of VimPathFinder class: it does not 
+    # matter for python which object has find_module function attached to as 
+    # an attribute.
+    class VimPathFinder(object):
+        def find_module(cls, fullname, path=None):
+            try:
+                return VimModuleLoader(_find_module(fullname, fullname, path or vim._get_paths()))
+            except ImportError:
+                return None
+        find_module = classmethod(find_module)
+
+        def load_module(cls, fullname, path=None):
+            return _find_module(fullname, fullname, path or vim._get_paths())
+        load_module = classmethod(load_module)
+
+    def hook(path):
+        if path == vim.VIM_SPECIAL_PATH:
+            return VimPathFinder
+        else:
+            raise ImportError
+
+    sys.path_hooks.append(hook)
 
 Implementation for python 3 is cleaner: code is similar to the following, but, 
 again, written in C: >
@@ -395,14 +396,13 @@ vim.VIM_SPECIAL_PATH                                      *python-VIM_SPECIAL_PATH*
        Note: you must not use value of this constant directly, always use 
              vim.VIM_SPECIAL_PATH object.
 
-vim.load_module(name)                                  *python-load_module*
 vim.find_module(...)                                   *python-find_module*
 vim.path_hook(path)                                    *python-path_hook*
        Methods or objects used to implement path loading as described above. 
        You should not be using any of these directly except for vim.path_hook 
        in case you need to do something with sys.meta_path. It is not 
        guaranteed that any of the objects will exist in the future vim 
-       versions. In fact, load_module and find_module methods do not exists 
+       versions. In fact, find_module methods do not exists 
        in python3.
 
 vim._get_paths                                         *python-_get_paths*
index 1e5a151e7127c886436256e8e84d18eb90c9aa91..bd1d70434f6a27cd08b32c7528a02e486f6b4dd1 100644 (file)
@@ -940,7 +940,6 @@ static struct PyMethodDef VimMethods[] = {
     {"foreach_rtp", VimForeachRTP,             METH_VARARGS,                   "Call given callable for each path in &rtp"},
 #if PY_MAJOR_VERSION < 3
     {"find_module", FinderFindModule,          METH_VARARGS,                   "Internal use only, returns loader object for any input it receives"},
-    {"load_module", LoaderLoadModule,          METH_VARARGS,                   "Internal use only, tries importing the given module from &rtp by temporary mocking sys.path (to an rtp-based one) and unsetting sys.meta_path and sys.path_hooks"},
 #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"},
@@ -5195,6 +5194,13 @@ typedef struct
     PyObject_HEAD
 } FinderObject;
 static PyTypeObject FinderType;
+#else
+typedef struct
+{
+    PyObject_HEAD
+    PyObject   *module;
+} LoaderObject;
+static PyTypeObject LoaderType;
 #endif
 
     static void
@@ -5444,6 +5450,8 @@ init_types()
     PYTYPE_READY(OutputType);
 #if PY_MAJOR_VERSION >= 3
     PYTYPE_READY(FinderType);
+#else
+    PYTYPE_READY(LoaderType);
 #endif
     return 0;
 }
@@ -5570,6 +5578,8 @@ static struct object_constant {
     {"Options",    (PyObject *)&OptionsType},
 #if PY_MAJOR_VERSION >= 3
     {"Finder",     (PyObject *)&FinderType},
+#else
+    {"Loader",     (PyObject *)&LoaderType},
 #endif
 };
 
@@ -5666,6 +5676,9 @@ populate_module(PyObject *m, object_adder add_object, attr_getter get_attr)
     ADD_CHECKED_OBJECT(m, "_find_module",
            (py_find_module = PyObject_GetAttrString(path_finder,
                                                     "find_module")));
+#else
+    ADD_OBJECT(m, "_find_module", py_find_module);
+    ADD_OBJECT(m, "_load_module", py_load_module);
 #endif
 
     return 0;
index 09b3ca5d23202f6e25f9ee5f0a0acd39a6a55aa3..d19ef3a39b8de2b7a6ee7f8dd56e55cd9ab02ffe 100644 (file)
@@ -150,6 +150,7 @@ struct PyMethodDef { Py_ssize_t a; };
 # undef Py_InitModule4
 # undef Py_InitModule4_64
 # undef PyObject_CallMethod
+# undef PyObject_CallFunction
 
 /*
  * Wrapper defines
@@ -219,6 +220,7 @@ struct PyMethodDef { Py_ssize_t a; };
 # define PyObject_HasAttrString dll_PyObject_HasAttrString
 # define PyObject_SetAttrString dll_PyObject_SetAttrString
 # define PyObject_CallFunctionObjArgs dll_PyObject_CallFunctionObjArgs
+# define PyObject_CallFunction dll_PyObject_CallFunction
 # define PyObject_Call dll_PyObject_Call
 # define PyString_AsString dll_PyString_AsString
 # define PyString_AsStringAndSize dll_PyString_AsStringAndSize
@@ -357,6 +359,7 @@ static PyObject* (*dll_PyObject_GetAttrString)(PyObject *, const char *);
 static int (*dll_PyObject_HasAttrString)(PyObject *, const char *);
 static PyObject* (*dll_PyObject_SetAttrString)(PyObject *, const char *, PyObject *);
 static PyObject* (*dll_PyObject_CallFunctionObjArgs)(PyObject *, ...);
+static PyObject* (*dll_PyObject_CallFunction)(PyObject *, char *, ...);
 static PyObject* (*dll_PyObject_Call)(PyObject *, PyObject *, PyObject *);
 static char*(*dll_PyString_AsString)(PyObject *);
 static int(*dll_PyString_AsStringAndSize)(PyObject *, char **, int *);
@@ -528,6 +531,7 @@ static struct
     {"PyObject_HasAttrString", (PYTHON_PROC*)&dll_PyObject_HasAttrString},
     {"PyObject_SetAttrString", (PYTHON_PROC*)&dll_PyObject_SetAttrString},
     {"PyObject_CallFunctionObjArgs", (PYTHON_PROC*)&dll_PyObject_CallFunctionObjArgs},
+    {"PyObject_CallFunction", (PYTHON_PROC*)&dll_PyObject_CallFunction},
     {"PyObject_Call", (PYTHON_PROC*)&dll_PyObject_Call},
     {"PyString_AsString", (PYTHON_PROC*)&dll_PyString_AsString},
     {"PyString_AsStringAndSize", (PYTHON_PROC*)&dll_PyString_AsStringAndSize},
@@ -748,10 +752,12 @@ static PyObject *DictionaryGetattr(PyObject *, char*);
 static PyObject *ListGetattr(PyObject *, char *);
 static PyObject *FunctionGetattr(PyObject *, char *);
 
-static PyObject *LoaderLoadModule(PyObject *, PyObject *);
 static PyObject *FinderFindModule(PyObject *, PyObject *);
 static PyObject *VimPathHook(PyObject *, PyObject *);
 
+static PyObject *py_find_module;
+static PyObject *py_load_module;
+
 #ifndef Py_VISIT
 # define Py_VISIT(obj) visit(obj, arg)
 #endif
@@ -1376,90 +1382,153 @@ python_tabpage_free(tabpage_T *tab)
 }
 #endif
 
+    static void
+LoaderDestructor(LoaderObject *self)
+{
+    Py_DECREF(self->module);
+    DESTRUCTOR_FINISH(self);
+}
+
     static PyObject *
-LoaderLoadModule(PyObject *self, PyObject *args)
+LoaderLoadModule(LoaderObject *self, PyObject *args UNUSED)
 {
-    char       *fullname;
-    PyObject   *path;
-    PyObject   *meta_path;
-    PyObject   *path_hooks;
-    PyObject   *new_path;
-    PyObject   *r;
-    PyObject   *new_list;
+    PyObject   *r = self->module;
 
-    if (!PyArg_ParseTuple(args, "s", &fullname))
-       return NULL;
+    Py_INCREF(r);
+    return r;
+}
 
-    if (!(new_path = Vim_GetPaths(self)))
+static struct PyMethodDef LoaderMethods[] = {
+    /* name,       function,                           calling,        doc */
+    {"load_module", (PyCFunction)LoaderLoadModule,     METH_VARARGS,   ""},
+    { NULL,        NULL,                               0,              NULL}
+};
+
+    static PyObject *
+call_load_module(char *name, int len, PyObject *find_module_result)
+{
+    PyObject   *fd, *pathname, *description;
+
+    if (!PyTuple_Check(find_module_result)
+           || PyTuple_GET_SIZE(find_module_result) != 3)
+    {
+       PyErr_SetString(PyExc_TypeError,
+               _("expected 3-tuple as imp.find_module() result"));
        return NULL;
+    }
 
-    if (!(new_list = PyList_New(0)))
+    if (!(fd = PyTuple_GET_ITEM(find_module_result, 0))
+           || !(pathname = PyTuple_GET_ITEM(find_module_result, 1))
+           || !(description = PyTuple_GET_ITEM(find_module_result, 2)))
+    {
+       PyErr_SetString(PyExc_RuntimeError,
+               _("internal error: imp.find_module returned tuple with NULL"));
        return NULL;
+    }
+
+    return PyObject_CallFunction(py_load_module,
+           "s#OOO", name, len, fd, pathname, description);
+}
 
-#define GET_SYS_OBJECT(objstr, obj) \
-    obj = PySys_GetObject(objstr); \
-    PyErr_Clear(); \
-    Py_XINCREF(obj);
+    static PyObject *
+find_module(char *fullname, char *tail, PyObject *new_path)
+{
+    PyObject   *find_module_result;
+    PyObject   *module;
+    char       *dot;
 
-    GET_SYS_OBJECT("meta_path", meta_path);
-    if (PySys_SetObject("meta_path", new_list))
+    if ((dot = (char *) vim_strchr((char_u *) tail, '.')))
     {
-       Py_XDECREF(meta_path);
-       Py_DECREF(new_list);
-       return NULL;
+       /*
+        * There is a dot in the name: call find_module recursively without the 
+        * first component
+        */
+       PyObject        *newest_path;
+       int             partlen = (int) (dot - 1 - tail);
+
+       if (!(find_module_result = PyObject_CallFunction(py_find_module,
+                       "s#O", tail, partlen, new_path)))
+           return NULL;
+
+       if (!(module = call_load_module(
+                       fullname,
+                       ((int) (tail - fullname)) + partlen,
+                       find_module_result)))
+       {
+           Py_DECREF(find_module_result);
+           return NULL;
+       }
+
+       Py_DECREF(find_module_result);
+
+       if (!(newest_path = PyObject_GetAttrString(module, "__path__")))
+       {
+           Py_DECREF(module);
+           return NULL;
+       }
+
+       Py_DECREF(module);
+
+       module = find_module(fullname, dot + 1, newest_path);
+
+       Py_DECREF(newest_path);
+
+       return module;
     }
-    Py_DECREF(new_list); /* Now it becomes a reference borrowed from
-                           sys.meta_path */
+    else
+    {
+       if (!(find_module_result = PyObject_CallFunction(py_find_module,
+                       "sO", tail, new_path)))
+           return NULL;
+
+       if (!(module = call_load_module(
+                       fullname,
+                       STRLEN(fullname),
+                       find_module_result)))
+       {
+           Py_DECREF(find_module_result);
+           return NULL;
+       }
 
-#define RESTORE_SYS_OBJECT(objstr, obj) \
-    if (obj) \
-    { \
-       PySys_SetObject(objstr, obj); \
-       Py_DECREF(obj); \
+       Py_DECREF(find_module_result);
+
+       return module;
     }
+}
 
-    GET_SYS_OBJECT("path_hooks", path_hooks);
-    if (PySys_SetObject("path_hooks", new_list))
-    {
-       RESTORE_SYS_OBJECT("meta_path", meta_path);
-       Py_XDECREF(path_hooks);
+    static PyObject *
+FinderFindModule(PyObject *self, PyObject *args)
+{
+    char       *fullname;
+    PyObject   *module;
+    PyObject   *new_path;
+    LoaderObject       *loader;
+
+    if (!PyArg_ParseTuple(args, "s", &fullname))
        return NULL;
-    }
 
-    GET_SYS_OBJECT("path", path);
-    if (PySys_SetObject("path", new_path))
-    {
-       RESTORE_SYS_OBJECT("meta_path", meta_path);
-       RESTORE_SYS_OBJECT("path_hooks", path_hooks);
-       Py_XDECREF(path);
+    if (!(new_path = Vim_GetPaths(self)))
        return NULL;
-    }
-    Py_DECREF(new_path);
 
-    r = PyImport_ImportModule(fullname);
+    module = find_module(fullname, fullname, new_path);
 
-    RESTORE_SYS_OBJECT("meta_path", meta_path);
-    RESTORE_SYS_OBJECT("path_hooks", path_hooks);
-    RESTORE_SYS_OBJECT("path", path);
+    Py_DECREF(new_path);
 
-    if (PyErr_Occurred())
+    if (!module)
     {
-       Py_XDECREF(r);
+       Py_INCREF(Py_None);
+       return Py_None;
+    }
+
+    if (!(loader = PyObject_NEW(LoaderObject, &LoaderType)))
+    {
+       Py_DECREF(module);
        return NULL;
     }
 
-    return r;
-}
+    loader->module = module;
 
-    static PyObject *
-FinderFindModule(PyObject *self UNUSED, PyObject *args UNUSED)
-{
-    /*
-     * Don't bother actually finding the module, it is delegated to the "loader"
-     * object (which is basically the same object: vim module).
-     */
-    Py_INCREF(vim_module);
-    return vim_module;
+    return (PyObject *) loader;
 }
 
     static PyObject *
@@ -1483,7 +1552,34 @@ VimPathHook(PyObject *self UNUSED, PyObject *args)
 PythonMod_Init(void)
 {
     /* The special value is removed from sys.path in Python_Init(). */
-    static char *(argv[2]) = {"/must>not&exist/foo", NULL};
+    static char        *(argv[2]) = {"/must>not&exist/foo", NULL};
+    PyObject   *imp;
+
+    if (!(imp = PyImport_ImportModule("imp")))
+       return -1;
+
+    if (!(py_find_module = PyObject_GetAttrString(imp, "find_module")))
+    {
+       Py_DECREF(imp);
+       return -1;
+    }
+
+    if (!(py_load_module = PyObject_GetAttrString(imp, "load_module")))
+    {
+       Py_DECREF(py_find_module);
+       Py_DECREF(imp);
+       return -1;
+    }
+
+    Py_DECREF(imp);
+
+    vim_memset(&LoaderType, 0, sizeof(LoaderType));
+    LoaderType.tp_name = "vim.Loader";
+    LoaderType.tp_basicsize = sizeof(LoaderObject);
+    LoaderType.tp_flags = Py_TPFLAGS_DEFAULT;
+    LoaderType.tp_doc = "vim message object";
+    LoaderType.tp_methods = LoaderMethods;
+    LoaderType.tp_dealloc = (destructor)LoaderDestructor;
 
     if (init_types())
        return -1;
index 5f0a6f0a9f7da8ec4cba2c1f241db59937fec538..e90106ab2ae9310e8f35360d96ea25281556f1f8 100644 (file)
@@ -1 +1,2 @@
+import before_1
 dir = '2'
index 1ccfb094c0c002adb8c2bc031e838c9e424d216a..24bd036e1d8ef692a7c1074848ccc7b77dda3822 100644 (file)
@@ -1 +1,2 @@
+import before_1
 dir = '3'
diff --git a/src/testdir/python_after/after.py b/src/testdir/python_after/after.py
new file mode 100644 (file)
index 0000000..5cf8fa4
--- /dev/null
@@ -0,0 +1,2 @@
+import before_2
+dir = "after"
diff --git a/src/testdir/python_before/before.py b/src/testdir/python_before/before.py
new file mode 100644 (file)
index 0000000..531e81a
--- /dev/null
@@ -0,0 +1 @@
+dir = "before"
index 26c7fe21783ee3f6257d7fc0d22c93a88b8d464e..e22ae310395b637de6f79c70fac4366d2d8f9f44 100644 (file)
@@ -8,6 +8,7 @@ See http://svn.python.org/view/python/trunk/Misc/README.valgrind?view=markup
 STARTTEST
 :so small.vim
 :set encoding=latin1
+:set noswapfile
 :if !has('python') | e! test.ok | wq! test.out | endif
 :lang C
 :py import vim
@@ -1071,10 +1072,16 @@ EOF
 :"
 :" Test import
 py << EOF
+sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
+sys.path.append(os.path.join(os.getcwd(), 'python_after'))
 vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
 from module import dir as d
 from modulex import ddir
 cb.append(d + ',' + ddir)
+import before
+cb.append(before.dir)
+import after
+cb.append(after.dir)
 EOF
 :"
 :" Test exceptions
index 145979ff5342cdf4243c4cb5838399e15285e487..e981544f02cef91a6da2dc58fc8ea61ce59b626a 100644 (file)
@@ -1084,6 +1084,8 @@ vim.current.window = True:(<type 'exceptions.TypeError'>, TypeError('expected vi
 vim.current.tabpage = True:(<type 'exceptions.TypeError'>, TypeError('expected vim.TabPage object',))
 vim.current.xxx = True:(<type 'exceptions.AttributeError'>, AttributeError('xxx',))
 2,xx
+before
+after
 vim.command("throw 'abc'"):(<class 'vim.error'>, error('abc',))
 Exe("throw 'def'"):(<class 'vim.error'>, error('def',))
 vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',))
index 7a3d583590943ad7991f99d8ff1b822b9a5db21f..1223ca96aadb134545dd3013deaa44292a4bf067 100644 (file)
@@ -2,6 +2,7 @@ Tests for various python features.     vim: set ft=vim :
 
 STARTTEST
 :so small.vim
+:set noswapfile
 :if !has('python3') | e! test.ok | wq! test.out | endif
 :lang C
 :py3 import vim
@@ -1038,10 +1039,16 @@ EOF
 :"
 :" Test import
 py3 << EOF
+sys.path.insert(0, os.path.join(os.getcwd(), 'python_before'))
+sys.path.append(os.path.join(os.getcwd(), 'python_after'))
 vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
 from module import dir as d
 from modulex import ddir
 cb.append(d + ',' + ddir)
+import before
+cb.append(before.dir)
+import after
+cb.append(after.dir)
 EOF
 :"
 :" Test exceptions
index 31c7df4b6654c595181229237a4fcefdcbe52202..1d4044519dfb1f41e913b37537b6340dacca752d 100644 (file)
@@ -1093,6 +1093,8 @@ vim.current.window = True:(<class 'TypeError'>, TypeError('expected vim.Window o
 vim.current.tabpage = True:(<class 'TypeError'>, TypeError('expected vim.TabPage object',))
 vim.current.xxx = True:(<class 'AttributeError'>, AttributeError('xxx',))
 3,xx
+before
+after
 vim.command("throw 'abc'"):(<class 'vim.error'>, error('abc',))
 Exe("throw 'def'"):(<class 'vim.error'>, error('def',))
 vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',))
index c6ef1bdd3f8db7d8a7645360463f75c3b27cc748..9d7750e531e77dc53566991577dc4ef7a0e2ef31 100644 (file)
@@ -728,6 +728,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1172,
 /**/
     1171,
 /**/