8b9a1c
To: vim_dev@googlegroups.com
8b9a1c
Subject: Patch 7.4.107
8b9a1c
Fcc: outbox
8b9a1c
From: Bram Moolenaar <Bram@moolenaar.net>
8b9a1c
Mime-Version: 1.0
8b9a1c
Content-Type: text/plain; charset=UTF-8
8b9a1c
Content-Transfer-Encoding: 8bit
8b9a1c
------------
8b9a1c
8b9a1c
Patch 7.4.107
8b9a1c
Problem:    Python: When vim.eval() encounters a Vim error, a try/catch in the
8b9a1c
	    Python code doesn't catch it. (Yggdroot Chen)
8b9a1c
Solution:   Throw exceptions on errors in vim.eval(). (ZyX)
8b9a1c
Files:	    src/ex_eval.c, src/if_py_both.h, src/proto/ex_eval.pro,
8b9a1c
	    src/testdir/test86.in, src/testdir/test86.ok,
8b9a1c
	    src/testdir/test87.in, src/testdir/test87.ok
8b9a1c
8b9a1c
8b9a1c
*** ../vim-7.4.106/src/ex_eval.c	2013-06-08 15:50:28.000000000 +0200
8b9a1c
--- src/ex_eval.c	2013-11-28 16:59:09.000000000 +0100
8b9a1c
***************
8b9a1c
*** 321,326 ****
8b9a1c
--- 321,337 ----
8b9a1c
  }
8b9a1c
  
8b9a1c
  /*
8b9a1c
+  * Free global "*msg_list" and the messages it contains, then set "*msg_list"
8b9a1c
+  * to NULL.
8b9a1c
+  */
8b9a1c
+     void
8b9a1c
+ free_global_msglist()
8b9a1c
+ {
8b9a1c
+     free_msglist(*msg_list);
8b9a1c
+     *msg_list = NULL;
8b9a1c
+ }
8b9a1c
+ 
8b9a1c
+ /*
8b9a1c
   * Throw the message specified in the call to cause_errthrow() above as an
8b9a1c
   * error exception.  If cstack is NULL, postpone the throw until do_cmdline()
8b9a1c
   * has returned (see do_one_cmd()).
8b9a1c
***************
8b9a1c
*** 410,475 ****
8b9a1c
      return TRUE;
8b9a1c
  }
8b9a1c
  
8b9a1c
- 
8b9a1c
  /*
8b9a1c
!  * Throw a new exception.  Return FAIL when out of memory or it was tried to
8b9a1c
!  * throw an illegal user exception.  "value" is the exception string for a user
8b9a1c
!  * or interrupt exception, or points to a message list in case of an error
8b9a1c
!  * exception.
8b9a1c
   */
8b9a1c
!     static int
8b9a1c
! throw_exception(value, type, cmdname)
8b9a1c
      void	*value;
8b9a1c
      int		type;
8b9a1c
      char_u	*cmdname;
