feat(provider)!: remove support for python2 and python3.[3-5]

These versions of python has reached End-of-life. getting rid
of python2 support removes a lot of logic to support two
incompatible python versions in the same version.
This commit is contained in:
Björn Linse 2022-01-28 15:42:19 +01:00
parent b2f77c354a
commit baec0d3152
20 changed files with 110 additions and 450 deletions

View File

@ -104,8 +104,8 @@ function! s:check_rplugin_manifest() abort
if !has_key(existing_rplugins, script) if !has_key(existing_rplugins, script)
let msg = printf('"%s" is not registered.', fnamemodify(path, ':t')) let msg = printf('"%s" is not registered.', fnamemodify(path, ':t'))
if python_version ==# 'pythonx' if python_version ==# 'pythonx'
if !has('python2') && !has('python3') if !has('python3')
let msg .= ' (python2 and python3 not available)' let msg .= ' (python3 not available)'
endif endif
elseif !has(python_version) elseif !has(python_version)
let msg .= printf(' (%s not available)', python_version) let msg .= printf(' (%s not available)', python_version)

View File

@ -282,10 +282,10 @@ function! s:disabled_via_loaded_var(provider) abort
return 0 return 0
endfunction endfunction
function! s:check_python(version) abort function! s:check_python() abort
call health#report_start('Python ' . a:version . ' provider (optional)') call health#report_start('Python 3 provider (optional)')
let pyname = 'python'.(a:version == 2 ? '' : '3') let pyname = 'python3'
let python_exe = '' let python_exe = ''
let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
let host_prog_var = pyname.'_host_prog' let host_prog_var = pyname.'_host_prog'
@ -301,7 +301,7 @@ function! s:check_python(version) abort
call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var)))
endif endif
let [pyname, pythonx_errors] = provider#pythonx#Detect(a:version) let [pyname, pythonx_errors] = provider#pythonx#Detect(3)
if empty(pyname) if empty(pyname)
call health#report_warn('No Python executable found that can `import neovim`. ' call health#report_warn('No Python executable found that can `import neovim`. '
@ -405,7 +405,7 @@ function! s:check_python(version) abort
" can import 'pynvim'. If so, that Python failed to import 'neovim' as " can import 'pynvim'. If so, that Python failed to import 'neovim' as
" well, which is most probably due to a failed pip upgrade: " well, which is most probably due to a failed pip upgrade:
" https://github.com/neovim/neovim/wiki/Following-HEAD#20181118 " https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', a:version) let [pynvim_exe, errors] = provider#pythonx#DetectByModule('pynvim', 3)
if !empty(pynvim_exe) if !empty(pynvim_exe)
call health#report_error( call health#report_error(
\ 'Detected pip upgrade failure: Python executable can import "pynvim" but ' \ 'Detected pip upgrade failure: Python executable can import "pynvim" but '
@ -416,14 +416,14 @@ function! s:check_python(version) abort
\ . pynvim_exe ." -m pip install neovim # only if needed by third-party software") \ . pynvim_exe ." -m pip install neovim # only if needed by third-party software")
endif endif
else else
let [pyversion, current, latest, status] = s:version_info(python_exe) let [majorpyversion, current, latest, status] = s:version_info(python_exe)
if a:version != str2nr(pyversion) if 3 != str2nr(majorpyversion)
call health#report_warn('Unexpected Python version.' . call health#report_warn('Unexpected Python version.' .
\ ' This could lead to confusing error messages.') \ ' This could lead to confusing error messages.')
endif endif
call health#report_info('Python version: ' . pyversion) call health#report_info('Python version: ' . majorpyversion)
if s:is_bad_response(status) if s:is_bad_response(status)
call health#report_info(printf('pynvim version: %s (%s)', current, status)) call health#report_info(printf('pynvim version: %s (%s)', current, status))
@ -751,8 +751,7 @@ endfunction
function! health#provider#check() abort function! health#provider#check() abort
call s:check_clipboard() call s:check_clipboard()
call s:check_python(2) call s:check_python()
call s:check_python(3)
call s:check_virtualenv() call s:check_virtualenv()
call s:check_ruby() call s:check_ruby()
call s:check_node() call s:check_node()

View File

