Skip to content

Commit 8b386b3

Browse files
committed
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)
1 parent 9026686 commit 8b386b3

File tree

8 files changed

+179
-124
lines changed

8 files changed

+179
-124
lines changed

src/ex_eval.c

Lines changed: 83 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,17 @@ free_msglist(l)
320320
}
321321
}
322322

323+
/*
324+
* Free global "*msg_list" and the messages it contains, then set "*msg_list"
325+
* to NULL.
326+
*/
327+
void
328+
free_global_msglist()
329+
{
330+
free_msglist(*msg_list);
331+
*msg_list = NULL;
332+
}
333+
323334
/*
324335
* Throw the message specified in the call to cause_errthrow() above as an
325336
* error exception. If cstack is NULL, postpone the throw until do_cmdline()
@@ -410,66 +421,41 @@ do_intthrow(cstack)
410421
return TRUE;
411422
}
412423

413-
414424
/*
415-
* Throw a new exception. Return FAIL when out of memory or it was tried to
416-
* throw an illegal user exception. "value" is the exception string for a user
417-
* or interrupt exception, or points to a message list in case of an error
418-
* exception.
425+
* Get an exception message that is to be stored in current_exception->value.
419426
*/
420-
static int
421-
throw_exception(value, type, cmdname)
427+
char_u *
428+
get_exception_string(value, type, cmdname, should_free)
422429
void *value;
423430
int type;
424431
char_u *cmdname;
432+
int *should_free;
425433
{
426-
except_T *excp;
427-
char_u *p, *mesg, *val;
434+
char_u *ret, *mesg;
428435
int cmdlen;
429-
430-
/*
431-
* Disallow faking Interrupt or error exceptions as user exceptions. They
432-
* would be treated differently from real interrupt or error exceptions when
433-
* no active try block is found, see do_cmdline().
434-
*/
435-
if (type == ET_USER)
436-
{
437-
if (STRNCMP((char_u *)value, "Vim", 3) == 0 &&
438-
(((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':' ||
439-
((char_u *)value)[3] == '('))
440-
{
441-
EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
442-
goto fail;
443-
}
444-
}
445-
446-
excp = (except_T *)alloc((unsigned)sizeof(except_T));
447-
if (excp == NULL)
448-
goto nomem;
436+
char_u *p, *val;
449437

450438
if (type == ET_ERROR)
451439
{
452-
/* Store the original message and prefix the exception value with
453-
* "Vim:" or, if a command name is given, "Vim(cmdname):". */
454-
excp->messages = (struct msglist *)value;
455-
mesg = excp->messages->throw_msg;
440+
*should_free = FALSE;
441+
mesg = ((struct msglist *)value)->throw_msg;
456442
if (cmdname != NULL && *cmdname != NUL)
457443
{
458444
cmdlen = (int)STRLEN(cmdname);
459-
excp->value = vim_strnsave((char_u *)"Vim(",
445+
ret = vim_strnsave((char_u *)"Vim(",
460446
4 + cmdlen + 2 + (int)STRLEN(mesg));
461-
if (excp->value == NULL)
462-
goto nomem;
463-
STRCPY(&excp->value[4], cmdname);
464-
STRCPY(&excp->value[4 + cmdlen], "):");
465-
val = excp->value + 4 + cmdlen + 2;
447+
if (ret == NULL)
448+
return ret;
449+
STRCPY(&ret[4], cmdname);
450+
STRCPY(&ret[4 + cmdlen], "):");
451+
val = ret + 4 + cmdlen + 2;
466452
}
467453
else
468454
{
469-
excp->value = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));
470-
if (excp->value == NULL)
471-
goto nomem;
472-
val = excp->value + 4;
455+
ret = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));
456+
if (ret == NULL)
457+
return ret;
458+
val = ret + 4;
473459
}
474460

475461
/* msg_add_fname may have been used to prefix the message with a file
@@ -506,14 +492,65 @@ throw_exception(value, type, cmdname)
506492
}
507493
}
508494
else
509-
excp->value = value;
495+
{
496+
*should_free = FALSE;
497+
ret = (char_u *) value;
498+
}
499+
500+
return ret;
501+
}
502+
503+
504+
/*
505+
* Throw a new exception. Return FAIL when out of memory or it was tried to
506+
* throw an illegal user exception. "value" is the exception string for a
507+
* user or interrupt exception, or points to a message list in case of an
508+
* error exception.
509+
*/
510+
static int
511+
throw_exception(value, type, cmdname)
512+
void *value;
513+
int type;
514+
char_u *cmdname;
515+
{
516+
except_T *excp;
517+
int should_free;
518+
519+
/*
520+
* Disallow faking Interrupt or error exceptions as user exceptions. They
521+
* would be treated differently from real interrupt or error exceptions
522+
* when no active try block is found, see do_cmdline().
523+
*/
524+
if (type == ET_USER)
525+
{
526+
if (STRNCMP((char_u *)value, "Vim", 3) == 0
527+
&& (((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':'
528+
|| ((char_u *)value)[3] == '('))
529+
{
530+
EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
531+
goto fail;
532+
}
533+
}
534+
535+
excp = (except_T *)alloc((unsigned)sizeof(except_T));
536+
if (excp == NULL)
537+
goto nomem;
538+
539+
if (type == ET_ERROR)
540+
/* Store the original message and prefix the exception value with
541+
* "Vim:" or, if a command name is given, "Vim(cmdname):". */
542+
excp->messages = (struct msglist *)value;
543+
544+
excp->value = get_exception_string(value, type, cmdname, &should_free);
545+
if (excp->value == NULL && should_free)
546+
goto nomem;
510547

511548
excp->type = type;
512549
excp->throw_name = vim_strsave(sourcing_name == NULL
513550
? (char_u *)"" : sourcing_name);
514551
if (excp->throw_name == NULL)
515552
{
516-
if (type == ET_ERROR)
553+
if (should_free)
517554
vim_free(excp->value);
518555
goto nomem;
519556
}
@@ -2033,10 +2070,7 @@ leave_cleanup(csp)
20332070
/* If an error was about to be converted to an exception when
20342071
* enter_cleanup() was called, free the message list. */
20352072
if (msg_list != NULL)
2036-
{
2037-
free_msglist(*msg_list);
2038-
*msg_list = NULL;
2039-
}
2073+
free_global_msglist();
20402074
}
20412075

20422076
/*

src/if_py_both.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,28 @@ VimTryEnd(void)
566566
PyErr_SetNone(PyExc_KeyboardInterrupt);
567567
return -1;
568568
}
569+
else if (msg_list != NULL && *msg_list != NULL)
570+
{
571+
int should_free;
572+
char_u *msg;
573+
574+
msg = get_exception_string(*msg_list, ET_ERROR, NULL, &should_free);
575+
576+
if (msg == NULL)
577+
{
578+
PyErr_NoMemory();
579+
return -1;
580+
}
581+
582+
PyErr_SetVim((char *) msg);
583+
584+
free_global_msglist();
585+
586+
if (should_free)
587+
vim_free(msg);
588+
589+
return -1;
590+
}
569591
else if (!did_throw)
570592
return (PyErr_Occurred() ? -1 : 0);
571593
/* Python exception is preferred over vim one; unlikely to occur though */

src/proto/ex_eval.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ void update_force_abort __ARGS((void));
44
int should_abort __ARGS((int retcode));
55
int aborted_in_try __ARGS((void));
66
int cause_errthrow __ARGS((char_u *mesg, int severe, int *ignore));
7+
void free_global_msglist __ARGS((void));
78
void do_errthrow __ARGS((struct condstack *cstack, char_u *cmdname));
89
int do_intthrow __ARGS((struct condstack *cstack));
10+
char_u *get_exception_string __ARGS((void *value, int type, char_u *cmdname, int *should_free));
911
void discard_current_exception __ARGS((void));
1012
void report_make_pending __ARGS((int pending, void *value));
1113
void report_resume_pending __ARGS((int pending, void *value));

src/testdir/test86.in

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,32 @@ EOF
179179
:unlockvar! l
180180
:"
181181
:" Function calls
182+
py << EOF
183+
import sys
184+
def ee(expr, g=globals(), l=locals()):
185+
try:
186+
exec(expr, g, l)
187+
except:
188+
ei = sys.exc_info()
189+
msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args)
190+
msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
191+
if expr.find('None') > -1:
192+
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
193+
'TypeError:("\'NoneType\' object is not iterable",)')
194+
if expr.find('FailingNumber') > -1:
195+
msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
196+
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
197+
'TypeError:("\'FailingNumber\' object is not iterable",)')
198+
if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
199+
msg = msg.replace('(\'', '("').replace('\',)', '",)')
200+
if expr == 'fd(self=[])':
201+
# HACK: PyMapping_Check changed meaning
202+
msg = msg.replace('AttributeError:(\'keys\',)',
203+
'TypeError:(\'unable to convert list to vim dictionary\',)')
204+
vim.current.buffer.append(expr + ':' + msg)
205+
else:
206+
vim.current.buffer.append(expr + ':NOT FAILED')
207+
EOF
182208
:fun New(...)
183209
: return ['NewStart']+a:000+['NewEnd']
184210
:endfun
@@ -193,18 +219,10 @@ EOF
193219
:$put =string(l)
194220
:py l.extend([l[0].name])
195221
:$put =string(l)
196-
:try
197-
: py l[1](1, 2, 3)
198-
:catch
199-
: $put =v:exception[:16]
200-
:endtry
222+
:py ee('l[1](1, 2, 3)')
201223
:py f=l[0]
202224
:delfunction New
203-
:try
204-
: py f(1, 2, 3)
205-
:catch
206-
: $put =v:exception[:16]
207-
:endtry
225+
:py ee('f(1, 2, 3)')
208226
:if has('float')
209227
: let l=[0.0]
210228
: py l=vim.bindeval('l')
@@ -216,7 +234,6 @@ EOF
216234
:let messages=[]
217235
:delfunction DictNew
218236
py <<EOF
219-
import sys
220237
d=vim.bindeval('{}')
221238
m=vim.bindeval('messages')
222239
def em(expr, g=globals(), l=locals()):
@@ -323,6 +340,7 @@ EOF
323340
:py l[0] = t.t > 8 # check if the background thread is working
324341
:py del time
325342
:py del threading
343+
:py del t
326344
:$put =string(l)
327345
:"
328346
:" settrace
@@ -882,29 +900,6 @@ EOF
882900
:fun D()
883901
:endfun
884902
py << EOF
885-
def ee(expr, g=globals(), l=locals()):
886-
try:
887-
exec(expr, g, l)
888-
except:
889-
ei = sys.exc_info()
890-
msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args)
891-
msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
892-
if expr.find('None') > -1:
893-
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
894-
'TypeError:("\'NoneType\' object is not iterable",)')
895-
if expr.find('FailingNumber') > -1:
896-
msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
897-
msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
898-
'TypeError:("\'FailingNumber\' object is not iterable",)')
899-
if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
900-
msg = msg.replace('(\'', '("').replace('\',)', '",)')
901-
if expr == 'fd(self=[])':
902-
# HACK: PyMapping_Check changed meaning
903-
msg = msg.replace('AttributeError:(\'keys\',)',
904-
'TypeError:(\'unable to convert list to vim dictionary\',)')
905-
cb.append(expr + ':' + msg)
906-
else:
907-
cb.append(expr + ':NOT FAILED')
908903
d = vim.Dictionary()
909904
ned = vim.Dictionary(foo='bar', baz='abcD')
910905
dl = vim.Dictionary(a=1)
@@ -1276,6 +1271,7 @@ ee('Exe("throw \'def\'")')
12761271
ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
12771272
ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
12781273
ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
1274+
ee('vim.eval("xxx_unknown_function_xxx()")')
12791275
ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
12801276
del Exe
12811277
EOF

src/testdir/test86.ok

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ None
5353
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
5454
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
5555
[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
56-
Vim(python):E725:
57-
Vim(python):E117:
56+
l[1](1, 2, 3):error:('Vim:E725: Calling dict function without Dictionary: DictNew',)
57+
f(1, 2, 3):error:('Vim:E117: Unknown function: New',)
5858
[0.0, 0.0]
5959
KeyError
6060
TypeError
@@ -1197,6 +1197,7 @@ Exe("throw 'def'"):error:('def',)
11971197
vim.eval("Exe('throw ''ghi''')"):error:('ghi',)
11981198
vim.eval("Exe('echoerr ''jkl''')"):error:('Vim(echoerr):jkl',)
11991199
vim.eval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)
1200+
vim.eval("xxx_unknown_function_xxx()"):error:('Vim:E117: Unknown function: xxx_unknown_function_xxx',)
12001201
vim.bindeval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)
12011202
Caught KeyboardInterrupt
12021203
Running :put

0 commit comments

Comments
 (0)