8b9a1c
  {
8b9a1c
!     except_T	*excp;
8b9a1c
!     char_u	*p, *mesg, *val;
8b9a1c
      int		cmdlen;
8b9a1c
! 
8b9a1c
!     /*
8b9a1c
!      * Disallow faking Interrupt or error exceptions as user exceptions.  They
8b9a1c
!      * would be treated differently from real interrupt or error exceptions when
8b9a1c
!      * no active try block is found, see do_cmdline().
8b9a1c
!      */
8b9a1c
!     if (type == ET_USER)
8b9a1c
!     {
8b9a1c
! 	if (STRNCMP((char_u *)value, "Vim", 3) == 0 &&
8b9a1c
! 		(((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':' ||
8b9a1c
! 		 ((char_u *)value)[3] == '('))
8b9a1c
! 	{
8b9a1c
! 	    EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
8b9a1c
! 	    goto fail;
8b9a1c
! 	}
8b9a1c
!     }
8b9a1c
! 
8b9a1c
!     excp = (except_T *)alloc((unsigned)sizeof(except_T));
8b9a1c
!     if (excp == NULL)
8b9a1c
! 	goto nomem;
8b9a1c
  
8b9a1c
      if (type == ET_ERROR)
8b9a1c
      {
8b9a1c
! 	/* Store the original message and prefix the exception value with
8b9a1c
! 	 * "Vim:" or, if a command name is given, "Vim(cmdname):". */
8b9a1c
! 	excp->messages = (struct msglist *)value;
8b9a1c
! 	mesg = excp->messages->throw_msg;
8b9a1c
  	if (cmdname != NULL && *cmdname != NUL)
8b9a1c
  	{
8b9a1c
  	    cmdlen = (int)STRLEN(cmdname);
8b9a1c
! 	    excp->value = vim_strnsave((char_u *)"Vim(",
8b9a1c
  					   4 + cmdlen + 2 + (int)STRLEN(mesg));
8b9a1c
! 	    if (excp->value == NULL)
8b9a1c
! 		goto nomem;
8b9a1c
! 	    STRCPY(&excp->value[4], cmdname);
8b9a1c
! 	    STRCPY(&excp->value[4 + cmdlen], "):");
8b9a1c
! 	    val = excp->value + 4 + cmdlen + 2;
8b9a1c
  	}
8b9a1c
  	else
8b9a1c
  	{
8b9a1c
! 	    excp->value = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));
8b9a1c
! 	    if (excp->value == NULL)
8b9a1c
! 		goto nomem;
8b9a1c
! 	    val = excp->value + 4;
8b9a1c
  	}
8b9a1c
  
8b9a1c
  	/* msg_add_fname may have been used to prefix the message with a file
8b9a1c
--- 421,461 ----
8b9a1c
      return TRUE;
8b9a1c
  }
8b9a1c
  
8b9a1c
  /*
8b9a1c
!  * Get an exception message that is to be stored in current_exception->value.
8b9a1c
   */
8b9a1c
!     char_u *
8b9a1c
! get_exception_string(value, type, cmdname, should_free)
8b9a1c
      void	*value;
8b9a1c
      int		type;
8b9a1c
      char_u	*cmdname;
8b9a1c
+     int		*should_free;
8b9a1c
  {
8b9a1c
!     char_u	*ret, *mesg;
8b9a1c
      int		cmdlen;
8b9a1c
!     char_u	*p, *val;
8b9a1c
  
8b9a1c
      if (type == ET_ERROR)
8b9a1c
      {
8b9a1c
! 	*should_free = FALSE;
8b9a1c
! 	mesg = ((struct msglist *)value)->throw_msg;
8b9a1c
  	if (cmdname != NULL && *cmdname != NUL)
8b9a1c
  	{
8b9a1c
  	    cmdlen = (int)STRLEN(cmdname);
8b9a1c
! 	    ret = vim_strnsave((char_u *)"Vim(",
8b9a1c
  					   4 + cmdlen + 2 + (int)STRLEN(mesg));
8b9a1c
! 	    if (ret == NULL)
8b9a1c
! 		return ret;
8b9a1c
! 	    STRCPY(&ret[4], cmdname);
8b9a1c
! 	    STRCPY(&ret[4 + cmdlen], "):");
8b9a1c
! 	    val = ret + 4 + cmdlen + 2;
8b9a1c
  	}
8b9a1c
  	else
8b9a1c
  	{
8b9a1c
! 	    ret = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg));
8b9a1c
! 	    if (ret == NULL)
8b9a1c
! 		return ret;
8b9a1c
! 	    val = ret + 4;
8b9a1c
  	}
8b9a1c
  
8b9a1c
  	/* msg_add_fname may have been used to prefix the message with a file
8b9a1c
***************
8b9a1c
*** 506,519 ****
8b9a1c
  	}
8b9a1c
      }
8b9a1c
      else
8b9a1c
! 	excp->value = value;
8b9a1c
  
8b9a1c
      excp->type = type;
8b9a1c
      excp->throw_name = vim_strsave(sourcing_name == NULL
8b9a1c
  					      ? (char_u *)"" : sourcing_name);
8b9a1c
      if (excp->throw_name == NULL)
8b9a1c
      {
8b9a1c
! 	if (type == ET_ERROR)
8b9a1c
  	    vim_free(excp->value);
8b9a1c
  	goto nomem;
8b9a1c
      }
8b9a1c
--- 492,556 ----
8b9a1c
  	}
8b9a1c
      }
8b9a1c
      else
8b9a1c
!     {
8b9a1c
! 	*should_free = FALSE;
8b9a1c
! 	ret = (char_u *) value;
8b9a1c
!     }
8b9a1c
! 
8b9a1c
!     return ret;
8b9a1c
! }
8b9a1c
! 
8b9a1c
! 
8b9a1c
! /*
8b9a1c
!  * Throw a new exception.  Return FAIL when out of memory or it was tried to
8b9a1c
!  * throw an illegal user exception.  "value" is the exception string for a
8b9a1c
!  * user or interrupt exception, or points to a message list in case of an
8b9a1c
!  * error exception.
8b9a1c
!  */
8b9a1c
!     static int
8b9a1c
! throw_exception(value, type, cmdname)
8b9a1c
!     void	*value;
8b9a1c
!     int		type;
8b9a1c
!     char_u	*cmdname;
8b9a1c
! {
8b9a1c
!     except_T	*excp;
8b9a1c
!     int		should_free;
8b9a1c
! 
8b9a1c
!     /*
8b9a1c
!      * Disallow faking Interrupt or error exceptions as user exceptions.  They
8b9a1c
!      * would be treated differently from real interrupt or error exceptions
8b9a1c
!      * when no active try block is found, see do_cmdline().
8b9a1c
!      */
8b9a1c
!     if (type == ET_USER)
8b9a1c
!     {
8b9a1c
! 	if (STRNCMP((char_u *)value, "Vim", 3) == 0
8b9a1c
! 		&& (((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':'
8b9a1c
! 		    || ((char_u *)value)[3] == '('))
8b9a1c
! 	{
8b9a1c
! 	    EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
8b9a1c
! 	    goto fail;
8b9a1c
! 	}
8b9a1c
!     }
8b9a1c
! 
8b9a1c
!     excp = (except_T *)alloc((unsigned)sizeof(except_T));
8b9a1c
!     if (excp == NULL)
8b9a1c
! 	goto nomem;
8b9a1c
! 
8b9a1c
!     if (type == ET_ERROR)
8b9a1c
! 	/* Store the original message and prefix the exception value with
8b9a1c
! 	 * "Vim:" or, if a command name is given, "Vim(cmdname):". */
8b9a1c
! 	excp->messages = (struct msglist *)value;
8b9a1c
! 
8b9a1c
!     excp->value = get_exception_string(value, type, cmdname, &should_free);
8b9a1c
!     if (excp->value == NULL && should_free)
8b9a1c
! 	goto nomem;
8b9a1c
  
8b9a1c
      excp->type = type;
8b9a1c
      excp->throw_name = vim_strsave(sourcing_name == NULL
8b9a1c
  					      ? (char_u *)"" : sourcing_name);
8b9a1c
      if (excp->throw_name == NULL)
8b9a1c
      {
8b9a1c
! 	if (should_free)
8b9a1c
  	    vim_free(excp->value);
8b9a1c
  	goto nomem;
8b9a1c
      }
8b9a1c
***************
8b9a1c
*** 2033,2042 ****
8b9a1c
  	/* If an error was about to be converted to an exception when
8b9a1c
  	 * enter_cleanup() was called, free the message list. */
8b9a1c
  	if (msg_list != NULL)
8b9a1c
! 	{
8b9a1c
! 	    free_msglist(*msg_list);
8b9a1c
! 	    *msg_list = NULL;
8b9a1c
! 	}
8b9a1c
      }
8b9a1c
  
8b9a1c
      /*
8b9a1c
--- 2070,2076 ----
8b9a1c
  	/* If an error was about to be converted to an exception when
8b9a1c
  	 * enter_cleanup() was called, free the message list. */
8b9a1c
  	if (msg_list != NULL)
8b9a1c
! 	    free_global_msglist();
8b9a1c
      }
8b9a1c
  
8b9a1c
      /*
8b9a1c
*** ../vim-7.4.106/src/if_py_both.h	2013-11-11 01:05:43.000000000 +0100
8b9a1c
--- src/if_py_both.h	2013-11-28 17:00:22.000000000 +0100
8b9a1c
***************
8b9a1c
*** 566,571 ****
8b9a1c
--- 566,593 ----
8b9a1c
  	PyErr_SetNone(PyExc_KeyboardInterrupt);
8b9a1c
  	return -1;
8b9a1c
      }
8b9a1c
+     else if (msg_list != NULL && *msg_list != NULL)
8b9a1c
+     {
8b9a1c
+ 	int	should_free;
8b9a1c
+ 	char_u	*msg;
8b9a1c
+ 
8b9a1c
+ 	msg = get_exception_string(*msg_list, ET_ERROR, NULL, &should_free);
8b9a1c
+ 
8b9a1c
+ 	if (msg == NULL)
8b9a1c
+ 	{
8b9a1c
+ 	    PyErr_NoMemory();
8b9a1c
+ 	    return -1;
8b9a1c
+ 	}
8b9a1c
+ 
8b9a1c
+ 	PyErr_SetVim((char *) msg);
8b9a1c
+ 
8b9a1c
+ 	free_global_msglist();
8b9a1c
+ 
8b9a1c
+ 	if (should_free)
8b9a1c
+ 	    vim_free(msg);
8b9a1c
+ 
8b9a1c
+ 	return -1;
8b9a1c
+     }
8b9a1c
      else if (!did_throw)
8b9a1c
  	return (PyErr_Occurred() ? -1 : 0);
8b9a1c
      /* Python exception is preferred over vim one; unlikely to occur though */
8b9a1c
*** ../vim-7.4.106/src/proto/ex_eval.pro	2013-08-10 13:37:10.000000000 +0200
8b9a1c
--- src/proto/ex_eval.pro	2013-11-28 16:56:33.000000000 +0100
8b9a1c
***************
8b9a1c
*** 4,11 ****
8b9a1c
--- 4,13 ----
8b9a1c
  int should_abort __ARGS((int retcode));
8b9a1c
  int aborted_in_try __ARGS((void));
8b9a1c
  int cause_errthrow __ARGS((char_u *mesg, int severe, int *ignore));
8b9a1c
+ void free_global_msglist __ARGS((void));
8b9a1c
  void do_errthrow __ARGS((struct condstack *cstack, char_u *cmdname));
8b9a1c
  int do_intthrow __ARGS((struct condstack *cstack));
8b9a1c
+ char_u *get_exception_string __ARGS((void *value, int type, char_u *cmdname, int *should_free));
8b9a1c
  void discard_current_exception __ARGS((void));
8b9a1c
  void report_make_pending __ARGS((int pending, void *value));
8b9a1c
  void report_resume_pending __ARGS((int pending, void *value));
8b9a1c
*** ../vim-7.4.106/src/testdir/test86.in	2013-11-11 01:05:43.000000000 +0100
8b9a1c
--- src/testdir/test86.in	2013-11-28 16:41:01.000000000 +0100
8b9a1c
***************
8b9a1c
*** 179,184 ****
8b9a1c
--- 179,210 ----
8b9a1c
  :unlockvar! l
8b9a1c
  :"
8b9a1c
  :" Function calls
8b9a1c
+ py << EOF
8b9a1c
+ import sys
8b9a1c
+ def ee(expr, g=globals(), l=locals()):
8b9a1c
+     try:
8b9a1c
+         exec(expr, g, l)
8b9a1c
+     except:
8b9a1c
+         ei = sys.exc_info()
8b9a1c
+         msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args)
8b9a1c
+         msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
8b9a1c
+         if expr.find('None') > -1:
8b9a1c
+             msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
8b9a1c
+                               'TypeError:("\'NoneType\' object is not iterable",)')
8b9a1c
+         if expr.find('FailingNumber') > -1:
8b9a1c
+             msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
8b9a1c
+             msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
8b9a1c
+                               'TypeError:("\'FailingNumber\' object is not iterable",)')
8b9a1c
+         if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
8b9a1c
+             msg = msg.replace('(\'', '("').replace('\',)', '",)')
8b9a1c
+         if expr == 'fd(self=[])':
8b9a1c
+             # HACK: PyMapping_Check changed meaning
8b9a1c
+             msg = msg.replace('AttributeError:(\'keys\',)',
8b9a1c
+                               'TypeError:(\'unable to convert list to vim dictionary\',)')
8b9a1c
+         vim.current.buffer.append(expr + ':' + msg)
8b9a1c
+     else:
8b9a1c
+         vim.current.buffer.append(expr + ':NOT FAILED')
8b9a1c
+ EOF
8b9a1c
  :fun New(...)