@ -1,45 +0,0 @@
" The Python provider uses a Python host to emulate an environment for running
" python-vim plugins. :help provider
"
" Associating the plugin with the Python host is the first step because plugins
" will be passed as command-line arguments
if exists('g:loaded_python_provider')
finish
endif
let [s:prog, s:err] = provider#pythonx#Detect(2)
let g:loaded_python_provider = empty(s:prog) ? 1 : 2
function! provider#python#Prog() abort
return s:prog
endfunction
function! provider#python#Error() abort
return s:err
endfunction
" The Python provider plugin will run in a separate instance of the Python
" host.
call remote#host#RegisterClone('legacy-python-provider', 'python')
call remote#host#RegisterPlugin('legacy-python-provider', 'script_host.py', [])
function! provider#python#Call(method, args) abort
if s:err != ''
return
endif
if !exists('s:host')
let s:rpcrequest = function('rpcrequest')
" Ensure that we can load the Python host before bootstrapping
try
let s:host = remote#host#Require('legacy-python-provider')
catch
let s:err = v:exception
echohl WarningMsg
echomsg v:exception
echohl None
return
endtry
endif
return call(s:rpcrequest, insert(insert(a:args, 'python_'.a:method), s:host))
endfunction

View File

@ -6,10 +6,8 @@ endif
let s:loaded_pythonx_provider = 1 let s:loaded_pythonx_provider = 1
function! provider#pythonx#Require(host) abort function! provider#pythonx#Require(host) abort
let ver = (a:host.orig_name ==# 'python') ? 2 : 3
" Python host arguments " Python host arguments
let prog = (ver == '2' ? provider#python#Prog() : provider#python3#Prog()) let prog = provider#python3#Prog()
let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()'] let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()']
@ -23,14 +21,12 @@ function! provider#pythonx#Require(host) abort
endfunction endfunction
function! s:get_python_executable_from_host_var(major_version) abort function! s:get_python_executable_from_host_var(major_version) abort
return expand(get(g:, 'python'.(a:major_version == 3 ? '3' : '').'_host_prog', ''), v:true) return expand(get(g:, 'python'.(a:major_version == 3 ? '3' : execute("throw 'unsupported'")).'_host_prog', ''), v:true)
endfunction endfunction
function! s:get_python_candidates(major_version) abort function! s:get_python_candidates(major_version) abort
return { return {
\ 2: ['python2', 'python2.7', 'python2.6', 'python'], \ 3: ['python3', 'python3.10', 'python3.9', 'python3.8', 'python3.7', 'python']
\ 3: ['python3', 'python3.10', 'python3.9', 'python3.8', 'python3.7',
\ 'python3.6', 'python']
\ }[a:major_version] \ }[a:major_version]
endfunction endfunction
@ -82,7 +78,7 @@ function! provider#pythonx#CheckForModule(prog, module, major_version) abort
return [0, a:prog . ' not found in search path or not executable.'] return [0, a:prog . ' not found in search path or not executable.']
endif endif
let min_version = (a:major_version == 2) ? '2.6' : '3.3' let min_version = '3.7'
" Try to load module, and output Python version. " Try to load module, and output Python version.
" Exit codes: " Exit codes:

View File

@ -5806,7 +5806,6 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The
this is not present). this is not present).
mac MacOS system. mac MacOS system.
nvim This is Nvim. nvim This is Nvim.
python2 Legacy Vim |python2| interface. |has-python|
python3 Legacy Vim |python3| interface. |has-python| python3 Legacy Vim |python3| interface. |has-python|
pythonx Legacy Vim |python_x| interface. |has-pythonx| pythonx Legacy Vim |python_x| interface. |has-pythonx|
ttyin input is a terminal (tty) ttyin input is a terminal (tty)

View File

