0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

updated for version 7.4.107

Problem:    Python: When vim.eval() encounters a Vim error, a try/catch in the
            Python code doesn't catch it. (Yggdroot Chen)
Solution:   Throw exceptions on errors in vim.eval(). (ZyX)
This commit is contained in:
Bram Moolenaar
2013-11-28 17:04:43 +01:00
parent e88a5f3a2c
commit 9fee7d4729
8 changed files with 179 additions and 124 deletions

View File

@@ -320,6 +320,17 @@ free_msglist(l)
}
}
/*
* Free global "*msg_list" and the messages it contains, then set "*msg_list"
* to NULL.
*/
void
free_global_msglist()
{
free_msglist(*msg_list);
*msg_list = NULL;
}
/*
* Throw the message specified in the call to cause_errthrow() above as an
* error exception. If cstack is NULL, postpone the throw until do_cmdline()
@@ -410,66 +421,41 @@ do_intthrow(cstack)
return TRUE;
}
/*
* Throw a new exception. Return FAIL when out of memory or it was tried to
* throw an illegal user exception. "value" is the exception string for a user
* or interrupt exception, or points to a message list in case of an error
* exception.
* Get an exception message that is to be stored in current_exception->value.
*/
static int
throw_exception(value, type, cmdname)
char_u *
get_exception_string(value, type, cmdname, should_free)
void *value;
int type;
char_u *cmdname;
int *should_free;
{
except_T *excp;
char_u *p, *mesg, *val;
char_u *ret, *mesg;
int cmdlen;
/*
* Disallow faking Interrupt or error exceptions as user exceptions. They
* would be treated differently from real interrupt or error exceptions when
* no active try block is found, see do_cmdline().
*/
if (type == ET_USER)
{
if (STRNCMP((char_u *)value, "Vim", 3) == 0 &&
(((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':' ||
((char_u *)value)[3] == '('))
{
EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
goto fail;
}
}
excp = (except_T *)alloc((unsigned)sizeof(except_T));
if (excp == NULL)
goto nomem;
char_u *p, *val;
if (type == ET_ERROR)
{
/* Store the original message and prefix the exception value with
* "Vim:" or, if a command name is given, "Vim(cmdname):". */
excp->messages = (struct msglist *)value;
mesg = excp->messages->throw_msg;
*should_free = FALSE;
mesg = ((struct msglist *)value)->throw_msg;
if (cmdname != NULL && *cmdname != NUL)
{
cmdlen = (int)STRLEN(cmdname);
excp->value = vim_strnsave((char_u *)"Vim(",
ret = vim_strnsave((char_u *)"Vim(",
4 + cmdlen + 2 + (int)STRLEN(mesg));
if (excp->value == NULL)
goto nomem;
STRCPY(&excp->value[4], cmdname);
STRCPY(&excp->value[4 + cmdlen], "):");
val = excp->value + 4 + cmdlen + 2;
if (ret == NULL)
return ret;
STRCPY(&ret[4], cmdname);
STRCPY(&ret[4 + cmdlen], "):");
val = ret + 4 + cmdlen + 2;
}
else
{
excp->value = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));
if (excp->value == NULL)
goto nomem;
val = excp->value + 4;
ret = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));
if (ret == NULL)
return ret;
val = ret + 4;
}
/* msg_add_fname may have been used to prefix the message with a file
@@ -506,14 +492,65 @@ throw_exception(value, type, cmdname)
}
}
else
excp->value = value;
{
*should_free = FALSE;
ret = (char_u *) value;
}
return ret;
}
/*
* Throw a new exception. Return FAIL when out of memory or it was tried to
* throw an illegal user exception. "value" is the exception string for a
* user or interrupt exception, or points to a message list in case of an
* error exception.
*/
static int
throw_exception(value, type, cmdname)
void *value;
int type;
char_u *cmdname;
{
except_T *excp;
int should_free;
/*
* Disallow faking Interrupt or error exceptions as user exceptions. They
* would be treated differently from real interrupt or error exceptions
* when no active try block is found, see do_cmdline().
*/
if (type == ET_USER)
{
if (STRNCMP((char_u *)value, "Vim", 3) == 0
&& (((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':'
|| ((char_u *)value)[3] == '('))
{
EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
goto fail;
}
}
excp = (except_T *)alloc((unsigned)sizeof(except_T));
if (excp == NULL)
goto nomem;
if (type == ET_ERROR)
/* Store the original message and prefix the exception value with
* "Vim:" or, if a command name is given, "Vim(cmdname):". */
excp->messages = (struct msglist *)value;
excp->value = get_exception_string(value, type, cmdname, &should_free);
if (excp->value == NULL && should_free)
goto nomem;
excp->type = type;
excp->throw_name = vim_strsave(sourcing_name == NULL
? (char_u *)"" : sourcing_name);
if (excp->throw_name == NULL)
{
if (type == ET_ERROR)
if (should_free)
vim_free(excp->value);
goto nomem;
}
@@ -2033,10 +2070,7 @@ leave_cleanup(csp)
/* If an error was about to be converted to an exception when
* enter_cleanup() was called, free the message list. */
if (msg_list != NULL)
{
free_msglist(*msg_list);
*msg_list = NULL;
}
free_global_msglist();
}
/*

View File

@@ -566,6 +566,28 @@ VimTryEnd(void)
PyErr_SetNone(PyExc_KeyboardInterrupt);
return -1;
}
else if (msg_list != NULL && *msg_list != NULL)
{
int should_free;
char_u *msg;
msg = get_exception_string(*msg_list, ET_ERROR, NULL, &should_free);
if (msg == NULL)
{
PyErr_NoMemory();
return -1;
}
PyErr_SetVim((char *) msg);
free_global_msglist();
if (should_free)
vim_free(msg);
return -1;
}
else if (!did_throw)
return (PyErr_Occurred() ? -1 : 0);
/* Python exception is preferred over vim one; unlikely to occur though */

View File

@@ -4,8 +4,10 @@ void update_force_abort __ARGS((void));
int should_abort __ARGS((int retcode));
int aborted_in_try __ARGS((void));
int cause_errthrow __ARGS((char_u *mesg, int severe, int *ignore));
void free_global_msglist __ARGS((void));
void do_errthrow __ARGS((struct condstack *cstack, char_u *cmdname));
int do_intthrow __ARGS((struct condstack *cstack));
char_u *get_exception_string __ARGS((void *value, int type, char_u *cmdname, int *should_free));
void discard_current_exception __ARGS((void));
void report_make_pending __ARGS((int pending, void *value));
void report_resume_pending __ARGS((int pending, void *value));

View File

@@ -179,6 +179,32 @@ EOF
:unlockvar! l
:"
:" Function calls
py << EOF
import sys
def ee(expr, g=globals(), l=locals()):
try:
exec(expr, g, l)
except:
ei = sys.exc_info()
msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args)
msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
if expr.find('None') > -1:
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
'TypeError:("\'NoneType\' object is not iterable",)')
if expr.find('FailingNumber') > -1:
msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
'TypeError:("\'FailingNumber\' object is not iterable",)')
if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
msg = msg.replace('(\'', '("').replace('\',)', '",)')
if expr == 'fd(self=[])':
# HACK: PyMapping_Check changed meaning
msg = msg.replace('AttributeError:(\'keys\',)',
'TypeError:(\'unable to convert list to vim dictionary\',)')
vim.current.buffer.append(expr + ':' + msg)
else:
vim.current.buffer.append(expr + ':NOT FAILED')
EOF
:fun New(...)
: return ['NewStart']+a:000+['NewEnd']
:endfun
@@ -193,18 +219,10 @@ EOF
:$put =string(l)
:py l.extend([l[0].name])
:$put =string(l)
:try
: py l[1](1, 2, 3)
:catch
: $put =v:exception[:16]
:endtry
:py ee('l[1](1, 2, 3)')
:py f=l[0]
:delfunction New
:try
: py f(1, 2, 3)
:catch
: $put =v:exception[:16]
:endtry
:py ee('f(1, 2, 3)')
:if has('float')
: let l=[0.0]
: py l=vim.bindeval('l')
@@ -216,7 +234,6 @@ EOF
:let messages=[]
:delfunction DictNew
py <<EOF
import sys
d=vim.bindeval('{}')
m=vim.bindeval('messages')
def em(expr, g=globals(), l=locals()):
@@ -323,6 +340,7 @@ EOF
:py l[0] = t.t > 8 # check if the background thread is working
:py del time
:py del threading
:py del t
:$put =string(l)
:"
:" settrace
@@ -882,29 +900,6 @@ EOF
:fun D()
:endfun
py << EOF
def ee(expr, g=globals(), l=locals()):
try:
exec(expr, g, l)
except:
ei = sys.exc_info()
msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args)
msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
if expr.find('None') > -1:
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
'TypeError:("\'NoneType\' object is not iterable",)')
if expr.find('FailingNumber') > -1:
msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
'TypeError:("\'FailingNumber\' object is not iterable",)')
if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
msg = msg.replace('(\'', '("').replace('\',)', '",)')
if expr == 'fd(self=[])':
# HACK: PyMapping_Check changed meaning
msg = msg.replace('AttributeError:(\'keys\',)',
'TypeError:(\'unable to convert list to vim dictionary\',)')
cb.append(expr + ':' + msg)
else:
cb.append(expr + ':NOT FAILED')
d = vim.Dictionary()
ned = vim.Dictionary(foo='bar', baz='abcD')
dl = vim.Dictionary(a=1)
@@ -1276,6 +1271,7 @@ ee('Exe("throw \'def\'")')
ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
ee('vim.eval("xxx_unknown_function_xxx()")')
ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
del Exe
EOF