8b9a1c
  :   return ['NewStart']+a:000+['NewEnd']
8b9a1c
  :endfun
8b9a1c
***************
8b9a1c
*** 193,210 ****
8b9a1c
  :$put =string(l)
8b9a1c
  :py l.extend([l[0].name])
8b9a1c
  :$put =string(l)
8b9a1c
! :try
8b9a1c
! :   py l[1](1, 2, 3)
8b9a1c
! :catch
8b9a1c
! :   $put =v:exception[:16]
8b9a1c
! :endtry
8b9a1c
  :py f=l[0]
8b9a1c
  :delfunction New
8b9a1c
! :try
8b9a1c
! :   py f(1, 2, 3)
8b9a1c
! :catch
8b9a1c
! :   $put =v:exception[:16]
8b9a1c
! :endtry
8b9a1c
  :if has('float')
8b9a1c
  :   let l=[0.0]
8b9a1c
  :   py l=vim.bindeval('l')
8b9a1c
--- 219,228 ----
8b9a1c
  :$put =string(l)
8b9a1c
  :py l.extend([l[0].name])
8b9a1c
  :$put =string(l)
8b9a1c
! :py ee('l[1](1, 2, 3)')
8b9a1c
  :py f=l[0]
8b9a1c
  :delfunction New
8b9a1c
! :py ee('f(1, 2, 3)')
8b9a1c
  :if has('float')