@ -1,10 +1,10 @@
*if_pyth.txt* Nvim *if_pyth.txt* Nvim
VIM REFERENCE MANUAL by Paul Moore NVIM REFERENCE MANUAL
The Python Interface to Vim *if_pyth* *python* *Python* The Python Interface to NVim *if_pyth* *python* *Python*
See |provider-python| for more information. See |provider-python| for more information.
@ -134,7 +134,7 @@ Instead, put the Python command in a function and call that function:
Note that "EOF" must be at the start of the line. Note that "EOF" must be at the start of the line.
============================================================================== ==============================================================================
The vim module *python-vim* *python2* The vim module *python-vim*
Python code gets all of its access to vim (with one exception - see Python code gets all of its access to vim (with one exception - see
|python-output| below) via the "vim" module. The vim module implements two |python-output| below) via the "vim" module. The vim module implements two
@ -322,14 +322,13 @@ Output from Python *python-output*
supported, and may cause the program to crash. This should probably be supported, and may cause the program to crash. This should probably be
fixed. fixed.
*python2-directory* *python3-directory* *pythonx-directory* *python3-directory* *pythonx-directory*
Python 'runtimepath' handling *python-special-path* Python 'runtimepath' handling *python-special-path*
In python vim.VIM_SPECIAL_PATH special directory is used as a replacement for In python vim.VIM_SPECIAL_PATH special directory is used as a replacement for
the list of paths found in 'runtimepath': with this directory in sys.path and the list of paths found in 'runtimepath': with this directory in sys.path and
vim.path_hooks in sys.path_hooks python will try to load module from vim.path_hooks in sys.path_hooks python will try to load module from
{rtp}/python2 (or python3) and {rtp}/pythonx (for both python versions) for {rtp}/python3 and {rtp}/pythonx for each {rtp} found in 'runtimepath'.
each {rtp} found in 'runtimepath'.
Implementation is similar to the following, but written in C: > Implementation is similar to the following, but written in C: >
@ -401,8 +400,8 @@ vim._get_paths *python-_get_paths*
hook. You should not rely on this method being present in future hook. You should not rely on this method being present in future
versions, but can use it for debugging. versions, but can use it for debugging.
It returns a list of {rtp}/python2 (or {rtp}/python3) and It returns a list of {rtp}/python3 and {rtp}/pythonx
{rtp}/pythonx directories for each {rtp} in 'runtimepath'. directories for each {rtp} in 'runtimepath'.
============================================================================== ==============================================================================
Buffer objects *python-buffer* Buffer objects *python-buffer*
@ -590,6 +589,11 @@ functions to evaluate Python expressions and pass their values to Vim script.
============================================================================== ==============================================================================
Python 3 *python3* Python 3 *python3*
As Python 3 is the only supported version in Nvim, "python" is synonymous
with "python3" in the current version. However, code that aims to support older
versions of Neovim, as well as Vim, should prefer to use "python3"
variants explicitly if Python 3 is required.
*:py3* *:python3* *:py3* *:python3*
:[range]py3 {stmt} :[range]py3 {stmt}
:[range]py3 << [endmarker] :[range]py3 << [endmarker]
@ -619,31 +623,26 @@ Raising SystemExit exception in python isn't endorsed way to quit vim, use: >
:py vim.command("qall!") :py vim.command("qall!")
< <
*has-python* *has-python*
You can test what Python version is available with: > You can test if Python is available with: >
if has('python') if has('pythonx')
echo 'there is Python 2.x' echo 'there is Python'
endif endif
if has('python3') if has('python3')
echo 'there is Python 3.x' echo 'there is Python 3.x'
endif endif
Python 2 is no longer supported. Thus `has('python')` always returns
zero for backwards compatibility reasons.
============================================================================== ==============================================================================
Python X *python_x* *pythonx* Python X *python_x* *pythonx*
Because most python code can be written so that it works with Python 2.6+ and The "pythonx" and "pyx" prefixes were introduced for python code which
Python 3, the pyx* functions and commands have been written. They work the works with Python 2.6+ and Python 3. As Nvim only supports Python 3,
same as the Python 2 and 3 variants, but select the Python version using the all these commands are now synonymous to their "python3" equivalents.
'pyxversion' setting.
Set 'pyxversion' in your |vimrc| to prefer Python 2 or Python 3 for Python
commands. Changing this setting at runtime risks losing the state of plugins
(such as initialization).
If you want to use a module, you can put it in the {rtp}/pythonx directory.
See |pythonx-directory|.
*:pyx* *:pythonx* *:pyx* *:pythonx*
`:pyx` and `:pythonx` work similar to `:python`. To check if `:pyx` works: > `:pyx` and `:pythonx` work the same as `:python3`. To check if `:pyx` works: >
:pyx print("Hello") :pyx print("Hello")
To see what version of Python is being used: > To see what version of Python is being used: >
@ -651,34 +650,16 @@ To see what version of Python is being used: >
:pyx print(sys.version) :pyx print(sys.version)
< <
*:pyxfile* *python_x-special-comments* *:pyxfile* *python_x-special-comments*
`:pyxfile` works similar to `:pyfile`. But you can add a "shebang" comment to `:pyxfile` works the same as `:py3file`.
force Vim to use `:pyfile` or `:py3file`: >
#!/any string/python2 " Shebang. Must be the first line of the file.
#!/any string/python3 " Shebang. Must be the first line of the file.
# requires python 2.x " Maximum lines depend on 'modelines'.
# requires python 3.x " Maximum lines depend on 'modelines'.
Unlike normal modelines, the bottom of the file is not checked.
If none of them are found, the 'pyxversion' option is used.
*W20* *W21*
If Vim does not support the selected Python version a silent message will be
printed. Use `:messages` to read them.
*:pyxdo* *:pyxdo*
`:pyxdo` works similar to `:pydo`. `:pyxdo` works the same as `:py3do`.
*has-pythonx* *has-pythonx*
To check if pyx* functions and commands are available: > To check if `pyx*` functions and commands are available: >
if has('pythonx') if has('pythonx')
echo 'pyx* commands are available. (Python ' . &pyx . ')' echo 'pyx* commands are available. (Python ' . &pyx . ')'
endif endif
If you prefer Python 2 and want to fallback to Python 3, set 'pyxversion'
explicitly in your |.vimrc|. Example: >
if has('python')
set pyx=2
elseif has('python3')
set pyx=3
endif
============================================================================== ==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl: vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -4637,26 +4637,11 @@ A jump table for the options with a short description can be found at |Q_op|.
nudged to fit on the screen. nudged to fit on the screen.
*'pyxversion'* *'pyx'* *'pyxversion'* *'pyx'*
'pyxversion' 'pyx' number (default depends on the build) 'pyxversion' 'pyx' number (default 3)
global global
Specifies the python version used for pyx* functions and commands Specifies the python version used for pyx* functions and commands
|python_x|. The default value is as follows: |python_x|. As only Python 3 is supported, this always has the value
`3`. Setting any other value is an error.
|provider| installed Default ~
|+python| and |+python3| 0
only |+python| 2
only |+python3| 3
Available values are 0, 2 and 3.
If 'pyxversion' is 0, it is set to 2 or 3 after the first execution of
any python2/3 commands or functions. E.g. `:py` sets to 2, and `:py3`
sets to 3. `:pyx` sets it to 3 if Python 3 is available, otherwise sets
to 2 if Python 2 is available.
See also: |has-pythonx|
If only |+python| or |+python3| are available,
'pyxversion' has no effect. The pyx* functions and commands are
always the same as the installed version.
This option cannot be set from a |modeline| or in the |sandbox|, for This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons. security reasons.

