23

I'm struggling with Python in vim.
I still haven't found out how I can import a value from a python script (in a vim function) back to vim p.e.

function! myvimscript()

  python << endpython
    import vim, random, sys
    s = vim.eval("mylist")
    # do operation with variable "s" in python
  endpython

  " import variable "s" from above 
  " do operation with "s" in vimscript
endfunction

1) How can I use "s" again in vim (how can I import "s" from the python code back to vim)?

I can't find out as well how to use vim.current.buffer with a selection.

function! myvimscript()
  let startline = line("'<")
  let endline = line("'>")

  python << endpython
    start = vim.eval("startline")
    end = vim.eval("endline")
    cb = vim.current.buffer 
    l = cb[start:end]
  endpython
endfunction

2) How can I assign the dynamic value "start" and "end" to "l"

4
  • is this a python script running from a bash script? Commented Jul 15, 2013 at 14:26
  • @TallPaul, Un pythonscript running in a vimscript (and invoked with a map in vimrc) Commented Jul 15, 2013 at 14:46
  • Related: How do I get the value returned from a function in Python & Vimscript?. Commented Jul 15, 2013 at 15:32
  • Note: this basic approach of printf-style string interpolation vim.command("let sInVim = '%s'"% s) is a source of potential bugs. Specifically, this approach will not work as expected if the interpolated value causes delimiter collision on the single-quote character Commented Nov 22, 2022 at 19:24

3 Answers 3

28

First of all, please define your function name starting with uppercase.

Here is an example for your two questions. I hope it helps:

function! TestPy() range

    let startline = line("'<")
    let endline = line("'>")
    echo "vim-start:".startline . " vim-endline:".endline
python << EOF
import vim
s = "I was set in python"
vim.command("let sInVim = '%s'"% s)
start = vim.eval("startline")
end = vim.eval("endline")
print "start, end in python:%s,%s"% (start, end)
EOF
    echo sInVim
endfunction

first I paste the output of a small test: I visual selected 3,4,5, three lines, and :call TestPy()

The output I had:

vim-start:3 vim-endline:5
start, end in python:3,5
I was set in python

So I explain the output a bit, you may need to read the example function codes a little for understanding the comment below.

vim-start:3 vim-endline:5   #this line was printed in vim,  by vim's echo.
start, end in python:3,5    # this line was prrinted in py, using the vim var startline and endline. this answered your question two.
I was set in python         # this line was printed in vim, the variable value was set in python. it answered your question one.

I added a range for your function. because, if you don't have it, for each visual-selected line, vim will call your function once. in my example, the function will be executed 3 times (3,4,5). with range, it will handle visualselection as a range. It is sufficient for this example. If your real function will do something else, you could remove the range.

With range, better with a:firstline and a:lastline. I used the line("'<") just for keep it same as your codes.

EDIT with list variable:

check this function:

function! TestPy2()
python << EOF
import vim
s = range(10)
vim.command("let sInVim = %s"% s)
EOF
    echo type(sInVim)
    echo sInVim
endfunction

if you call it, the output is:

3
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

the "3" means type list (check type() function). and one line below is the string representation of list.

Sign up to request clarification or add additional context in comments.

5 Comments

great explication Kent! But what if s is not a string but a list. It gives errors with vim.command("let sInVim = '%s'"% s)
Thank you very much. I will study your answer a bit more. You gave me so much information. But now everything works fine!
Now with you info I'm trying to test a random function using python. :py import vim, random as r; cb = vim.current.buffer ; start = vim.eval('line("'<")'); end = vim.eval('line("'>")'); l = cb[start:end] ; r.shuffle(l) ; cb[start:end] = l Something still doesn't work. error: must be string not bool.
Found the solution :) I had to declare the variables 'start' and 'end' as integers :)
For global variables there is another option for vim.command("let sInVim = '%s'"% s): vim.vars["sInVim"] = s
5

As of v7.3.569, vim built-in functions pyeval() and py3eval() allow you to evaluate a python expression, and return its result as a vimscript value. It can handle simple scalar values, but also lists and dictionaries - see :help pyeval()

On 1): For using a python-defined variable in vim:

python << endPython
py_var = 8
endPython

let vim_var = pyeval('py_var')

On 2): I assume you want the buffer lines last highlighted in visual mode, as a list of strings in vimscript:

python << endPython
import vim
cb = vim.current.buffer
start = int(vim.eval("""line("'<")"""))
end = int(vim.eval("""line("'>")"""))
lines = cb[(start - 1) : end]
endPython
let lines = pyeval('lines')

Note: This is a somewhat contrived example, as you could get the same directly in vimscript:

let lines = getline("'<", "'>")

6 Comments

I use this one: python << endPython | my_val = ... | vim.command("let my_val = '%s'" % my_val) | endpython || let vim_val = my_val What is the difference between both?
@Reman: pyeval might facilitate using existing python libs, which are usually not aware of vim context. Also, with vim.command, lists and dictionaries might be slightly more brittle, due to potential quoting issues.
rippleslash, does it also work with Python 3.7? I do receive errors after having changed a few vim.command in ...pyeval(): "cannot find python library 2.7" - I don't use python 2.7
rippleslash, what to do if a list or dictionary has only integers? py3eval() can't convert the object: E859: Failed to convert returned python object to vim value
@Reman, py3eval("[1, 2, 3]") and py3eval("{ 'foo': 1 }") seem to work, but py3eval("{ 1: 'foo' }") doesn't (tried in vim 8.1.1137). According to :help Dictionary, vim always uses strings as dict keys, maybe that's why it fails in this case. Best I can think of is to stringify your dict keys - i.e. def convert(d): return { str(k): v for k, v in d.items() } in python and py3eval("convert({ 1: 'foo' })") in vim.
|
1

In short:

:py3 vim.command("let vimvar = '%s'" % 'pyvalue')

or

:let vimvar = py3eval("'pyvalue'")

Where vimvar is a vim variable and can be b: w: t: g: l: s: a: v: and 'pyvalue' is a python variable.

1 Comment

Note: this basic approach of printf-style string interpolation vim.command("let sInVim = '%s'"% s) is a source of potential bugs. Specifically, this approach will not work as expected if the interpolated value causes delimiter collision on the single-quote character

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.