8b9a1c
  :   let l=[0.0]
8b9a1c
  :   py l=vim.bindeval('l')
8b9a1c
***************
8b9a1c
*** 216,222 ****
8b9a1c
  :let messages=[]
8b9a1c
  :delfunction DictNew
8b9a1c
  py <
8b9a1c
- import sys
8b9a1c
  d=vim.bindeval('{}')
8b9a1c
  m=vim.bindeval('messages')
8b9a1c
  def em(expr, g=globals(), l=locals()):
8b9a1c
--- 234,239 ----
8b9a1c
***************
8b9a1c
*** 323,328 ****
8b9a1c
--- 340,346 ----
8b9a1c
  :py l[0] = t.t > 8  # check if the background thread is working
8b9a1c
  :py del time
8b9a1c
  :py del threading
8b9a1c
+ :py del t
8b9a1c
  :$put =string(l)
8b9a1c
  :"
8b9a1c
  :" settrace
8b9a1c
***************
8b9a1c
*** 882,910 ****
8b9a1c
  :fun D()
8b9a1c
  :endfun
8b9a1c
  py << EOF
8b9a1c
- def ee(expr, g=globals(), l=locals()):
8b9a1c
-     try:
8b9a1c
-         exec(expr, g, l)
8b9a1c
-     except:
8b9a1c
-         ei = sys.exc_info()
8b9a1c
-         msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args)
8b9a1c
-         msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'')
8b9a1c
-         if expr.find('None') > -1:
8b9a1c
-             msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
8b9a1c
-                               'TypeError:("\'NoneType\' object is not iterable",)')
8b9a1c
-         if expr.find('FailingNumber') > -1:
8b9a1c
-             msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'')
8b9a1c
-             msg = msg.replace('TypeError:(\'iteration over non-sequence\',)',
8b9a1c
-                               'TypeError:("\'FailingNumber\' object is not iterable",)')
8b9a1c
-         if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1:
8b9a1c
-             msg = msg.replace('(\'', '("').replace('\',)', '",)')
8b9a1c
-         if expr == 'fd(self=[])':
8b9a1c
-             # HACK: PyMapping_Check changed meaning
8b9a1c
-             msg = msg.replace('AttributeError:(\'keys\',)',
8b9a1c
-                               'TypeError:(\'unable to convert list to vim dictionary\',)')
8b9a1c
-         cb.append(expr + ':' + msg)
8b9a1c
-     else:
8b9a1c
-         cb.append(expr + ':NOT FAILED')
8b9a1c
  d = vim.Dictionary()