View File

@ -20,11 +20,12 @@ Run the |:checkhealth| command, and review the sections below.
============================================================================== ==============================================================================
Python integration *provider-python* Python integration *provider-python*
Nvim supports Python |remote-plugin|s and the Vim legacy |python2| and Nvim supports Python |remote-plugin|s and the Vim legacy |python3| and
|python3| interfaces (which are implemented as remote-plugins). |pythonx| interfaces (which are implemented as remote-plugins).
Note: Only the Vim 7.3 legacy interface is supported, not later features such Note: Only the Vim 7.3 legacy interface is supported, not later features such
as |python-bindeval| (Vim 7.4); use the Nvim API instead. as |python-bindeval| (Vim 7.4); use the Nvim API instead. Python 2 is not
supported.
PYTHON QUICKSTART ~ PYTHON QUICKSTART ~
@ -38,11 +39,6 @@ For Python 3 plugins:
2. Install the module (try "python" if "python3" is missing): > 2. Install the module (try "python" if "python3" is missing): >
python3 -m pip install --user --upgrade pynvim python3 -m pip install --user --upgrade pynvim
For Python 2 plugins:
1. Make sure Python 2.7 is available in your $PATH.
2. Install the module (try "python" if "python2" is missing): >
python2 -m pip install --user --upgrade pynvim
The pip `--upgrade` flag ensures that you get the latest version even if The pip `--upgrade` flag ensures that you get the latest version even if
a previous version was already installed. a previous version was already installed.
@ -56,21 +52,11 @@ If you run into problems, uninstall _both_ then install "pynvim" again: >
PYTHON PROVIDER CONFIGURATION ~ PYTHON PROVIDER CONFIGURATION ~
*g:python_host_prog*
Command to start Python 2 (executable, not directory). Setting this makes
startup faster. Useful for working with virtualenvs. Must be set before any
check for has("python2"). >
let g:python_host_prog = '/path/to/python'
<
*g:python3_host_prog* *g:python3_host_prog*
Command to start Python 3 (executable, not directory). Setting this makes Command to start Python 3 (executable, not directory). Setting this makes
startup faster. Useful for working with virtualenvs. Must be set before any startup faster. Useful for working with virtualenvs. Must be set before any
check for has("python3"). > check for has("python3"). >
let g:python3_host_prog = '/path/to/python3' let g:python3_host_prog = '/path/to/python3'
<
*g:loaded_python_provider*
To disable Python 2 support: >
let g:loaded_python_provider = 0
< <
*g:loaded_python3_provider* *g:loaded_python3_provider*
To disable Python 3 support: > To disable Python 3 support: >
@ -81,8 +67,8 @@ PYTHON VIRTUALENVS ~
*python-virtualenv* *python-virtualenv*
If you plan to use per-project virtualenvs often, you should assign one If you plan to use per-project virtualenvs often, you should assign one
virtualenv for Neovim and hard-code the interpreter path via virtualenv for Neovim and hard-code the interpreter path via
|g:python3_host_prog| (or |g:python_host_prog|) so that the "pynvim" package |g:python3_host_prog| so that the "pynvim" package is not required
is not required for each virtualenv. for each virtualenv.
Example using pyenv: > Example using pyenv: >
pyenv install 3.4.4 pyenv install 3.4.4

