0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

updated for version 7.3.1172

Problem:    Python 2: loading modules doesn't work well.
Solution:   Fix the code. Add more tests. (ZyX)
This commit is contained in:
Bram Moolenaar
2013-06-12 14:20:36 +02:00
parent 0ea4a6b94b
commit 9f3685a527
13 changed files with 243 additions and 107 deletions

View File

@@ -87,6 +87,8 @@ SRC_ALL = \
src/testdir/python2/*.py \ src/testdir/python2/*.py \
src/testdir/python3/*.py \ src/testdir/python3/*.py \
src/testdir/pythonx/*.py \ src/testdir/pythonx/*.py \
src/testdir/python_after/*.py \
src/testdir/python_before/*.py \
src/proto.h \ src/proto.h \
src/proto/blowfish.pro \ src/proto/blowfish.pro \
src/proto/buffer.pro \ src/proto/buffer.pro \

View 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 {rtp}/python2 (or python3) and {rtp}/pythonx (for both python versions) for
each {rtp} found in 'runtimepath'. each {rtp} found in 'runtimepath'.
Implementation for python 2 is the following: usual importing code with empty Implementation for python 2 is similar to the following, but written in C: >
lists in place of sys.path_hooks and sys.meta_path. Code is similar to the
below, but written in C: >
# Assuming vim variable is already accessible and is set to the current from imp import find_module, load_module
# module import vim
import sys import sys
def find_module(fullname): class VimModuleLoader(object):
return vim def __init__(self, module):
self.module = module
def load_module(fullname): def load_module(self, fullname, path=None):
# see vim._get_paths below return self.module
new_path = _get_paths()
try: old_path = sys.path def _find_module(fullname, oldtail, path):
except: pass idx = oldtail.find('.')
try: old_meta_path = sys.meta_path if idx > 0:
except: pass name = oldtail[:idx]
try: old_path_hooks = sys.path_hooks tail = oldtail[idx+1:]
except: pass 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)
sys.meta_path = [] # It uses vim module itself in place of VimPathFinder class: it does not
sys.path_hooks = sys.meta_path # matter for python which object has find_module function attached to as
sys.path = new_path # 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)
try: def load_module(cls, fullname, path=None):
exec ('import ' + fullname + ' as m') # No actual exec in C code return _find_module(fullname, fullname, path or vim._get_paths())
return m load_module = classmethod(load_module)
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): def hook(path):
if d == VIM_SPECIAL_PATH: if path == vim.VIM_SPECIAL_PATH:
return vim return VimPathFinder
raise ImportError else:
raise ImportError
sys.path_hooks.append(path_hook) sys.path_hooks.append(hook)
Implementation for python 3 is cleaner: code is similar to the following, but, Implementation for python 3 is cleaner: code is similar to the following, but,
again, written in C: > 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 Note: you must not use value of this constant directly, always use
vim.VIM_SPECIAL_PATH object. vim.VIM_SPECIAL_PATH object.
vim.load_module(name) *python-load_module*
vim.find_module(...) *python-find_module* vim.find_module(...) *python-find_module*
vim.path_hook(path) *python-path_hook* vim.path_hook(path) *python-path_hook*
Methods or objects used to implement path loading as described above. 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 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 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 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. in python3.
vim._get_paths *python-_get_paths* vim._get_paths *python-_get_paths*

View File

@@ -940,7 +940,6 @@ static struct PyMethodDef VimMethods[] = {
{"foreach_rtp", VimForeachRTP, METH_VARARGS, "Call given callable for each path in &rtp"}, {"foreach_rtp", VimForeachRTP, METH_VARARGS, "Call given callable for each path in &rtp"},
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
{"find_module", FinderFindModule, METH_VARARGS, "Internal use only, returns loader object for any input it receives"}, {"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 #endif
{"path_hook", VimPathHook, METH_VARARGS, "Hook function to install in sys.path_hooks"}, {"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"}, {"_get_paths", (PyCFunction)Vim_GetPaths, METH_NOARGS, "Get &rtp-based additions to sys.path"},
@@ -5195,6 +5194,13 @@ typedef struct
PyObject_HEAD PyObject_HEAD
} FinderObject; } FinderObject;
static PyTypeObject FinderType; static PyTypeObject FinderType;
#else
typedef struct
{
PyObject_HEAD
PyObject *module;
} LoaderObject;
static PyTypeObject LoaderType;
#endif #endif
static void static void
@@ -5444,6 +5450,8 @@ init_types()
PYTYPE_READY(OutputType); PYTYPE_READY(OutputType);
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
PYTYPE_READY(FinderType); PYTYPE_READY(FinderType);
#else
PYTYPE_READY(LoaderType);
#endif #endif
return 0; return 0;
} }
@@ -5570,6 +5578,8 @@ static struct object_constant {
{"Options", (PyObject *)&OptionsType}, {"Options", (PyObject *)&OptionsType},
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
{"Finder", (PyObject *)&FinderType}, {"Finder", (PyObject *)&FinderType},
#else
{"Loader", (PyObject *)&LoaderType},
#endif #endif
}; };
@@ -5666,6 +5676,9 @@ populate_module(PyObject *m, object_adder add_object, attr_getter get_attr)
ADD_CHECKED_OBJECT(m, "_find_module", ADD_CHECKED_OBJECT(m, "_find_module",
(py_find_module = PyObject_GetAttrString(path_finder, (py_find_module = PyObject_GetAttrString(path_finder,
"find_module"))); "find_module")));
#else
ADD_OBJECT(m, "_find_module", py_find_module);
ADD_OBJECT(m, "_load_module", py_load_module);
#endif #endif
return 0; return 0;

View File

@@ -150,6 +150,7 @@ struct PyMethodDef { Py_ssize_t a; };
# undef Py_InitModule4 # undef Py_InitModule4
# undef Py_InitModule4_64 # undef Py_InitModule4_64
# undef PyObject_CallMethod # undef PyObject_CallMethod
# undef PyObject_CallFunction
/* /*
* Wrapper defines * Wrapper defines
@@ -219,6 +220,7 @@ struct PyMethodDef { Py_ssize_t a; };
# define PyObject_HasAttrString dll_PyObject_HasAttrString # define PyObject_HasAttrString dll_PyObject_HasAttrString
# define PyObject_SetAttrString dll_PyObject_SetAttrString # define PyObject_SetAttrString dll_PyObject_SetAttrString
# define PyObject_CallFunctionObjArgs dll_PyObject_CallFunctionObjArgs # define PyObject_CallFunctionObjArgs dll_PyObject_CallFunctionObjArgs
# define PyObject_CallFunction dll_PyObject_CallFunction
# define PyObject_Call dll_PyObject_Call # define PyObject_Call dll_PyObject_Call
# define PyString_AsString dll_PyString_AsString # define PyString_AsString dll_PyString_AsString
# define PyString_AsStringAndSize dll_PyString_AsStringAndSize # 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 int (*dll_PyObject_HasAttrString)(PyObject *, const char *);
static PyObject* (*dll_PyObject_SetAttrString)(PyObject *, const char *, PyObject *); static PyObject* (*dll_PyObject_SetAttrString)(PyObject *, const char *, PyObject *);
static PyObject* (*dll_PyObject_CallFunctionObjArgs)(PyObject *, ...); static PyObject* (*dll_PyObject_CallFunctionObjArgs)(PyObject *, ...);
static PyObject* (*dll_PyObject_CallFunction)(PyObject *, char *, ...);
static PyObject* (*dll_PyObject_Call)(PyObject *, PyObject *, PyObject *); static PyObject* (*dll_PyObject_Call)(PyObject *, PyObject *, PyObject *);
static char*(*dll_PyString_AsString)(PyObject *); static char*(*dll_PyString_AsString)(PyObject *);
static int(*dll_PyString_AsStringAndSize)(PyObject *, char **, int *); static int(*dll_PyString_AsStringAndSize)(PyObject *, char **, int *);
@@ -528,6 +531,7 @@ static struct
{"PyObject_HasAttrString", (PYTHON_PROC*)&dll_PyObject_HasAttrString}, {"PyObject_HasAttrString", (PYTHON_PROC*)&dll_PyObject_HasAttrString},
{"PyObject_SetAttrString", (PYTHON_PROC*)&dll_PyObject_SetAttrString}, {"PyObject_SetAttrString", (PYTHON_PROC*)&dll_PyObject_SetAttrString},
{"PyObject_CallFunctionObjArgs", (PYTHON_PROC*)&dll_PyObject_CallFunctionObjArgs}, {"PyObject_CallFunctionObjArgs", (PYTHON_PROC*)&dll_PyObject_CallFunctionObjArgs},
{"PyObject_CallFunction", (PYTHON_PROC*)&dll_PyObject_CallFunction},
{"PyObject_Call", (PYTHON_PROC*)&dll_PyObject_Call}, {"PyObject_Call", (PYTHON_PROC*)&dll_PyObject_Call},
{"PyString_AsString", (PYTHON_PROC*)&dll_PyString_AsString}, {"PyString_AsString", (PYTHON_PROC*)&dll_PyString_AsString},
{"PyString_AsStringAndSize", (PYTHON_PROC*)&dll_PyString_AsStringAndSize}, {"PyString_AsStringAndSize", (PYTHON_PROC*)&dll_PyString_AsStringAndSize},
@@ -748,10 +752,12 @@ static PyObject *DictionaryGetattr(PyObject *, char*);
static PyObject *ListGetattr(PyObject *, char *); static PyObject *ListGetattr(PyObject *, char *);
static PyObject *FunctionGetattr(PyObject *, char *); static PyObject *FunctionGetattr(PyObject *, char *);
static PyObject *LoaderLoadModule(PyObject *, PyObject *);
static PyObject *FinderFindModule(PyObject *, PyObject *); static PyObject *FinderFindModule(PyObject *, PyObject *);
static PyObject *VimPathHook(PyObject *, PyObject *); static PyObject *VimPathHook(PyObject *, PyObject *);
static PyObject *py_find_module;
static PyObject *py_load_module;
#ifndef Py_VISIT #ifndef Py_VISIT
# define Py_VISIT(obj) visit(obj, arg) # define Py_VISIT(obj) visit(obj, arg)
#endif #endif
@@ -1376,16 +1382,127 @@ python_tabpage_free(tabpage_T *tab)
} }
#endif #endif
static void
LoaderDestructor(LoaderObject *self)
{
Py_DECREF(self->module);
DESTRUCTOR_FINISH(self);
}
static PyObject * static PyObject *
LoaderLoadModule(PyObject *self, PyObject *args) LoaderLoadModule(LoaderObject *self, PyObject *args UNUSED)
{
PyObject *r = self->module;
Py_INCREF(r);
return r;
}
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 (!(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);
}
static PyObject *
find_module(char *fullname, char *tail, PyObject *new_path)
{
PyObject *find_module_result;
PyObject *module;
char *dot;
if ((dot = (char *) vim_strchr((char_u *) tail, '.')))
{
/*
* 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;
}
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;
}
Py_DECREF(find_module_result);
return module;
}
}
static PyObject *
FinderFindModule(PyObject *self, PyObject *args)
{ {
char *fullname; char *fullname;
PyObject *path; PyObject *module;
PyObject *meta_path;
PyObject *path_hooks;
PyObject *new_path; PyObject *new_path;
PyObject *r; LoaderObject *loader;
PyObject *new_list;
if (!PyArg_ParseTuple(args, "s", &fullname)) if (!PyArg_ParseTuple(args, "s", &fullname))
return NULL; return NULL;
@@ -1393,73 +1510,25 @@ LoaderLoadModule(PyObject *self, PyObject *args)
if (!(new_path = Vim_GetPaths(self))) if (!(new_path = Vim_GetPaths(self)))
return NULL; return NULL;
if (!(new_list = PyList_New(0))) module = find_module(fullname, fullname, new_path);
return NULL;
#define GET_SYS_OBJECT(objstr, obj) \
obj = PySys_GetObject(objstr); \
PyErr_Clear(); \
Py_XINCREF(obj);
GET_SYS_OBJECT("meta_path", meta_path);
if (PySys_SetObject("meta_path", new_list))
{
Py_XDECREF(meta_path);
Py_DECREF(new_list);
return NULL;
}
Py_DECREF(new_list); /* Now it becomes a reference borrowed from
sys.meta_path */
#define RESTORE_SYS_OBJECT(objstr, obj) \
if (obj) \
{ \
PySys_SetObject(objstr, obj); \
Py_DECREF(obj); \
}
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);
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);
return NULL;
}
Py_DECREF(new_path); Py_DECREF(new_path);
r = PyImport_ImportModule(fullname); if (!module)
RESTORE_SYS_OBJECT("meta_path", meta_path);
RESTORE_SYS_OBJECT("path_hooks", path_hooks);
RESTORE_SYS_OBJECT("path", path);
if (PyErr_Occurred())
{ {
Py_XDECREF(r); Py_INCREF(Py_None);
return Py_None;
}
if (!(loader = PyObject_NEW(LoaderObject, &LoaderType)))
{
Py_DECREF(module);
return NULL; return NULL;
} }
return r; loader->module = module;
}
static PyObject * return (PyObject *) loader;
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;
} }
static PyObject * static PyObject *
@@ -1483,7 +1552,34 @@ VimPathHook(PyObject *self UNUSED, PyObject *args)
PythonMod_Init(void) PythonMod_Init(void)
{ {
/* The special value is removed from sys.path in Python_Init(). */ /* 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()) if (init_types())
return -1; return -1;

View File

@@ -1 +1,2 @@
import before_1
dir = '2' dir = '2'

View File

@@ -1 +1,2 @@
import before_1
dir = '3' dir = '3'

View File

@@ -0,0 +1,2 @@
import before_2
dir = "after"

View File

@@ -0,0 +1 @@
dir = "before"

View File

@@ -8,6 +8,7 @@ See http://svn.python.org/view/python/trunk/Misc/README.valgrind?view=markup
STARTTEST STARTTEST
:so small.vim :so small.vim
:set encoding=latin1 :set encoding=latin1
:set noswapfile
:if !has('python') | e! test.ok | wq! test.out | endif :if !has('python') | e! test.ok | wq! test.out | endif
:lang C :lang C
:py import vim :py import vim
@@ -1071,10 +1072,16 @@ EOF
:" :"
:" Test import :" Test import
py << EOF 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('\\', '\\\\') vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
from module import dir as d from module import dir as d
from modulex import ddir from modulex import ddir
cb.append(d + ',' + ddir) cb.append(d + ',' + ddir)
import before
cb.append(before.dir)
import after
cb.append(after.dir)
EOF EOF
:" :"
:" Test exceptions :" Test exceptions

View 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.tabpage = True:(<type 'exceptions.TypeError'>, TypeError('expected vim.TabPage object',))
vim.current.xxx = True:(<type 'exceptions.AttributeError'>, AttributeError('xxx',)) vim.current.xxx = True:(<type 'exceptions.AttributeError'>, AttributeError('xxx',))
2,xx 2,xx
before
after
vim.command("throw 'abc'"):(<class 'vim.error'>, error('abc',)) vim.command("throw 'abc'"):(<class 'vim.error'>, error('abc',))
Exe("throw 'def'"):(<class 'vim.error'>, error('def',)) Exe("throw 'def'"):(<class 'vim.error'>, error('def',))
vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',)) vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',))

View File

@@ -2,6 +2,7 @@ Tests for various python features. vim: set ft=vim :
STARTTEST STARTTEST
:so small.vim :so small.vim
:set noswapfile
:if !has('python3') | e! test.ok | wq! test.out | endif :if !has('python3') | e! test.ok | wq! test.out | endif
:lang C :lang C
:py3 import vim :py3 import vim
@@ -1038,10 +1039,16 @@ EOF
:" :"
:" Test import :" Test import
py3 << EOF 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('\\', '\\\\') vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\')
from module import dir as d from module import dir as d
from modulex import ddir from modulex import ddir
cb.append(d + ',' + ddir) cb.append(d + ',' + ddir)
import before
cb.append(before.dir)
import after
cb.append(after.dir)
EOF EOF
:" :"
:" Test exceptions :" Test exceptions

View 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.tabpage = True:(<class 'TypeError'>, TypeError('expected vim.TabPage object',))
vim.current.xxx = True:(<class 'AttributeError'>, AttributeError('xxx',)) vim.current.xxx = True:(<class 'AttributeError'>, AttributeError('xxx',))
3,xx 3,xx
before
after
vim.command("throw 'abc'"):(<class 'vim.error'>, error('abc',)) vim.command("throw 'abc'"):(<class 'vim.error'>, error('abc',))
Exe("throw 'def'"):(<class 'vim.error'>, error('def',)) Exe("throw 'def'"):(<class 'vim.error'>, error('def',))
vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',)) vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',))

View File

@@ -728,6 +728,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1172,
/**/ /**/
1171, 1171,
/**/ /**/