8b9a1c
  ned = vim.Dictionary(foo='bar', baz='abcD')
8b9a1c
  dl = vim.Dictionary(a=1)
8b9a1c
--- 900,905 ----
8b9a1c
***************
8b9a1c
*** 1276,1281 ****
8b9a1c
--- 1271,1277 ----
8b9a1c
  ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
8b9a1c
  ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
8b9a1c
  ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
8b9a1c
+ ee('vim.eval("xxx_unknown_function_xxx()")')
8b9a1c
  ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
8b9a1c
  del Exe
8b9a1c
  EOF
8b9a1c
*** ../vim-7.4.106/src/testdir/test86.ok	2013-11-11 01:05:43.000000000 +0100
8b9a1c
--- src/testdir/test86.ok	2013-11-28 16:41:01.000000000 +0100
8b9a1c
***************
8b9a1c
*** 53,60 ****
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
8b9a1c
! Vim(python):E725:
8b9a1c
! Vim(python):E117:
8b9a1c
  [0.0, 0.0]
8b9a1c
  KeyError
8b9a1c
  TypeError
8b9a1c
--- 53,60 ----
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
8b9a1c
! l[1](1, 2, 3):error:('Vim:E725: Calling dict function without Dictionary: DictNew',)
8b9a1c
! f(1, 2, 3):error:('Vim:E117: Unknown function: New',)
8b9a1c
  [0.0, 0.0]