View File

@ -1446,7 +1446,7 @@ error message (line numbers are not part of the actual output):
4 Traceback (most recent call last): 4 Traceback (most recent call last):
5 File "unittests/dbfacadeTest.py", line 89, in testFoo 5 File "unittests/dbfacadeTest.py", line 89, in testFoo
6 self.assertEquals(34, dtid) 6 self.assertEquals(34, dtid)
7 File "/usr/lib/python2.2/unittest.py", line 286, in 7 File "/usr/lib/python3.8/unittest.py", line 286, in
8 failUnlessEqual 8 failUnlessEqual
9 raise self.failureException, \ 9 raise self.failureException, \
10 AssertionError: 34 != 33 10 AssertionError: 34 != 33

View File

@ -270,8 +270,8 @@ return {
pum_getpos={}, pum_getpos={},
pumvisible={}, pumvisible={},
py3eval={args=1, base=1}, py3eval={args=1, base=1},
pyeval={args=1, base=1}, pyeval={args=1, base=1, func="f_py3eval"},
pyxeval={args=1, base=1}, pyxeval={args=1, base=1, func="f_py3eval"},
perleval={args=1, base=1}, perleval={args=1, base=1},
range={args={1, 3}, base=1}, range={args={1, 3}, base=1},
readdir={args={1, 2}, base=1}, readdir={args={1, 2}, base=1},

View File

@ -6982,36 +6982,13 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
} }
/* /// "py3eval()" and "pyxeval()" functions (always python3)
* "pyeval()" function
*/
static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
script_host_eval("python", argvars, rettv);
}
/*
* "py3eval()" function
*/
static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
script_host_eval("python3", argvars, rettv); script_host_eval("python3", argvars, rettv);
} }
// "pyxeval()" function
static void f_pyxeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
init_pyxversion();
if (p_pyx == 2) {
f_pyeval(argvars, rettv, NULL);
} else {
f_py3eval(argvars, rettv, NULL);
}
}
///
/// "perleval()" function /// "perleval()" function
///
static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
script_host_eval("perl", argvars, rettv); script_host_eval("perl", argvars, rettv);

View File