View File

@@ -53,8 +53,8 @@ None
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
Vim(python):E725:
Vim(python):E117:
l[1](1, 2, 3):error:('Vim:E725: Calling dict function without Dictionary: DictNew',)
f(1, 2, 3):error:('Vim:E117: Unknown function: New',)
[0.0, 0.0]
KeyError
TypeError
@@ -1197,6 +1197,7 @@ Exe("throw 'def'"):error:('def',)
vim.eval("Exe('throw ''ghi''')"):error:('ghi',)
vim.eval("Exe('echoerr ''jkl''')"):error:('Vim(echoerr):jkl',)
vim.eval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)
vim.eval("xxx_unknown_function_xxx()"):error:('Vim:E117: Unknown function: xxx_unknown_function_xxx',)
vim.bindeval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)
Caught KeyboardInterrupt
Running :put

View File

@@ -172,6 +172,36 @@ EOF
:unlockvar! l
:"
:" Function calls
py3 << EOF
import sys
import re
py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
def ee(expr, g=globals(), l=locals()):
cb = vim.current.buffer
try:
try:
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, 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:
cb.append(expr + ':' + repr((e.__class__, e)))
else:
cb.append(expr + ':' + repr((e.__class__, e)))
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']
:endfun
@@ -186,18 +216,10 @@ EOF
:$put =string(l)
:py3 l+=[l[0].name]
:$put =string(l)
:try
: py3 l[1](1, 2, 3)
:catch
: $put =v:exception[:13]
:endtry
:py3 ee('l[1](1, 2, 3)')
:py3 f=l[0]
:delfunction New
:try
: py3 f(1, 2, 3)
:catch
: $put =v:exception[:13]
:endtry
:py3 ee('f(1, 2, 3)')
:if has('float')
: let l=[0.0]
: py3 l=vim.bindeval('l')
@@ -315,6 +337,7 @@ EOF
:py3 l[0] = t.t > 8 # check if the background thread is working
:py3 del time
:py3 del threading
:py3 del t
:$put =string(l)
:"
:" settrace
@@ -829,33 +852,6 @@ EOF
:fun D()
:endfun
py3 << EOF
import re
py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
def ee(expr, g=globals(), l=locals()):
try:
try:
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, 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:
cb.append(expr + ':' + repr((e.__class__, e)))
else:
cb.append(expr + ':' + repr((e.__class__, e)))
else:
cb.append(expr + ':NOT FAILED')
except Exception as e:
cb.append(expr + '::' + repr((e.__class__, e)))
d = vim.Dictionary()
ned = vim.Dictionary(foo='bar', baz='abcD')
dl = vim.Dictionary(a=1)
@@ -1227,6 +1223,7 @@ ee('Exe("throw \'def\'")')
ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
ee('vim.eval("xxx_unknown_function_xxx()")')
ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
del Exe
EOF

View File

@@ -53,8 +53,8 @@ None
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
Vim(py3):E725:
Vim(py3):E117:
l[1](1, 2, 3):(<class 'vim.error'>, error('Vim:E725: Calling dict function without Dictionary: DictNew',))
f(1, 2, 3):(<class 'vim.error'>, error('Vim:E117: Unknown function: New',))
[0.0, 0.0]
KeyError
TypeError
@@ -1186,6 +1186,7 @@ Exe("throw 'def'"):(<class 'vim.error'>, error('def',))
vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',))
vim.eval("Exe('echoerr ''jkl''')"):(<class 'vim.error'>, error('Vim(echoerr):jkl',))
vim.eval("Exe('xxx_non_existent_command_xxx')"):(<class 'vim.error'>, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',))
vim.eval("xxx_unknown_function_xxx()"):(<class 'vim.error'>, error('Vim:E117: Unknown function: xxx_unknown_function_xxx',))
vim.bindeval("Exe('xxx_non_existent_command_xxx')"):(<class 'vim.error'>, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',))
Caught KeyboardInterrupt
Running :put

View File

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