8b9a1c
  KeyError
8b9a1c
  TypeError
8b9a1c
***************
8b9a1c
*** 1197,1202 ****
8b9a1c
--- 1197,1203 ----
8b9a1c
  vim.eval("Exe('throw ''ghi''')"):error:('ghi',)
8b9a1c
  vim.eval("Exe('echoerr ''jkl''')"):error:('Vim(echoerr):jkl',)
8b9a1c
  vim.eval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)
8b9a1c
+ vim.eval("xxx_unknown_function_xxx()"):error:('Vim:E117: Unknown function: xxx_unknown_function_xxx',)
8b9a1c
  vim.bindeval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)
8b9a1c
  Caught KeyboardInterrupt
8b9a1c
  Running :put
8b9a1c
*** ../vim-7.4.106/src/testdir/test87.in	2013-11-11 01:05:43.000000000 +0100
8b9a1c
--- src/testdir/test87.in	2013-11-28 16:41:01.000000000 +0100
8b9a1c
***************
8b9a1c
*** 172,177 ****
8b9a1c
--- 172,207 ----
8b9a1c
  :unlockvar! l
8b9a1c
  :"
8b9a1c
  :" Function calls
8b9a1c
+ py3 << EOF
8b9a1c
+ import sys
8b9a1c
+ import re
8b9a1c
+ 
8b9a1c
+ py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
8b9a1c
+ 
8b9a1c
+ def ee(expr, g=globals(), l=locals()):
8b9a1c
+     cb = vim.current.buffer
8b9a1c
+     try:
8b9a1c
+         try:
8b9a1c
+             exec(expr, g, l)
8b9a1c
+         except Exception as e:
8b9a1c
+             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."):
8b9a1c
+                 cb.append(expr + ':' + repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1]))))
8b9a1c
+             elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0:
8b9a1c
+                 cb.append(expr + ':' + repr((e.__class__, ImportError(str(e).replace("'", '')))))
8b9a1c
+             elif sys.version_info >= (3, 3) and e.__class__ is TypeError:
8b9a1c
+                 m = py33_type_error_pattern.search(str(e))
8b9a1c
+                 if m:
8b9a1c
+                     msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2))
8b9a1c
+                     cb.append(expr + ':' + repr((e.__class__, TypeError(msg))))
8b9a1c
+                 else:
8b9a1c
+                     cb.append(expr + ':' + repr((e.__class__, e)))
8b9a1c
+             else:
8b9a1c
+                 cb.append(expr + ':' + repr((e.__class__, e)))
8b9a1c
+         else:
8b9a1c
+             cb.append(expr + ':NOT FAILED')
8b9a1c
+     except Exception as e:
8b9a1c
+         cb.append(expr + '::' + repr((e.__class__, e)))
8b9a1c
+ EOF
8b9a1c
  :fun New(...)
