Patch 8.2.1146
Problem:    Not enough testing for Python.
Solution:   Add more tests.  Fix uncovered problems. (Yegappan Lakshmanan,
            closes #6392)
Files:      src/if_py_both.h, src/if_python3.c, src/testdir/shared.vim,
            src/testdir/test_python2.vim, src/testdir/test_python3.vim


*** ../vim-8.2.1145/src/if_py_both.h    2020-06-29 23:07:40.046628613 +0200
--- src/if_py_both.h    2020-07-06 21:00:54.181681615 +0200
***************
*** 2250,2255 ****
--- 2250,2258 ----
  {
      ListObject        *self;
  
+     if (list == NULL)
+       return NULL;
+ 
      self = (ListObject *) subtype->tp_alloc(subtype, 0);
      if (self == NULL)
        return NULL;
***************
*** 2695,2700 ****
--- 2698,2709 ----
      if (obj == NULL)
      {
        li = list_find(l, (long) index);
+       if (li == NULL)
+       {
+           PyErr_VIM_FORMAT(N_("internal error: failed to get Vim "
+                       "list item %d"), (int) index);
+           return -1;
+       }
        vimlist_remove(l, li, li);
        clear_tv(&li->li_tv);
        vim_free(li);
***************
*** 2716,2721 ****
--- 2725,2736 ----
      else
      {
        li = list_find(l, (long) index);
+       if (li == NULL)
+       {
+           PyErr_VIM_FORMAT(N_("internal error: failed to get Vim "
+                       "list item %d"), (int) index);
+           return -1;
+       }
        clear_tv(&li->li_tv);
        copy_tv(&tv, &li->li_tv);
        clear_tv(&tv);
***************
*** 3897,3903 ****
      PyObject_GC_UnTrack((void *)(self));
      if (self->win && self->win != INVALID_WINDOW_VALUE)
        WIN_PYTHON_REF(self->win) = NULL;
!      Py_XDECREF(((PyObject *)(self->tabObject)));
      PyObject_GC_Del((void *)(self));
  }
  
--- 3912,3918 ----
      PyObject_GC_UnTrack((void *)(self));
      if (self->win && self->win != INVALID_WINDOW_VALUE)
        WIN_PYTHON_REF(self->win) = NULL;
!     Py_XDECREF(((PyObject *)(self->tabObject)));
      PyObject_GC_Del((void *)(self));
  }
  
*** ../vim-8.2.1145/src/if_python3.c    2020-03-29 20:51:03.081780739 +0200
--- src/if_python3.c    2020-07-06 21:00:54.181681615 +0200
***************
*** 1256,1261 ****
--- 1256,1265 ----
      if (PyLong_Check(idx))
      {
        long n = PyLong_AsLong(idx);
+ 
+       if (CheckBuffer((BufferObject *) self))
+           return -1;
+ 
        return RBAsItem((BufferObject *)(self), n, val, 1,
                    (Py_ssize_t)((BufferObject 
*)(self))->buf->b_ml.ml_line_count,
                    NULL);
*** ../vim-8.2.1145/src/testdir/shared.vim      2020-05-07 18:16:26.915943785 
+0200
--- src/testdir/shared.vim      2020-07-06 21:00:54.181681615 +0200
***************
*** 353,356 ****
--- 353,371 ----
    return msg_list
  endfunc
  
+ " Run the list of commands in 'cmds' and look for 'errstr' in exception.
+ " Note that assert_fails() cannot be used in some places and this function
+ " can be used.
+ func AssertException(cmds, errstr)
+   let save_exception = ''
+   try
+     for cmd in a:cmds
+       exe cmd
+     endfor
+   catch
+     let save_exception = v:exception
+   endtry
+   call assert_match(a:errstr, save_exception)
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.1145/src/testdir/test_python2.vim        2020-07-03 
21:17:31.343914450 +0200
--- src/testdir/test_python2.vim        2020-07-06 21:00:54.185681601 +0200
***************
*** 3,8 ****
--- 3,9 ----
  source check.vim
  CheckFeature python
  CheckFeature quickfix
+ source shared.vim
  
  " NOTE: This will cause errors when run under valgrind.
  " This would require recompiling Python with:
***************
*** 55,67 ****
  endfunc
  
  func Test_pydo()
!   " Check deleting lines does not trigger ml_get error.
    new
    call setline(1, ['one', 'two', 'three'])
    pydo vim.command("%d_")
    bwipe!
  
!   " Check switching to another buffer does not trigger ml_get error.
    new
    let wincount = winnr('$')
    call setline(1, ['one', 'two', 'three'])
--- 56,68 ----
  endfunc
  
  func Test_pydo()
!   " Check deleting lines does not trigger an ml_get error.
    new
    call setline(1, ['one', 'two', 'three'])
    pydo vim.command("%d_")
    bwipe!
  
!   " Check switching to another buffer does not trigger an ml_get error.
    new
    let wincount = winnr('$')
    call setline(1, ['one', 'two', 'three'])
***************
*** 69,74 ****
--- 70,88 ----
    call assert_equal(wincount + 1, winnr('$'))
    bwipe!
    bwipe!
+ 
+   " Try modifying a buffer with 'nomodifiable' set
+   set nomodifiable
+   call assert_fails('pydo toupper(line)', 'cannot save undo information')
+   set modifiable
+ 
+   " Invalid command
+   call AssertException(['pydo non_existing_cmd'],
+         \ "Vim(pydo):NameError: global name 'non_existing_cmd' is not 
defined")
+   call AssertException(["pydo raise Exception('test')"],
+         \ 'Vim(pydo):Exception: test')
+   call AssertException(["pydo {lambda}"],
+         \ 'Vim(pydo):SyntaxError: invalid syntax')
  endfunc
  
  func Test_set_cursor()
***************
*** 106,119 ****
      call assert_false(v:exception)
    endtry
  
!   let caught_vim_err = v:false
!   try
!     let x = pyeval('f.abc')
!   catch
!     call assert_match('AttributeError: abc', v:exception)
!     let caught_vim_err = v:true
!   endtry
!   call assert_equal(v:true, caught_vim_err)
  
    py del f
    delfunc s:foo
--- 120,128 ----
      call assert_false(v:exception)
    endtry
  
!   " Non-existing function attribute
!   call AssertException(["let x = pyeval('f.abc')"],
!         \ 'Vim(let):AttributeError: abc')
  
    py del f
    delfunc s:foo
***************
*** 250,255 ****
--- 259,267 ----
    py r = b.range(1, 3)
    call assert_equal(0, pyeval('r.start'))
    call assert_equal(2, pyeval('r.end'))
+   call assert_equal('one', pyeval('r[0]'))
+   call assert_equal('one', pyeval('r[-3]'))
+   call assert_equal('three', pyeval('r[-4]'))
    call assert_equal(['two', 'three'], pyeval('r[1:]'))
    py r[0] = 'green'
    call assert_equal(['green', 'two', 'three'], getline(1, '$'))
***************
*** 257,270 ****
    call assert_equal(['red', 'blue', 'three'], getline(1, '$'))
    call assert_equal(['start', 'end', '__members__'], pyeval('r.__members__'))
  
!   let caught_vim_err = v:false
!   try
!     let x = pyeval('r.abc')
!   catch
!     call assert_match('AttributeError: abc', v:exception)
!     let caught_vim_err = v:true
!   endtry
!   call assert_equal(v:true, caught_vim_err)
  
    close!
  endfunc
--- 269,290 ----
    call assert_equal(['red', 'blue', 'three'], getline(1, '$'))
    call assert_equal(['start', 'end', '__members__'], pyeval('r.__members__'))
  
!   " try different invalid start/end index for the range slice
!   %d
!   call setline(1, ['one', 'two', 'three'])
!   py r[-10:1] = ["a"]
!   py r[10:12] = ["b"]
!   py r[-10:-9] = ["c"]
!   py r[1:0] = ["d"]
!   call assert_equal(['c', 'd', 'a', 'two', 'three', 'b'], getline(1, '$'))
! 
!   " FIXME: The following code triggers ml_get errors
!   " %d
!   " let x = pyeval('r[:]')
! 
!   " Non-existing range attribute
!   call AssertException(["let x = pyeval('r.abc')"],
!         \ 'Vim(let):AttributeError: abc')
  
    close!
  endfunc
***************
*** 273,305 ****
  func Test_python_tabpage()
    tabnew
    py t = vim.tabpages[1]
    tabclose
!   let caught_vim_err = v:false
!   try
!     let n = pyeval('t.number')
!   catch
!     call assert_match('vim.error: attempt to refer to deleted tab page',
!           \ v:exception)
!     let caught_vim_err = v:true
!   endtry
!   call assert_equal(v:true, caught_vim_err)
    %bw!
  endfunc
  
  " Test for the python window object
  func Test_python_window()
!   new
    py w = vim.current.window
    close
!   let caught_vim_err = v:false
!   try
!     let n = pyeval('w.number')
!   catch
!     call assert_match('vim.error: attempt to refer to deleted window',
!           \ v:exception)
!     let caught_vim_err = v:true
!   endtry
!   call assert_equal(v:true, caught_vim_err)
  endfunc
  
  " Test for the python List object
--- 293,342 ----
  func Test_python_tabpage()
    tabnew
    py t = vim.tabpages[1]
+   py wl = t.windows
    tabclose
!   " Accessing a closed tabpage
!   call AssertException(["let n = pyeval('t.number')"],
!         \ 'Vim(let):vim.error: attempt to refer to deleted tab page')
!   call AssertException(["let n = pyeval('len(wl)')"],
!         \ 'Vim(let):vim.error: attempt to refer to deleted tab page')
!   call AssertException(["py w = wl[0]"],
!         \ 'Vim(python):vim.error: attempt to refer to deleted tab page')
!   call AssertException(["py vim.current.tabpage = t"],
!         \ 'Vim(python):vim.error: attempt to refer to deleted tab page')
!   call assert_match('<tabpage object (deleted)', pyeval('repr(t)'))
    %bw!
  endfunc
  
  " Test for the python window object
  func Test_python_window()
!   " Test for setting the window height
!   10new
!   py vim.current.window.height = 5
!   call assert_equal(5, winheight(0))
! 
!   " Test for setting the window width
!   10vnew
!   py vim.current.window.width = 6
!   call assert_equal(6, winwidth(0))
! 
!   " Try accessing a closed window
    py w = vim.current.window
+   py wopts = w.options
    close
!   " Access the attributes of a closed window
!   call AssertException(["let n = pyeval('w.number')"],
!         \ 'Vim(let):vim.error: attempt to refer to deleted window')
!   call AssertException(["py w.height = 5"],
!         \ 'Vim(python):vim.error: attempt to refer to deleted window')
!   call AssertException(["py vim.current.window = w"],
!         \ 'Vim(python):vim.error: attempt to refer to deleted window')
!   " Try to set one of the options of the closed window
!   " FIXME: The following causes ASAN failure
!   "call AssertException(["py wopts['list'] = False"],
!   "      \ 'vim.error: problem while switching windows')
!   call assert_match('<window object (deleted)', pyeval("repr(w)"))
!   %bw!
  endfunc
  
  " Test for the python List object
***************
*** 308,313 ****
--- 345,365 ----
    py pl = vim.bindeval('l')
    call assert_equal(['locked', '__members__'], pyeval('pl.__members__'))
  
+   " Try to convert a null List
+   call AssertException(["py t = vim.eval('test_null_list()')"],
+         \ 'Vim(python):SystemError: error return without exception set')
+ 
+   " Try to convert a List with a null List item
+   call AssertException(["py t = vim.eval('[test_null_list()]')"],
+         \ 'Vim(python):SystemError: error return without exception set')
+ 
+   " Try to bind a null List variable
+   let cmds =<< trim END
+     let l = test_null_list()
+     py ll = vim.bindeval('l')
+   END
+   call AssertException(cmds, 'Vim(python):SystemError: error return without 
exception set')
+ 
    let l = []
    py l = vim.bindeval('l')
    py f = vim.bindeval('function("strlen")')
***************
*** 322,327 ****
--- 374,389 ----
    call assert_equal([0, "as'd", [1, 2, function("strlen"), {'a': 1}]], l)
    py l[-2] = f
    call assert_equal([0, function("strlen"), [1, 2, function("strlen"), {'a': 
1}]], l)
+ 
+   " appending to a list
+   let l = [1, 2]
+   py ll = vim.bindeval('l')
+   py ll[2] = 8
+   call assert_equal([1, 2, 8], l)
+ 
+   " Using dict as an index
+   call AssertException(['py ll[{}] = 10'],
+         \ 'Vim(python):TypeError: index must be int or slice, not dict')
  endfunc
  
  " Test for the python Dict object
***************
*** 330,335 ****
--- 392,417 ----
    py pd = vim.bindeval('d')
    call assert_equal(['locked', 'scope', '__members__'],
          \ pyeval('pd.__members__'))
+ 
+   " Try to convert a null Dict
+   call AssertException(["py t = vim.eval('test_null_dict()')"],
+         \ 'Vim(python):SystemError: error return without exception set')
+ 
+   " Try to convert a Dict with a null List value
+   call AssertException(["py t = vim.eval(\"{'a' : test_null_list()}\")"],
+         \ 'Vim(python):SystemError: error return without exception set')
+ 
+   " Try to convert a Dict with a null string key
+   py t = vim.eval("{test_null_string() : 10}")
+   call assert_fails("let d = pyeval('t')", 'E859:')
+ 
+   " Dict length
+   let d = {'a' : 10, 'b' : 20}
+   py d = vim.bindeval('d')
+   call assert_equal(2, pyeval('len(d)'))
+ 
+   " Deleting an non-existing key
+   call AssertException(["py del d['c']"], "Vim(python):KeyError: 'c'")
  endfunc
  
  " Extending Dictionary directly with different types
***************
*** 356,361 ****
--- 438,449 ----
      di.sort(cmpfun)
    EOF
  
+   " Try extending a locked dictionary
+   lockvar d
+   call AssertException(["py d.update({'b' : 20})"],
+         \ 'Vim(python):vim.error: dictionary is locked')
+   unlockvar d
+ 
    call assert_equal(1, pyeval("d['f'](self={})"))
    call assert_equal("['-1', '0', '1', 'b', 'f']", pyeval('repr(dk)'))
    call assert_equal("['asd', -1L, <vim.Function '1'>, <vim.dictionary object 
at >, <vim.list object at >]", substitute(pyeval('repr(dv)'),'0x\x\+','','g'))
***************
*** 552,557 ****
--- 640,650 ----
    EOF
    call assert_equal(['', "l[2] threw vim.error: error:('list is locked',)"],
          \ getline(1, '$'))
+ 
+   " Try to concatenate a locked list
+   call AssertException(['py l += [4, 5]'],
+         \ 'Vim(python):vim.error: list is locked')
+ 
    call assert_equal([0, 1, 2, 3], l)
    unlockvar! l
    close!
***************
*** 665,670 ****
--- 758,768 ----
    call assert_equal([0], l)
    call assert_equal([1], ll)
    unlet l ll
+ 
+   " Try changing an attribute of a fixed list
+   py a = vim.bindeval('v:argv')
+   call AssertException(['py a.locked = 0'],
+         \ 'Vim(python):TypeError: cannot modify fixed list')
  endfunc
  
  " Test for pyeval()
***************
*** 679,720 ****
    call assert_equal(v:none, pyeval('None'))
    call assert_equal('', v:errmsg)
  
    if has('float')
      call assert_equal(0.0, pyeval('0.0'))
    endif
  
!   " Invalid values:
!   let caught_859 = 0
!   try
!     let v = pyeval('"\0"')
!   catch /E859:/
!     let caught_859 = 1
!   endtry
!   call assert_equal(1, caught_859)
! 
!   let caught_859 = 0
!   try
!     let v = pyeval('{"\0" : 1}')
!   catch /E859:/
!     let caught_859 = 1
!   endtry
!   call assert_equal(1, caught_859)
! 
!   let caught_nameerr = 0
!   try
!     let v = pyeval("undefined_name")
!   catch /NameError: name 'undefined_name'/
!     let caught_nameerr = 1
!   endtry
!   call assert_equal(1, caught_nameerr)
! 
!   let caught_859 = 0
!   try
!     let v = pyeval("vim")
!   catch /E859:/
!     let caught_859 = 1
!   endtry
!   call assert_equal(1, caught_859)
  endfunc
  
  " threading
--- 777,814 ----
    call assert_equal(v:none, pyeval('None'))
    call assert_equal('', v:errmsg)
  
+   py v = vim.eval('test_null_function()')
+   call assert_equal(v:none, pyeval('v'))
+ 
    if has('float')
      call assert_equal(0.0, pyeval('0.0'))
    endif
  
!   " Evaluate an invalid values
!   call AssertException(['let v = pyeval(''"\0"'')'], 'E859:')
!   call AssertException(['let v = pyeval(''{"\0" : 1}'')'], 'E859:')
!   call AssertException(['let v = pyeval("undefined_name")'],
!         \ "Vim(let):NameError: name 'undefined_name' is not defined")
!   call AssertException(['let v = pyeval("vim")'], 'E859:')
! endfunc
! 
! " Test for vim.bindeval()
! func Test_python_vim_bindeval()
!   " Float
!   let f = 3.14
!   py f = vim.bindeval('f')
!   call assert_equal(3.14, pyeval('f'))
! 
!   " Blob
!   let b = 0z12
!   py b = vim.bindeval('b')
!   call assert_equal("\x12", pyeval('b'))
! 
!   " Bool
!   call assert_equal(1, pyeval("vim.bindeval('v:true')"))
!   call assert_equal(0, pyeval("vim.bindeval('v:false')"))
!   call assert_equal(v:none, pyeval("vim.bindeval('v:null')"))
!   call assert_equal(v:none, pyeval("vim.bindeval('v:none')"))
  endfunc
  
  " threading
***************
*** 812,818 ****
--- 906,929 ----
    call assert_equal([0, 2, 4], pyeval('l'))
    py l = ll[4:2:1]
    call assert_equal([], pyeval('l'))
+ 
+   " Error case: Use an invalid index
+   call AssertException(['py ll[-10] = 5'], 'Vim(python):vim.error: internal 
error:')
+ 
+   " Use a step value of 0
+   call AssertException(['py ll[0:3:0] = [1, 2, 3]'],
+         \ 'Vim(python):ValueError: slice step cannot be zero')
+ 
+   " Error case: Invalid slice type
+   call AssertException(["py x = ll['abc']"],
+         \ 'Vim(python):TypeError: index must be int or slice, not str')
    py del l
+ 
+   " Error case: List with a null list item
+   let l = [test_null_list()]
+   py ll = vim.bindeval('l')
+   call AssertException(["py x = ll[:]"],
+         \ 'Vim(python):SystemError: error return without exception set')
  endfunc
  
  " Vars
***************
*** 1249,1254 ****
--- 1360,1383 ----
  
    call assert_equal(expected, g:res)
    unlet g:res
+ 
+   call assert_equal(0, pyeval("'' in vim.options"))
+ 
+   " use an empty key to index vim.options
+   call AssertException(["let v = pyeval(\"vim.options['']\")"],
+         \ 'Vim(let):ValueError: empty keys are not allowed')
+   call AssertException(["py vim.current.window.options[''] = 0"],
+         \ 'Vim(python):ValueError: empty keys are not allowed')
+   call AssertException(["py vim.current.window.options[{}] = 0"],
+         \ 'Vim(python):TypeError: expected str() or unicode() instance, but 
got dict')
+ 
+   " set one of the number options to a very large number
+   let cmd = ["py vim.options['previewheight'] = 9999999999999999"]
+   call AssertException(cmd, 'OverflowError:')
+ 
+   " unset a global-local string option
+   call AssertException(["py del vim.options['errorformat']"],
+         \ 'Vim(python):ValueError: unable to unset global option errorformat')
  endfunc
  
  " Test for vim.buffer object
***************
*** 1267,1277 ****
--- 1396,1421 ----
    py b = vim.current.buffer
    wincmd w
  
+   " Test for getting lines from the buffer using a slice
+   call assert_equal(['First line'], pyeval('b[-10:1]'))
+   call assert_equal(['Third line'], pyeval('b[2:10]'))
+   call assert_equal([], pyeval('b[2:0]'))
+   call assert_equal([], pyeval('b[10:12]'))
+   call assert_equal([], pyeval('b[-10:-8]'))
+ 
    " Tests BufferAppend and BufferItem
    py cb.append(b[0])
    call assert_equal(['First line'], getbufline(bnr1, 2))
    %d
  
+   " Try to append using out-of-range line number
+   call AssertException(["py b.append('abc', 10)"],
+         \ 'Vim(python):IndexError: line number out of range')
+ 
+   " Append a non-string item
+   call AssertException(["py b.append([22])"],
+         \ 'Vim(python):TypeError: expected str() or unicode() instance, but 
got int')
+ 
    " Tests BufferSlice and BufferAssSlice
    py cb.append('abc5') # Will be overwritten
    py cb[-1:] = b[:-2]
***************
*** 1363,1373 ****
--- 1507,1568 ----
    EOF
    call assert_equal([''], getline(1, '$'))
  
+   " Delete all the lines in a buffer
+   call setline(1, ['a', 'b', 'c'])
+   py vim.current.buffer[:] = []
+   call assert_equal([''], getline(1, '$'))
+ 
+   " Test for modifying a 'nomodifiable' buffer
+   setlocal nomodifiable
+   call AssertException(["py vim.current.buffer[0] = 'abc'"],
+         \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' 
is off")
+   call AssertException(["py vim.current.buffer[0] = None"],
+         \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' 
is off")
+   call AssertException(["py vim.current.buffer[:] = None"],
+         \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' 
is off")
+   call AssertException(["py vim.current.buffer[:] = []"],
+         \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' 
is off")
+   call AssertException(["py vim.current.buffer.append('abc')"],
+         \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' 
is off")
+   call AssertException(["py vim.current.buffer.append([])"],
+         \ "Vim(python):vim.error: Vim:E21: Cannot make changes, 'modifiable' 
is off")
+   setlocal modifiable
+ 
    augroup BUFS
      autocmd!
    augroup END
    augroup! BUFS
    %bw!
+ 
+   " Range object for a deleted buffer
+   new Xfile
+   call setline(1, ['one', 'two', 'three'])
+   py b = vim.current.buffer
+   py r = vim.current.buffer.range(0, 2)
+   call assert_equal('<range Xfile (0:2)>', pyeval('repr(r)'))
+   %bw!
+   call AssertException(['py r[:] = []'],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
+   call assert_match('<buffer object (deleted)', pyeval('repr(b)'))
+   call assert_match('<range object (for deleted buffer)', pyeval('repr(r)'))
+   call AssertException(["let n = pyeval('len(r)')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py r.append('abc')"],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
+ 
+   " object for a deleted buffer
+   call AssertException(["py b[0] = 'one'"],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py b.append('one')"],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["let n = pyeval('len(b)')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py pos = b.mark('a')"],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py vim.current.buffer = b"],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py rn = b.range(0, 2)"],
+         \ 'Vim(python):vim.error: attempt to refer to deleted buffer')
  endfunc
  
  " Test vim.buffers object
***************
*** 1468,1473 ****
--- 1663,1670 ----
    vnew b.2
    vnew c.2
  
+   call assert_equal(4, pyeval('vim.current.window.tabpage.number'))
+ 
    py << trim EOF
      cb.append('Number of tabs: ' + str(len(vim.tabpages)))
      cb.append('Current tab pages:')
***************
*** 1617,1622 ****
--- 1814,1821 ----
      Current line: 'python interface'
    END
    call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$'))
+   py vim.current.line = 'one line'
+   call assert_equal('one line', getline('.'))
    call deletebufline(bufnr('Xfile'), 1, '$')
  
    py << trim EOF
***************
*** 1739,1751 ****
  
  " Test vim.Function
  func Test_python_vim_func()
!   function Args(...)
      return a:000
!   endfunction
  
!   function SelfArgs(...) dict
      return [a:000, self]
!   endfunction
  
    " The following four lines should not crash
    let Pt = function('tr', [[]], {'l': []})
--- 1938,1950 ----
  
  " Test vim.Function
  func Test_python_vim_func()
!   func Args(...)
      return a:000
!   endfunc
  
!   func SelfArgs(...) dict
      return [a:000, self]
!   endfunc
  
    " The following four lines should not crash
    let Pt = function('tr', [[]], {'l': []})
***************
*** 1753,1758 ****
--- 1952,1959 ----
    unlet Pt
    py del Pt
  
+   call assert_equal(3, pyeval('vim.strwidth("a\tb")'))
+ 
    %bw!
    py cb = vim.current.buffer
    py << trim EOF
***************
*** 2129,2137 ****
  " Test subclassing
  func Test_python_subclass()
    new
!   fun Put(...)
!      return a:000
!   endfun
  
    py << trim EOF
      class DupDict(vim.Dictionary):
--- 2330,2338 ----
  " Test subclassing
  func Test_python_subclass()
    new
!   func Put(...)
!     return a:000
!   endfunc
  
    py << trim EOF
      class DupDict(vim.Dictionary):
***************
*** 2211,2221 ****
  
  " Test errors
  func Test_python_errors()
!   fun F() dict
!   endfun
  
!   fun D()
!   endfun
  
    new
    py cb = vim.current.buffer
--- 2412,2422 ----
  
  " Test errors
  func Test_python_errors()
!   func F() dict
!   endfunc
  
!   func D()
!   endfunc
  
    new
    py cb = vim.current.buffer
***************
*** 2537,2544 ****
      ee('vim.windows[1000]')
      cb.append("> Buffer")
      cb.append(">> StringToLine (indirect)")
-     ee('vim.current.buffer[0] = u"\\na"')
      ee('vim.current.buffer[0] = "\\na"')
      cb.append(">> SetBufferLine (indirect)")
      ee('vim.current.buffer[0] = True')
      cb.append(">> SetBufferLineList (indirect)")
--- 2738,2745 ----
      ee('vim.windows[1000]')
      cb.append("> Buffer")
      cb.append(">> StringToLine (indirect)")
      ee('vim.current.buffer[0] = "\\na"')
+     ee('vim.current.buffer[0] = u"\\na"')
      cb.append(">> SetBufferLine (indirect)")
      ee('vim.current.buffer[0] = True')
      cb.append(">> SetBufferLineList (indirect)")
***************
*** 3360,3367 ****
      vim.windows[1000]:IndexError:('no such window',)
      > Buffer
      >> StringToLine (indirect)
-     vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',)
      vim.current.buffer[0] = "\na":error:('string cannot contain newlines',)
      >> SetBufferLine (indirect)
      vim.current.buffer[0] = True:TypeError:('bad argument type for built-in 
operation',)
      >> SetBufferLineList (indirect)
--- 3561,3568 ----
      vim.windows[1000]:IndexError:('no such window',)
      > Buffer
      >> StringToLine (indirect)
      vim.current.buffer[0] = "\na":error:('string cannot contain newlines',)
+     vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',)
      >> SetBufferLine (indirect)
      vim.current.buffer[0] = True:TypeError:('bad argument type for built-in 
operation',)
      >> SetBufferLineList (indirect)
***************
*** 3442,3447 ****
--- 3643,3649 ----
      cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, 
'/')[-len('modulex/topmodule/__init__.py'):])
      cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, 
'/')[-len('modulex/topmodule/submodule/__init__.py'):])
      cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, 
'/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):])
+ 
      del before
      del after
      del d
***************
*** 3463,3475 ****
    END
    call assert_equal(expected, getline(2, '$'))
    close!
  endfunc
  
  " Test exceptions
  func Test_python_exception()
!   fun Exe(e)
      execute a:e
!   endfun
  
    new
    py cb = vim.current.buffer
--- 3665,3680 ----
    END
    call assert_equal(expected, getline(2, '$'))
    close!
+ 
+   " Try to import a non-existing moudle with a dot (.)
+   call AssertException(['py import a.b.c'], 'ImportError:')
  endfunc
  
  " Test exceptions
  func Test_python_exception()
!   func Exe(e)
      execute a:e
!   endfunc
  
    new
    py cb = vim.current.buffer
*** ../vim-8.2.1145/src/testdir/test_python3.vim        2020-07-03 
21:17:31.343914450 +0200
--- src/testdir/test_python3.vim        2020-07-06 21:00:54.185681601 +0200
***************
*** 2,7 ****
--- 2,8 ----
  
  source check.vim
  CheckFeature python3
+ source shared.vim
  
  " This function should be called first. This sets up python functions used by
  " the other tests.
***************
*** 73,79 ****
  
  func Test_py3do()
    " Check deleting lines does not trigger an ml_get error.
-   py3 import vim
    new
    call setline(1, ['one', 'two', 'three'])
    py3do vim.command("%d_")
--- 74,79 ----
***************
*** 87,97 ****
    call assert_equal(wincount + 1, winnr('$'))
    bwipe!
    bwipe!
  endfunc
  
  func Test_set_cursor()
    " Check that setting the cursor position works.
-   py3 import vim
    new
    call setline(1, ['first line', 'second line'])
    normal gg
--- 87,109 ----
    call assert_equal(wincount + 1, winnr('$'))
    bwipe!
    bwipe!
+ 
+   " Try modifying a buffer with 'nomodifiable' set
+   set nomodifiable
+   call assert_fails('py3do toupper(line)', 'cannot save undo information')
+   set modifiable
+ 
+   " Invalid command
+   call AssertException(['py3do non_existing_cmd'],
+         \ "Vim(py3do):NameError: name 'non_existing_cmd' is not defined")
+   call AssertException(["py3do raise Exception('test')"],
+         \ 'Vim(py3do):Exception: test')
+   call AssertException(["py3do {lambda}"],
+         \ 'Vim(py3do):SyntaxError: invalid syntax')
  endfunc
  
  func Test_set_cursor()
    " Check that setting the cursor position works.
    new
    call setline(1, ['first line', 'second line'])
    normal gg
***************
*** 105,111 ****
  
  func Test_vim_function()
    " Check creating vim.Function object
-   py3 import vim
  
    func s:foo()
      return matchstr(expand('<sfile>'), '<SNR>\zs\d\+_foo$')
--- 117,122 ----
***************
*** 126,139 ****
      call assert_false(v:exception)
    endtry
  
!   let caught_vim_err = v:false
!   try
!     let x = py3eval('f.abc')
!   catch
!     call assert_match("AttributeError: 'vim.function' object has no attribute 
'abc'", v:exception)
!     let caught_vim_err = v:true
!   endtry
!   call assert_equal(v:true, caught_vim_err)
  
    py3 del f
    delfunc s:foo
--- 137,145 ----
      call assert_false(v:exception)
    endtry
  
!   " Non-existing function attribute
!   call AssertException(["let x = py3eval('f.abc')"],
!         \ "Vim(let):AttributeError: 'vim.function' object has no attribute 
'abc'")
  
    py3 del f
    delfunc s:foo
***************
*** 148,154 ****
  endfunc
  
  func _SetUpHiddenBuffer()
-   py3 import vim
    new
    edit hidden
    setlocal bufhidden=hide
--- 154,159 ----
***************
*** 198,204 ****
  endfunc
  
  func _SetUpVisibleBuffer()
-   py3 import vim
    new
    let lnum = 0
    while lnum < 10
--- 203,208 ----
***************
*** 303,310 ****
  
    call assert_fails('py3 r[3] = "x"', 'IndexError: line number out of range')
    call assert_fails('py3 x = r[3]', 'IndexError: line number out of range')
!   call assert_fails('py3 r["a"] = "x"', 'TypeError')
!   call assert_fails('py3 x = r["a"]', 'TypeError')
  
    py3 del r[:]
    call assert_equal(['1', '5', '6'], getline(1, '$'))
--- 307,314 ----
  
    call assert_fails('py3 r[3] = "x"', 'IndexError: line number out of range')
    call assert_fails('py3 x = r[3]', 'IndexError: line number out of range')
!   call assert_fails('py3 r["a"] = "x"', 'TypeError: index must be int or 
slice, not str')
!   call assert_fails('py3 x = r["a"]', 'TypeError: index must be int or slice, 
not str')
  
    py3 del r[:]
    call assert_equal(['1', '5', '6'], getline(1, '$'))
***************
*** 431,438 ****
--- 435,546 ----
    call assert_equal('ABCDE', pyxeval('s'))
  endfunc
  
+ " Test for the buffer range object
+ func Test_python3_range2()
+   new
+   call setline(1, ['one', 'two', 'three'])
+   py3 b = vim.current.buffer
+   py3 r = b.range(1, 3)
+   call assert_equal(0, py3eval('r.start'))
+   call assert_equal(2, py3eval('r.end'))
+   call assert_equal('one', py3eval('r[0]'))
+   call assert_equal('one', py3eval('r[-3]'))
+   call AssertException(["let x = py3eval('r[-4]')"],
+         \ 'Vim(let):IndexError: line number out of range')
+   call assert_equal(['two', 'three'], py3eval('r[1:]'))
+   py3 r[0] = 'green'
+   call assert_equal(['green', 'two', 'three'], getline(1, '$'))
+   py3 r[0:2] = ['red', 'blue']
+   call assert_equal(['red', 'blue', 'three'], getline(1, '$'))
+ 
+   " try different invalid start/end index for the range slice
+   %d
+   call setline(1, ['one', 'two', 'three'])
+   py3 r[-10:1] = ["a"]
+   py3 r[10:12] = ["b"]
+   py3 r[-10:-9] = ["c"]
+   py3 r[1:0] = ["d"]
+   call assert_equal(['c', 'd', 'a', 'two', 'three', 'b'], getline(1, '$'))
+ 
+   " FIXME: The following code triggers ml_get errors
+   " %d
+   " let x = py3eval('r[:]')
+ 
+   " Non-existing range attribute
+   call AssertException(["let x = py3eval('r.abc')"],
+         \ "Vim(let):AttributeError: 'vim.range' object has no attribute 
'abc'")
+ 
+   close!
+ endfunc
+ 
+ " Test for the python tabpage object
+ func Test_python3_tabpage()
+   tabnew
+   py3 t = vim.tabpages[1]
+   py3 wl = t.windows
+   tabclose
+   " Accessing a closed tabpage
+   call AssertException(["let n = py3eval('t.number')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted tab page')
+   call AssertException(["let n = py3eval('len(wl)')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted tab page')
+   call AssertException(["py3 w = wl[0]"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted tab page')
+   call AssertException(["py3 vim.current.tabpage = t"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted tab page')
+   call assert_match('<tabpage object (deleted)', py3eval('repr(t)'))
+   %bw!
+ endfunc
+ 
+ " Test for the python window object
+ func Test_python3_window()
+   " Test for setting the window height
+   10new
+   py3 vim.current.window.height = 5
+   call assert_equal(5, winheight(0))
+ 
+   " Test for setting the window width
+   10vnew
+   py3 vim.current.window.width = 6
+   call assert_equal(6, winwidth(0))
+ 
+   " Try accessing a closed window
+   py3 w = vim.current.window
+   py3 wopts = w.options
+   close
+   " Access the attributes of a closed window
+   call AssertException(["let n = py3eval('w.number')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted window')
+   call AssertException(["py3 w.height = 5"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted window')
+   call AssertException(["py3 vim.current.window = w"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted window')
+   " Try to set one of the options of the closed window
+   " FIXME: The following causes ASAN failure
+   "call AssertException(["py3 wopts['list'] = False"],
+   "      \ 'Vim(py3):vim.error: problem while switching windows')
+   call assert_match('<window object (deleted)', py3eval("repr(w)"))
+   %bw!
+ endfunc
+ 
  " Test for the python List object
  func Test_python3_list()
+   " Try to convert a null List
+   call AssertException(["py3 t = vim.eval('test_null_list()')"],
+         \ 'Vim(py3):SystemError: <built-in function eval> returned NULL 
without setting an error')
+ 
+   " Try to convert a List with a null List item
+   call AssertException(["py3 t = vim.eval('[test_null_list()]')"],
+         \ 'Vim(py3):SystemError: <built-in function eval> returned NULL 
without setting an error')
+ 
+   " Try to bind a null List variable
+   let cmds =<< trim END
+     let l = test_null_list()
+     py3 ll = vim.bindeval('l')
+   END
+   call AssertException(cmds,
+         \ 'Vim(py3):SystemError: <built-in function bindeval> returned NULL 
without setting an error')
+ 
    let l = []
    py3 l = vim.bindeval('l')
    py3 f = vim.bindeval('function("strlen")')
***************
*** 447,452 ****
--- 555,593 ----
    call assert_equal([0, "as'd", [1, 2, function("strlen"), {'a': 1}]], l)
    py3 l[-2] = f
    call assert_equal([0, function("strlen"), [1, 2, function("strlen"), {'a': 
1}]], l)
+ 
+   " appending to a list
+   let l = [1, 2]
+   py3 ll = vim.bindeval('l')
+   py3 ll[2] = 8
+   call assert_equal([1, 2, 8], l)
+ 
+   " Using dict as an index
+   call AssertException(['py3 ll[{}] = 10'],
+         \ 'Vim(py3):TypeError: index must be int or slice, not dict')
+ endfunc
+ 
+ " Test for the python Dict object
+ func Test_python3_dict()
+   " Try to convert a null Dict
+   call AssertException(["py3 t = vim.eval('test_null_dict()')"],
+         \ 'Vim(py3):SystemError: <built-in function eval> returned NULL 
without setting an error')
+ 
+   " Try to convert a Dict with a null List value
+   call AssertException(["py3 t = vim.eval(\"{'a' : test_null_list()}\")"],
+         \ 'Vim(py3):SystemError: <built-in function eval> returned NULL 
without setting an error')
+ 
+   " Try to convert a Dict with a null string key
+   py3 t = vim.eval("{test_null_string() : 10}")
+   call assert_fails("let d = py3eval('t')", 'E859:')
+ 
+   " Dict length
+   let d = {'a' : 10, 'b' : 20}
+   py3 d = vim.bindeval('d')
+   call assert_equal(2, py3eval('len(d)'))
+ 
+   " Deleting an non-existing key
+   call AssertException(["py3 del d['c']"], "Vim(py3):KeyError: 'c'")
  endfunc
  
  " Extending Dictionary directly with different types
***************
*** 472,477 ****
--- 613,624 ----
      di.sort(key=repr)
    EOF
  
+   " Try extending a locked dictionary
+   lockvar d
+   call AssertException(["py3 d.update({'b' : 20})"],
+         \ 'Vim(py3):vim.error: dictionary is locked')
+   unlockvar d
+ 
    call assert_equal(1, py3eval("d['f'](self={})"))
    call assert_equal("[b'-1', b'0', b'1', b'b', b'f']", py3eval('repr(dk)'))
    call assert_equal("[-1, <vim.Function '1'>, <vim.dictionary object at >, 
<vim.list object at >, b'asd']", 
substitute(py3eval('repr(dv)'),'0x\x\+','','g'))
***************
*** 668,673 ****
--- 815,824 ----
    EOF
    call assert_equal(['', "l[2] threw vim.error: error:('list is locked',)"],
          \ getline(1, '$'))
+ 
+   " Try to concatenate a locked list
+   call AssertException(['py3 l += [4, 5]'], 'Vim(py3):vim.error: list is 
locked')
+ 
    call assert_equal([0, 1, 2, 3], l)
    unlockvar! l
    close!
***************
*** 785,790 ****
--- 936,946 ----
    call assert_equal([0], l)
    call assert_equal([1], ll)
    unlet l ll
+ 
+   " Try changing an attribute of a fixed list
+   py3 a = vim.bindeval('v:argv')
+   call AssertException(['py3 a.locked = 0'],
+         \ 'Vim(py3):TypeError: cannot modify fixed list')
  endfunc
  
  " Test for py3eval()
***************
*** 799,846 ****
    call assert_equal(v:none, py3eval('None'))
    call assert_equal('', v:errmsg)
  
    if has('float')
      call assert_equal(0.0, py3eval('0.0'))
    endif
  
!   " Invalid values:
!   let caught_859 = 0
!   try
!     let v = py3eval('"\0"')
!   catch /E859:/
!     let caught_859 = 1
!   endtry
!   call assert_equal(1, caught_859)
! 
!   let caught_859 = 0
!   try
!     let v = py3eval('{"\0" : 1}')
!   catch /E859:/
!     let caught_859 = 1
!   endtry
!   call assert_equal(1, caught_859)
! 
!   let caught_nameerr = 0
!   try
!     let v = py3eval("undefined_name")
!   catch /NameError: name 'undefined_name'/
!     let caught_nameerr = 1
!   endtry
!   call assert_equal(1, caught_nameerr)
! 
!   let caught_859 = 0
!   try
!     let v = py3eval("vim")
!   catch /E859:/
!     let caught_859 = 1
!   endtry
!   call assert_equal(1, caught_859)
  endfunc
  
  " threading
  " Running py3do command (Test_pydo) before this test, stops the python thread
  " from running. So this test should be run before the pydo test
! func Test_aaa_python_threading()
    let l = [0]
    py3 l = vim.bindeval('l')
    py3 << trim EOF
--- 955,998 ----
    call assert_equal(v:none, py3eval('None'))
    call assert_equal('', v:errmsg)
  
+   py3 v = vim.eval('test_null_function()')
+   call assert_equal(v:none, py3eval('v'))
+ 
    if has('float')
      call assert_equal(0.0, py3eval('0.0'))
    endif
  
!   " Evaluate an invalid values
!   call AssertException(['let v = py3eval(''"\0"'')'], 'E859:')
!   call AssertException(['let v = py3eval(''{"\0" : 1}'')'], 'E859:')
!   call AssertException(['let v = py3eval("undefined_name")'],
!         \ "Vim(let):NameError: name 'undefined_name' is not defined")
!   call AssertException(['let v = py3eval("vim")'], 'E859:')
! endfunc
! 
! " Test for vim.bindeval()
! func Test_python3_vim_bindeval()
!   " Float
!   let f = 3.14
!   py3 f = vim.bindeval('f')
!   call assert_equal(3.14, py3eval('f'))
! 
!   " Blob
!   let b = 0z12
!   py3 b = vim.bindeval('b')
!   call assert_equal("\x12", py3eval('b'))
! 
!   " Bool
!   call assert_equal(1, py3eval("vim.bindeval('v:true')"))
!   call assert_equal(0, py3eval("vim.bindeval('v:false')"))
!   call assert_equal(v:none, py3eval("vim.bindeval('v:null')"))
!   call assert_equal(v:none, py3eval("vim.bindeval('v:none')"))
  endfunc
  
  " threading
  " Running py3do command (Test_pydo) before this test, stops the python thread
  " from running. So this test should be run before the pydo test
! func Test_aaa_python3_threading()
    let l = [0]
    py3 l = vim.bindeval('l')
    py3 << trim EOF
***************
*** 932,938 ****
--- 1084,1107 ----
    call assert_equal([0, 2, 4], py3eval('l'))
    py3 l = ll[4:2:1]
    call assert_equal([], py3eval('l'))
+ 
+   " Error case: Use an invalid index
+   call AssertException(['py3 ll[-10] = 5'], 'Vim(py3):vim.error: internal 
error:')
+ 
+   " Use a step value of 0
+   call AssertException(['py3 ll[0:3:0] = [1, 2, 3]'],
+         \ 'Vim(py3):ValueError: slice step cannot be zero')
+ 
+   " Error case: Invalid slice type
+   call AssertException(["py3 x = ll['abc']"],
+         \ "Vim(py3):TypeError: index must be int or slice, not str")
    py3 del l
+ 
+   " Error case: List with a null list item
+   let l = [test_null_list()]
+   py3 ll = vim.bindeval('l')
+   call AssertException(["py3 x = ll[:]"],
+         \ "Vim(py3):SystemError: error return without exception set")
  endfunc
  
  " Vars
***************
*** 1369,1374 ****
--- 1538,1561 ----
  
    call assert_equal(expected, g:res)
    unlet g:res
+ 
+   call assert_equal(0, py3eval("'' in vim.options"))
+ 
+   " use an empty key to index vim.options
+   call AssertException(["let v = py3eval(\"vim.options['']\")"],
+         \ 'Vim(let):ValueError: empty keys are not allowed')
+   call AssertException(["py3 vim.current.window.options[''] = 0"],
+         \ 'Vim(py3):ValueError: empty keys are not allowed')
+   call AssertException(["py3 vim.current.window.options[{}] = 0"],
+         \ 'Vim(py3):TypeError: expected bytes() or str() instance, but got 
dict')
+ 
+   " set one of the number options to a very large number
+   let cmd = ["py3 vim.options['previewheight'] = 9999999999999999"]
+   call AssertException(cmd, "Vim(py3):OverflowError:")
+ 
+   " unset a global-local string option
+   call AssertException(["py3 del vim.options['errorformat']"],
+         \ 'Vim(py3):ValueError: unable to unset global option errorformat')
  endfunc
  
  " Test for vim.buffer object
***************
*** 1387,1397 ****
--- 1574,1599 ----
    py3 b = vim.current.buffer
    wincmd w
  
+   " Test for getting lines from the buffer using a slice
+   call assert_equal(['First line'], py3eval('b[-10:1]'))
+   call assert_equal(['Third line'], py3eval('b[2:10]'))
+   call assert_equal([], py3eval('b[2:0]'))
+   call assert_equal([], py3eval('b[10:12]'))
+   call assert_equal([], py3eval('b[-10:-8]'))
+ 
    " Tests BufferAppend and BufferItem
    py3 cb.append(b[0])
    call assert_equal(['First line'], getbufline(bnr1, 2))
    %d
  
+   " Try to append using out-of-range line number
+   call AssertException(["py3 b.append('abc', 10)"],
+         \ 'Vim(py3):IndexError: line number out of range')
+ 
+   " Append a non-string item
+   call AssertException(["py3 b.append([22])"],
+         \ 'Vim(py3):TypeError: expected bytes() or str() instance, but got 
int')
+ 
    " Tests BufferSlice and BufferAssSlice
    py3 cb.append('abc5') # Will be overwritten
    py3 cb[-1:] = b[:-2]
***************
*** 1483,1493 ****
--- 1685,1746 ----
    EOF
    call assert_equal([''], getline(1, '$'))
  
+   " Delete all the lines in a buffer
+   call setline(1, ['a', 'b', 'c'])
+   py3 vim.current.buffer[:] = []
+   call assert_equal([''], getline(1, '$'))
+ 
+   " Test for modifying a 'nomodifiable' buffer
+   setlocal nomodifiable
+   call AssertException(["py3 vim.current.buffer[0] = 'abc'"],
+         \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is 
off")
+   call AssertException(["py3 vim.current.buffer[0] = None"],
+         \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is 
off")
+   call AssertException(["py3 vim.current.buffer[:] = None"],
+         \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is 
off")
+   call AssertException(["py3 vim.current.buffer[:] = []"],
+         \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is 
off")
+   call AssertException(["py3 vim.current.buffer.append('abc')"],
+         \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is 
off")
+   call AssertException(["py3 vim.current.buffer.append([])"],
+         \ "Vim(py3):vim.error: Vim:E21: Cannot make changes, 'modifiable' is 
off")
+   setlocal modifiable
+ 
    augroup BUFS
      autocmd!
    augroup END
    augroup! BUFS
    %bw!
+ 
+   " Range object for a deleted buffer
+   new Xfile
+   call setline(1, ['one', 'two', 'three'])
+   py3 b = vim.current.buffer
+   py3 r = vim.current.buffer.range(0, 2)
+   call assert_equal('<range Xfile (0:2)>', py3eval('repr(r)'))
+   %bw!
+   call AssertException(['py3 r[:] = []'],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
+   call assert_match('<buffer object (deleted)', py3eval('repr(b)'))
+   call assert_match('<range object (for deleted buffer)', py3eval('repr(r)'))
+   call AssertException(["let n = py3eval('len(r)')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py3 r.append('abc')"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
+ 
+   " object for a deleted buffer
+   call AssertException(["py3 b[0] = 'one'"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py3 b.append('one')"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["let n = py3eval('len(b)')"],
+         \ 'Vim(let):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py3 pos = b.mark('a')"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py3 vim.current.buffer = b"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
+   call AssertException(["py3 rn = b.range(0, 2)"],
+         \ 'Vim(py3):vim.error: attempt to refer to deleted buffer')
  endfunc
  
  " Test vim.buffers object
***************
*** 1731,1736 ****
--- 1984,1991 ----
      Current line: 'python interface'
    END
    call assert_equal(expected, getbufline(bufnr('Xfile'), 2, '$'))
+   py3 vim.current.line = 'one line'
+   call assert_equal('one line', getline('.'))
    call deletebufline(bufnr('Xfile'), 1, '$')
  
    py3 << trim EOF
***************
*** 1853,1863 ****
  
  " Test vim.Function
  func Test_python3_vim_func()
!   function Args(...)
      return a:000
    endfunc
  
!   function SelfArgs(...) dict
      return [a:000, self]
    endfunc
  
--- 2108,2118 ----
  
  " Test vim.Function
  func Test_python3_vim_func()
!   func Args(...)
      return a:000
    endfunc
  
!   func SelfArgs(...) dict
      return [a:000, self]
    endfunc
  
***************
*** 1867,1872 ****
--- 2122,2129 ----
    unlet Pt
    py3 del Pt
  
+   call assert_equal(3, py3eval('vim.strwidth("a\tb")'))
+ 
    %bw!
    py3 cb = vim.current.buffer
    py3 << trim EOF
***************
*** 2309,2315 ****
        cb.append(str(fnamemodify('.', ':p:h:t')))
        cb.append(vim.eval('@%')[len(path)+1:].replace(os.path.sep, '/'))
        os.chdir(path)
-       del path
      else:
        cb.append(str(fnamemodify('.', ':p:h:t')))
        cb.append(vim.eval('@%').replace(os.path.sep, '/'))
--- 2566,2571 ----
***************
*** 3579,3584 ****
--- 3835,3843 ----
    END
    call assert_equal(expected, getline(2, '$'))
    close!
+ 
+   " Try to import a non-existing moudle with a dot (.)
+   call AssertException(['py3 import a.b.c'], "No module named 'a'")
  endfunc
  
  " Test exceptions
*** ../vim-8.2.1145/src/version.c       2020-07-05 21:38:07.857431843 +0200
--- src/version.c       2020-07-06 21:01:59.881502366 +0200
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     1146,
  /**/

-- 
An easy way to determine if you have enough teamwork to be doomed is simply to
measure how long it takes from the time you decide to go to lunch together
until the time you actually eat.
                                (Scott Adams - The Dilbert principle)

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202007061903.066J3hdL892395%40masaka.moolenaar.net.

Raspunde prin e-mail lui