@ -2097,19 +2097,19 @@ module.cmds = {
command='python', command='python',
flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_python', func='ex_python3',
}, },
{ {
command='pydo', command='pydo',
flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_pydo', func='ex_pydo3',
}, },
{ {
command='pyfile', command='pyfile',
flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_pyfile', func='ex_py3file',
}, },
{ {
command='py3', command='py3',
@ -2139,25 +2139,25 @@ module.cmds = {
command='pyx', command='pyx',
flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_pyx', func='ex_python3',
}, },
{ {
command='pyxdo', command='pyxdo',
flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_pyxdo', func='ex_pydo3',
}, },
{ {
command='pythonx', command='pythonx',
flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_pyx', func='ex_python3',
}, },
{ {
command='pyxfile', command='pyxfile',
flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
addr_type='ADDR_LINES', addr_type='ADDR_LINES',
func='ex_pyxfile', func='ex_py3file',
}, },
{ {
command='quit', command='quit',

View File

@ -135,21 +135,6 @@ void ex_profile(exarg_T *eap)
} }
} }
void ex_python(exarg_T *eap)
{
script_host_execute("python", eap);
}
void ex_pyfile(exarg_T *eap)
{
script_host_execute_file("python", eap);
}
void ex_pydo(exarg_T *eap)
{
script_host_do_range("python", eap);
}
void ex_ruby(exarg_T *eap) void ex_ruby(exarg_T *eap)
{ {
script_host_execute("ruby", eap); script_host_execute("ruby", eap);
@ -1660,126 +1645,6 @@ void ex_options(exarg_T *eap)
cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
} }
// Detect Python 3 or 2, and initialize 'pyxversion'.
void init_pyxversion(void)
{
if (p_pyx == 0) {
if (eval_has_provider("python3")) {
p_pyx = 3;
} else if (eval_has_provider("python")) {
p_pyx = 2;
}
}
}
// Does a file contain one of the following strings at the beginning of any
// line?
// "#!(any string)python2" => returns 2
// "#!(any string)python3" => returns 3
// "# requires python 2.x" => returns 2
// "# requires python 3.x" => returns 3
// otherwise return 0.
static int requires_py_version(char_u *filename)
{
FILE *file;
int requires_py_version = 0;
int i, lines;
lines = (int)p_mls;
if (lines < 0) {
lines = 5;
}
file = os_fopen((char *)filename, "r");
if (file != NULL) {
for (i = 0; i < lines; i++) {
if (vim_fgets(IObuff, IOSIZE, file)) {
break;
}
if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') {
// Check shebang.
if (strstr((char *)IObuff + 2, "python2") != NULL) {
requires_py_version = 2;
break;
}
if (strstr((char *)IObuff + 2, "python3") != NULL) {
requires_py_version = 3;
break;
}
}
IObuff[21] = '\0';
if (STRCMP("# requires python 2.x", IObuff) == 0) {
requires_py_version = 2;
break;
}
if (STRCMP("# requires python 3.x", IObuff) == 0) {
requires_py_version = 3;
break;
}
}
fclose(file);
}
return requires_py_version;
}
// Source a python file using the requested python version.
static void source_pyx_file(exarg_T *eap, char_u *fname)
{
exarg_T ex;
long int v = requires_py_version(fname);
init_pyxversion();
if (v == 0) {
// user didn't choose a preference, 'pyx' is used
v = p_pyx;
}
// now source, if required python version is not supported show
// unobtrusive message.
if (eap == NULL) {
memset(&ex, 0, sizeof(ex));
} else {
ex = *eap;
}
ex.arg = fname;
ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
if (v == 2) {
ex_pyfile(&ex);
} else {
ex_py3file(&ex);
}
}
// ":pyxfile {fname}"
void ex_pyxfile(exarg_T *eap)
{
source_pyx_file(eap, eap->arg);
}
// ":pyx"
void ex_pyx(exarg_T *eap)
{
init_pyxversion();
if (p_pyx == 2) {
ex_python(eap);
} else {
ex_python3(eap);
}
}
// ":pyxdo"
void ex_pyxdo(exarg_T *eap)
{
init_pyxversion();
if (p_pyx == 2) {
ex_pydo(eap);
} else {
ex_pydo3(eap);
}
}
/// ":source [{fname}]" /// ":source [{fname}]"
void ex_source(exarg_T *eap) void ex_source(exarg_T *eap)
{ {

View File

@ -4349,6 +4349,12 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
} else if (value > 10000) { } else if (value > 10000) {
errmsg = e_invarg; errmsg = e_invarg;
} }
} else if (pp == &p_pyx) {
if (value == 0) {
value = 3;
} else if (value != 3) {
errmsg = e_invarg;
}
} else if (pp == &p_re) { } else if (pp == &p_re) {
if (value < 0 || value > 2) { if (value < 0 || value > 2) {
errmsg = e_invarg; errmsg = e_invarg;
@ -4523,10 +4529,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
if (pum_drawn()) { if (pum_drawn()) {
pum_redraw(); pum_redraw();
} }
} else if (pp == &p_pyx) {
if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) {
errmsg = e_invarg;
}
} else if (pp == &p_ul || pp == &curbuf->b_p_ul) { } else if (pp == &p_ul || pp == &curbuf->b_p_ul) {
// sync undo before 'undolevels' changes // sync undo before 'undolevels' changes
// use the old value, otherwise u_sync() may not work properly // use the old value, otherwise u_sync() may not work properly

View File

@ -1846,7 +1846,7 @@ return {
type='number', scope={'global'}, type='number', scope={'global'},
secure=true, secure=true,
varname='p_pyx', varname='p_pyx',
defaults={if_true=0} defaults={if_true=3}
}, },
{ {
full_name='quickfixtextfunc', abbreviation='qftf', full_name='quickfixtextfunc', abbreviation='qftf',

View File

@ -69,6 +69,7 @@ func Test_vim_function()
endfunc endfunc
func Test_skipped_python3_command_does_not_affect_pyxversion() func Test_skipped_python3_command_does_not_affect_pyxversion()
throw 'skipped: Nvim hardcodes pyxversion=3'
set pyxversion=0 set pyxversion=0
if 0 if 0
python3 import vim python3 import vim

View File

@ -1,9 +1,9 @@
" Test for pyx* commands and functions with Python 2. " Test for pyx* commands and functions with Python 2.
set pyx=2
if !has('python') if !has('python')
finish finish
endif endif
set pyx=2
let s:py2pattern = '^2\.[0-7]\.\d\+' let s:py2pattern = '^2\.[0-7]\.\d\+'
let s:py3pattern = '^3\.\d\+\.\d\+' let s:py3pattern = '^3\.\d\+\.\d\+'

View File

@ -62,10 +62,10 @@ describe('script_get-based command', function()
-- Provider-based scripts -- Provider-based scripts
test_garbage_exec('ruby', not missing_provider('ruby')) test_garbage_exec('ruby', not missing_provider('ruby'))
test_garbage_exec('python', not missing_provider('python'))
test_garbage_exec('python3', not missing_provider('python3')) test_garbage_exec('python3', not missing_provider('python3'))
-- Missing scripts -- Missing scripts
test_garbage_exec('python', false)
test_garbage_exec('tcl', false) test_garbage_exec('tcl', false)
test_garbage_exec('mzscheme', false) test_garbage_exec('mzscheme', false)
test_garbage_exec('perl', false) test_garbage_exec('perl', false)

View File

@ -8,6 +8,7 @@ local source = helpers.source
local missing_provider = helpers.missing_provider local missing_provider = helpers.missing_provider
local matches = helpers.matches local matches = helpers.matches
local pcall_err = helpers.pcall_err local pcall_err = helpers.pcall_err
local funcs = helpers.funcs
do do
clear() clear()
@ -93,16 +94,40 @@ describe('python3 provider', function()
ghi]]) ghi]])
end) end)
it('py3eval', function() describe('py3eval()', function()
eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]])) it('works', function()
eq({1, 2, {['key'] = 'val'}}, funcs.py3eval('[1, 2, {"key": "val"}]'))
end)
it('errors out when given non-string', function()
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(10)'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:_null_dict)'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:_null_list)'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(0.0)'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(function("tr"))'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:true)'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:false)'))
eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:null)'))
end)
it('accepts NULL string', function()
matches('.*SyntaxError.*', pcall_err(eval, 'py3eval($XXX_NONEXISTENT_VAR_XXX)'))
end)
end) end)
it('pyxeval #10758', function() it('pyxeval #10758', function()
eq(0, eval([[&pyxversion]])) eq(3, eval([[&pyxversion]]))
eq(3, eval([[pyxeval('sys.version_info[:3][0]')]])) eq(3, eval([[pyxeval('sys.version_info[:3][0]')]]))
eq(3, eval([[&pyxversion]])) eq(3, eval([[&pyxversion]]))
end) end)
it("setting 'pyxversion'", function()
command 'set pyxversion=3' -- no error
eq('Vim(set):E474: Invalid argument: pyxversion=2', pcall_err(command, 'set pyxversion=2'))
command 'set pyxversion=0' -- allowed, but equivalent to pyxversion=3
eq(3, eval'&pyxversion')
end)
it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function()
helpers.add_builddir_to_rtp() helpers.add_builddir_to_rtp()
source([=[ source([=[
@ -120,3 +145,15 @@ describe('python3 provider', function()
assert_alive() assert_alive()
end) end)
end) end)
describe('python2 feature test', function()
-- python2 is not supported, so correct behaviour is to return 0
it('works', function()
eq(0, funcs.has('python2'))
eq(0, funcs.has('python'))
eq(0, funcs.has('python_compiled'))
eq(0, funcs.has('python_dynamic'))
eq(0, funcs.has('python_dynamic_'))
eq(0, funcs.has('python_'))
end)
end)

View File

@ -1,123 +0,0 @@
local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local neq = helpers.neq
local feed = helpers.feed
local clear = helpers.clear
local funcs = helpers.funcs
local meths = helpers.meths
local insert = helpers.insert
local expect = helpers.expect
local command = helpers.command
local exc_exec = helpers.exc_exec
local write_file = helpers.write_file
local curbufmeths = helpers.curbufmeths
local missing_provider = helpers.missing_provider
local matches = helpers.matches
local pcall_err = helpers.pcall_err
do
clear()
local reason = missing_provider('python')
if reason then
it(':python reports E319 if provider is missing', function()
local expected = [[Vim%(py.*%):E319: No "python" provider found.*]]
matches(expected, pcall_err(command, 'py print("foo")'))
matches(expected, pcall_err(command, 'pyfile foo'))
end)
pending(string.format('Python 2 (or the pynvim module) is broken/missing (%s)', reason), function() end)
return
end
end
before_each(function()
clear()
command('python import vim')
end)
describe('python feature test', function()
it('works', function()
eq(1, funcs.has('python'))
eq(1, funcs.has('python_compiled'))
eq(1, funcs.has('python_dynamic'))
eq(0, funcs.has('python_dynamic_'))
eq(0, funcs.has('python_'))
end)
end)
describe(':python command', function()
it('works with a line', function()
command('python vim.vars["set_by_python"] = [100, 0]')
eq({100, 0}, meths.get_var('set_by_python'))
end)
-- TODO(ZyX-I): works with << EOF
-- TODO(ZyX-I): works with execute 'python' line1."\n".line2."\n"…
it('supports nesting', function()
command([[python vim.command('python vim.command("python vim.command(\'let set_by_nested_python = 555\')")')]])
eq(555, meths.get_var('set_by_nested_python'))
end)
it('supports range', function()
insert([[
line1
line2
line3
line4]])
feed('ggjvj:python vim.vars["range"] = vim.current.range[:]<CR>')
eq({'line2', 'line3'}, meths.get_var('range'))
end)
end)
describe(':pyfile command', function()
it('works', function()
local fname = 'pyfile.py'
write_file(fname, 'vim.command("let set_by_pyfile = 123")')
command('pyfile pyfile.py')
eq(123, meths.get_var('set_by_pyfile'))
os.remove(fname)
end)
end)
describe(':pydo command', function()
it('works', function()
-- :pydo 42 returns None for all lines,
-- the buffer should not be changed
command('normal :pydo 42')
eq(false, curbufmeths.get_option('modified'))
-- insert some text
insert('abc\ndef\nghi')
expect([[
abc
def
ghi]])
-- go to top and select and replace the first two lines
feed('ggvj:pydo return str(linenr)<CR>')
expect([[
1
2
ghi]])
end)
end)
describe('pyeval()', function()
it('works', function()
eq({1, 2, {['key'] = 'val'}}, funcs.pyeval('[1, 2, {"key": "val"}]'))
end)
it('errors out when given non-string', function()
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(10)'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:_null_dict)'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:_null_list)'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(0.0)'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(function("tr"))'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:true)'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:false)'))
eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:null)'))
end)
it('accepts NULL string', function()
neq(0, exc_exec('call pyeval($XXX_NONEXISTENT_VAR_XXX)'))
end)
end)