8b9a1c
  :   return ['NewStart']+a:000+['NewEnd']
8b9a1c
  :endfun
8b9a1c
***************
8b9a1c
*** 186,203 ****
8b9a1c
  :$put =string(l)
8b9a1c
  :py3 l+=[l[0].name]
8b9a1c
  :$put =string(l)
8b9a1c
! :try
8b9a1c
! :   py3 l[1](1, 2, 3)
8b9a1c
! :catch
8b9a1c
! :   $put =v:exception[:13]
8b9a1c
! :endtry
8b9a1c
  :py3 f=l[0]
8b9a1c
  :delfunction New
8b9a1c
! :try
8b9a1c
! :   py3 f(1, 2, 3)
8b9a1c
! :catch
8b9a1c
! :   $put =v:exception[:13]
8b9a1c
! :endtry
8b9a1c
  :if has('float')
8b9a1c
  :   let l=[0.0]
8b9a1c
  :   py3 l=vim.bindeval('l')
8b9a1c
--- 216,225 ----
8b9a1c
  :$put =string(l)
8b9a1c
  :py3 l+=[l[0].name]
8b9a1c
  :$put =string(l)
8b9a1c
! :py3 ee('l[1](1, 2, 3)')
8b9a1c
  :py3 f=l[0]
8b9a1c
  :delfunction New
8b9a1c
! :py3 ee('f(1, 2, 3)')
8b9a1c
  :if has('float')
8b9a1c
  :   let l=[0.0]
8b9a1c
  :   py3 l=vim.bindeval('l')
8b9a1c
***************
8b9a1c
*** 315,320 ****
8b9a1c
--- 337,343 ----
8b9a1c
  :py3 l[0] = t.t > 8  # check if the background thread is working
8b9a1c
  :py3 del time
8b9a1c
  :py3 del threading
8b9a1c
+ :py3 del t
8b9a1c
  :$put =string(l)
8b9a1c
  :"
8b9a1c
  :" settrace
8b9a1c
***************
8b9a1c
*** 829,861 ****
8b9a1c
  :fun D()
8b9a1c
  :endfun
8b9a1c
  py3 << EOF
8b9a1c
- import re
8b9a1c
- 
8b9a1c
- py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$')
8b9a1c
- 
8b9a1c
- def ee(expr, g=globals(), l=locals()):
8b9a1c
-     try:
8b9a1c
-         try:
8b9a1c
-             exec(expr, g, l)
8b9a1c
-         except Exception as e:
8b9a1c
-             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."):
8b9a1c
-                 cb.append(expr + ':' + repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1]))))
8b9a1c
-             elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0:
8b9a1c
-                 cb.append(expr + ':' + repr((e.__class__, ImportError(str(e).replace("'", '')))))
8b9a1c
-             elif sys.version_info >= (3, 3) and e.__class__ is TypeError:
8b9a1c
-                 m = py33_type_error_pattern.search(str(e))
8b9a1c
-                 if m:
8b9a1c
-                     msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2))
8b9a1c
-                     cb.append(expr + ':' + repr((e.__class__, TypeError(msg))))
8b9a1c
-                 else:
8b9a1c
-                     cb.append(expr + ':' + repr((e.__class__, e)))
8b9a1c
-             else:
8b9a1c
-                 cb.append(expr + ':' + repr((e.__class__, e)))
8b9a1c
-         else:
8b9a1c
-             cb.append(expr + ':NOT FAILED')
8b9a1c
-     except Exception as e:
8b9a1c
-         cb.append(expr + '::' + repr((e.__class__, e)))
8b9a1c
- 
8b9a1c
  d = vim.Dictionary()
8b9a1c
  ned = vim.Dictionary(foo='bar', baz='abcD')
8b9a1c
  dl = vim.Dictionary(a=1)
8b9a1c
--- 852,857 ----
8b9a1c
***************
8b9a1c
*** 1227,1232 ****
8b9a1c
--- 1223,1229 ----
8b9a1c
  ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")')
8b9a1c
  ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")')
8b9a1c
  ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")')
8b9a1c
+ ee('vim.eval("xxx_unknown_function_xxx()")')
8b9a1c
  ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")')
8b9a1c
  del Exe
8b9a1c
  EOF
8b9a1c
*** ../vim-7.4.106/src/testdir/test87.ok	2013-11-11 01:05:43.000000000 +0100
8b9a1c
--- src/testdir/test87.ok	2013-11-28 16:41:01.000000000 +0100
8b9a1c
***************
8b9a1c
*** 53,60 ****
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
8b9a1c
! Vim(py3):E725:
8b9a1c
! Vim(py3):E117:
8b9a1c
  [0.0, 0.0]
8b9a1c
  KeyError
8b9a1c
  TypeError
8b9a1c
--- 53,60 ----
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd']
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}]
8b9a1c
  [function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New']
8b9a1c
! l[1](1, 2, 3):(<class 'vim.error'>, error('Vim:E725: Calling dict function without Dictionary: DictNew',))
8b9a1c
! f(1, 2, 3):(<class 'vim.error'>, error('Vim:E117: Unknown function: New',))
8b9a1c
  [0.0, 0.0]
8b9a1c
  KeyError
8b9a1c
  TypeError
8b9a1c
***************
8b9a1c
*** 1186,1191 ****
8b9a1c
--- 1186,1192 ----
8b9a1c
  vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',))
8b9a1c
  vim.eval("Exe('echoerr ''jkl''')"):(<class 'vim.error'>, error('Vim(echoerr):jkl',))
8b9a1c
  vim.eval("Exe('xxx_non_existent_command_xxx')"):(<class 'vim.error'>, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',))
8b9a1c
+ vim.eval("xxx_unknown_function_xxx()"):(<class 'vim.error'>, error('Vim:E117: Unknown function: xxx_unknown_function_xxx',))
8b9a1c
  vim.bindeval("Exe('xxx_non_existent_command_xxx')"):(<class 'vim.error'>, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',))
8b9a1c
  Caught KeyboardInterrupt
8b9a1c
  Running :put
8b9a1c
*** ../vim-7.4.106/src/version.c	2013-11-28 16:32:34.000000000 +0100
8b9a1c
--- src/version.c	2013-11-28 16:41:43.000000000 +0100
8b9a1c
***************
8b9a1c
*** 740,741 ****
8b9a1c
--- 740,743 ----
8b9a1c
  {   /* Add new patch number below this line */
8b9a1c
+ /**/
8b9a1c
+     107,
8b9a1c
  /**/
8b9a1c
8b9a1c
-- 
8b9a1c
hundred-and-one symptoms of being an internet addict:
8b9a1c
8. You spend half of the plane trip with your laptop on your lap...and your
8b9a1c
   child in the overhead compartment.
8b9a1c
8b9a1c
 /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net   \\\
8b9a1c
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
8b9a1c
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
8b9a1c
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///