# HG changeset patch # User Christian Brabandt # Date 1485616504 -3600 # Node ID 7598ce51bf2af6b5545e5da4e02b315657d3338a # Parent 9177c4f6a229740a7b501bcc83c7e478948203a8 patch 8.0.0251: not easy to select Python 2 or 3 commit https://github.com/vim/vim/commit/f42dd3c3901ea0ba38e67a616aea9953cae81b8d Author: Bram Moolenaar Date: Sat Jan 28 16:06:38 2017 +0100 patch 8.0.0251: not easy to select Python 2 or 3 Problem: It is not so easy to write a script that works with both Python 2 and Python 3, even when the Python code works with both. Solution: Add 'pyxversion', :pyx, etc. (Marc Weber, Ken Takata) diff --git a/Filelist b/Filelist --- a/Filelist +++ b/Filelist @@ -122,6 +122,7 @@ SRC_ALL = \ src/testdir/pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py \ src/testdir/python_after/*.py \ src/testdir/python_before/*.py \ + src/testdir/pyxfile/*.py \ src/testdir/bench*.in \ src/testdir/bench*.vim \ src/testdir/samples/*.txt \ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2239,6 +2239,7 @@ printf({fmt}, {expr1}...) String format pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression py3eval({expr}) any evaluate |python3| expression +pyxeval({expr}) any evaluate |python_x| expression range({expr} [, {max} [, {stride}]]) List items from {expr} to {max} readfile({fname} [, {binary} [, {max}]]) @@ -6163,6 +6164,14 @@ pyeval({expr}) *pyeval()* non-string keys result in error. {only available when compiled with the |+python| feature} +pyxeval({expr}) *pyxeval()* + Evaluate Python expression {expr} and return its result + converted to Vim data structures. + Uses Python 2 or 3, see |python_x| and 'pyxversion'. + See also: |pyeval()|, |py3eval()| + {only available when compiled with the |+python| or the + |+python3| feature} + *E726* *E727* range({expr} [, {max} [, {stride}]]) *range()* Returns a |List| with Numbers: @@ -8402,6 +8411,7 @@ printer Compiled with |:hardcopy| supp profile Compiled with |:profile| support. python Compiled with Python 2.x interface. |has-python| python3 Compiled with Python 3.x interface. |has-python| +pythonx Compiled with |python_x| interface. |has-pythonx| qnx QNX version of Vim. quickfix Compiled with |quickfix| support. reltime Compiled with |reltime()| support. diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -16,6 +16,7 @@ 7. vim.bindeval objects |python-binde 8. pyeval(), py3eval() Vim functions |python-pyeval| 9. Dynamic loading |python-dynamic| 10. Python 3 |python3| +11. Python X |python_x| {Vi does not have any of these commands} @@ -711,6 +712,7 @@ 8. pyeval() and py3eval() Vim functions To facilitate bi-directional interface, you can use |pyeval()| and |py3eval()| functions to evaluate Python expressions and pass their values to VimL. +|pyxeval()| is also available. ============================================================================== 9. Dynamic loading *python-dynamic* @@ -812,4 +814,68 @@ loaded at a time, just checking if Pytho the other one from being available. ============================================================================== +11. Python X *python_x* *pythonx* + +Because most python code can be written so that it works with python 2.6+ and +python 3 the pyx* functions and commands have been writen. They work exactly +the same as the Python 2 and 3 variants, but select the Python version using +the 'pyxversion' setting. + +You should set 'pyxversion' in your |.vimrc| to prefer Python 2 or Python 3 +for Python commands. If you change this setting at runtime you may risk that +state of plugins (such as initialization) may be lost. + +If you want to use a module, you can put it in the {rtp}/pythonx directory. +See |pythonx-directory|. + + *:pyx* *:pythonx* +The `:pyx` and `:pythonx` commands work similar to `:python`. A simple check +if the `:pyx` command is working: > + :pyx print("Hello") + +To see what version of Python is being used: > + :pyx import sys + :pyx print(sys.version) +< + *:pyxfile* *python_x-special-comments* +The `:pyxfile` command works similar to `:pyfile`. However you can add one of +these comments to force Vim using `: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' setting 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* +The `:pyxdo` command works similar to `:pydo`. + + *has-pythonx* +You can test if pyx* commands are available with: > + if has('pythonx') + echo 'pyx* commands are available. (Python ' . &pyx . ')' + endif + +When compiled with only one of |+python| or |+python3|, the has() returns 1. +When compiled with both |+python| and |+python3|, the test depends on the +'pyxversion' setting. If 'pyxversion' is 0, it tests Python 3 first, and if +it is not available then Python 2. If 'pyxversion' is 2 or 3, it tests only +Python 2 or 3 respectively. + +Note that for has('pythonx') to work it may try to dynamically load Python 3 +or 2. This may have side effects, especially when Vim can only load one of +the two. + +If a user prefers Python 2 and want to fallback to Python 3, he needs to set +'pyxversion' explicitly in his |.vimrc|. E.g.: > + if has('python') + set pyx=2 + elseif has('python3') + set pyx=3 + endif + +============================================================================== vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1440,6 +1440,10 @@ tag command action ~ |:python| :py[thon] execute Python command |:pydo| :pyd[o] execute Python command for each line |:pyfile| :pyf[ile] execute Python script file +|:pyx| :pyx execute |python_x| command +|:pythonx| :pythonx same as :pyx +|:pyxdo| :pyxd[o] execute |python_x| command for each line +|:pyxfile| :pyxf[ile] execute |python_x| script file |:quit| :q[uit] quit current window (when one window quit Vim) |:quitall| :quita[ll] quit Vim |:qall| :qa[ll] quit Vim diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5789,6 +5789,34 @@ A jump table for the options with a shor This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + *'pyxversion'* *'pyx'* +'pyxversion' 'pyx' number (default depends on the build) + global + {not in Vi} + {only available when compiled with the |+python| or + the |+python3| feature} + Specifies the python version used for pyx* functions and commands + |python_x|. The default value is as follows: + + Compiled with 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 Vim is compiled with only |+python| or |+python3| setting + 'pyxversion' has no effect. The pyx* functions and commands are + always the same as the compiled version. + + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + *'quoteescape'* *'qe'* 'quoteescape' 'qe' string (default "\") local to buffer diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -835,6 +835,7 @@ Short explanation of each option: *opti 'pumheight' 'ph' maximum height of the popup menu 'pythondll' name of the Python 2 dynamic library 'pythonthreedll' name of the Python 3 dynamic library +'pyxversion' 'pyx' Python version used for pyx* commands 'quoteescape' 'qe' escape characters used in a string 'readonly' 'ro' disallow writing the buffer 'redrawtime' 'rdt' timeout for 'hlsearch' and |:match| highlighting diff --git a/runtime/optwin.vim b/runtime/optwin.vim --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -923,7 +923,7 @@ if has("folding") call append("$", "foldmarker\tmarkers used when 'foldmethod' is \"marker\"") call append("$", "\t(local to window)") call OptionL("fmr") - call append("$", "foldnestmax\tmaximum fold depth for when 'foldmethod is \"indent\" or \"syntax\"") + call append("$", "foldnestmax\tmaximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"") call append("$", "\t(local to window)") call OptionL("fdn") endif @@ -1324,6 +1324,10 @@ if exists("&perldll") call append("$", "perldll\tname of the Perl dynamic library") call OptionG("perldll", &perldll) endif +if has('pythonx') + call append("$", "pyxversion\twhether to use Python 2 or 3") + call append("$", " \tset pyx=" . &wd) +endif if exists("&pythondll") call append("$", "pythondll\tname of the Python 2 dynamic library") call OptionG("pythondll", &pythondll) diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -2151,6 +2151,8 @@ test_arglist \ test_popup \ test_profile \ test_put \ + test_pyx2 \ + test_pyx3 \ test_quickfix \ test_regexp_latin \ test_regexp_utf8 \ @@ -2754,6 +2756,7 @@ shadow: runtime pixmaps ../../testdir/*.vim \ ../../testdir/*.py \ ../../testdir/python* \ + ../../testdir/pyxfile \ ../../testdir/sautest \ ../../testdir/samples \ ../../testdir/test83-tags? \ diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -289,6 +289,9 @@ static void f_py3eval(typval_T *argvars, #ifdef FEAT_PYTHON static void f_pyeval(typval_T *argvars, typval_T *rettv); #endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +static void f_pyxeval(typval_T *argvars, typval_T *rettv); +#endif static void f_range(typval_T *argvars, typval_T *rettv); static void f_readfile(typval_T *argvars, typval_T *rettv); static void f_reltime(typval_T *argvars, typval_T *rettv); @@ -716,6 +719,9 @@ static struct fst #ifdef FEAT_PYTHON {"pyeval", 1, 1, f_pyeval}, #endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) + {"pyxeval", 1, 1, f_pyxeval}, +#endif {"range", 1, 3, f_range}, {"readfile", 1, 3, f_readfile}, {"reltime", 0, 2, f_reltime}, @@ -5734,15 +5740,13 @@ f_has(typval_T *argvars, typval_T *rettv #ifdef FEAT_PERSISTENT_UNDO "persistent_undo", #endif -#ifdef FEAT_PYTHON -#ifndef DYNAMIC_PYTHON +#if defined(FEAT_PYTHON) && !defined(DYNAMIC_PYTHON) "python", -#endif -#endif -#ifdef FEAT_PYTHON3 -#ifndef DYNAMIC_PYTHON3 + "pythonx", +#endif +#if defined(FEAT_PYTHON3) && !defined(DYNAMIC_PYTHON3) "python3", -#endif + "pythonx", #endif #ifdef FEAT_POSTSCRIPT "postscript", @@ -5972,17 +5976,30 @@ f_has(typval_T *argvars, typval_T *rettv else if (STRICMP(name, "ruby") == 0) n = ruby_enabled(FALSE); #endif -#ifdef FEAT_PYTHON #ifdef DYNAMIC_PYTHON else if (STRICMP(name, "python") == 0) n = python_enabled(FALSE); #endif -#endif -#ifdef FEAT_PYTHON3 #ifdef DYNAMIC_PYTHON3 else if (STRICMP(name, "python3") == 0) n = python3_enabled(FALSE); #endif +#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3) + else if (STRICMP(name, "pythonx") == 0) + { +# if defined(DYNAMIC_PYTHON) && defined(DYNAMIC_PYTHON3) + if (p_pyx == 0) + n = python3_enabled(FALSE) || python_enabled(FALSE); + else if (p_pyx == 3) + n = python3_enabled(FALSE); + else if (p_pyx == 2) + n = python_enabled(FALSE); +# elif defined(DYNAMIC_PYTHON) + n = python_enabled(FALSE); +# elif defined(DYNAMIC_PYTHON3) + n = python3_enabled(FALSE); +# endif + } #endif #ifdef DYNAMIC_PERL else if (STRICMP(name, "perl") == 0) @@ -8007,6 +8024,9 @@ f_py3eval(typval_T *argvars, typval_T *r char_u *str; char_u buf[NUMBUFLEN]; + if (p_pyx == 0) + p_pyx = 3; + str = get_tv_string_buf(&argvars[0], buf); do_py3eval(str, rettv); } @@ -8022,11 +8042,35 @@ f_pyeval(typval_T *argvars, typval_T *re char_u *str; char_u buf[NUMBUFLEN]; + if (p_pyx == 0) + p_pyx = 2; + str = get_tv_string_buf(&argvars[0], buf); do_pyeval(str, rettv); } #endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +/* + * "pyxeval()" function + */ + static void +f_pyxeval(typval_T *argvars, typval_T *rettv) +{ +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + f_pyeval(argvars, rettv); + else + f_py3eval(argvars, rettv); +# elif defined(FEAT_PYTHON) + f_pyeval(argvars, rettv); +# elif defined(FEAT_PYTHON3) + f_py3eval(argvars, rettv); +# endif +} +#endif + /* * "range()" function */ diff --git a/src/ex_cmds.h b/src/ex_cmds.h --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -1132,6 +1132,18 @@ EX(CMD_python3, "python3", ex_py3, EX(CMD_py3file, "py3file", ex_py3file, RANGE|FILE1|NEEDARG|CMDWIN, ADDR_LINES), +EX(CMD_pyx, "pyx", ex_pyx, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pyxdo, "pyxdo", ex_pyxdo, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pythonx, "pythonx", ex_pyx, + RANGE|EXTRA|NEEDARG|CMDWIN, + ADDR_LINES), +EX(CMD_pyxfile, "pyxfile", ex_pyxfile, + RANGE|FILE1|NEEDARG|CMDWIN, + ADDR_LINES), EX(CMD_quit, "quit", ex_quit, BANG|RANGE|COUNT|NOTADR|TRLBAR|CMDWIN, ADDR_WINDOWS), diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -3675,6 +3675,194 @@ ex_options( } #endif +#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO) + +# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO) +/* + * Detect Python 3 or 2, and initialize 'pyxversion'. + */ + void +init_pyxversion(void) +{ + if (p_pyx == 0) + { + if (python3_enabled(FALSE)) + p_pyx = 3; + else if (python_enabled(FALSE)) + p_pyx = 2; + } +} +# endif + +/* + * 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 = mch_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; + int v = requires_py_version(fname); + +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); +# endif + if (v == 0) + { +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + /* user didn't choose a preference, 'pyx' is used */ + v = p_pyx; +# elif defined(FEAT_PYTHON) + v = 2; +# elif defined(FEAT_PYTHON3) + v = 3; +# endif + } + + /* + * now source, if required python version is not supported show + * unobtrusive message. + */ + if (eap == NULL) + vim_memset(&ex, 0, sizeof(ex)); + else + ex = *eap; + ex.arg = fname; + ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3"); + + if (v == 2) + { +# ifdef FEAT_PYTHON + ex_pyfile(&ex); +# else + vim_snprintf((char *)IObuff, IOSIZE, + _("W20: Required python version 2.x not supported, ignoring file: %s"), + fname); + MSG(IObuff); +# endif + return; + } + else + { +# ifdef FEAT_PYTHON3 + ex_py3file(&ex); +# else + vim_snprintf((char *)IObuff, IOSIZE, + _("W21: Required python version 3.x not supported, ignoring file: %s"), + fname); + MSG(IObuff); +# endif + return; + } +} + +/* + * ":pyxfile {fname}" + */ + void +ex_pyxfile(exarg_T *eap) +{ + source_pyx_file(eap, eap->arg); +} + +/* + * ":pyx" + */ + void +ex_pyx(exarg_T *eap) +{ +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + ex_python(eap); + else + ex_py3(eap); +# elif defined(FEAT_PYTHON) + ex_python(eap); +# elif defined(FEAT_PYTHON3) + ex_py3(eap); +# endif +} + +/* + * ":pyxdo" + */ + void +ex_pyxdo(exarg_T *eap) +{ +# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) + init_pyxversion(); + if (p_pyx == 2) + ex_pydo(eap); + else + ex_py3do(eap); +# elif defined(FEAT_PYTHON) + ex_pydo(eap); +# elif defined(FEAT_PYTHON3) + ex_py3do(eap); +# endif +} + +#endif + /* * ":source {fname}" */ diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -288,6 +288,11 @@ static void ex_popup(exarg_T *eap); # define ex_py3do ex_ni # define ex_py3file ex_ni #endif +#if !defined(FEAT_PYTHON) && !defined(FEAT_PYTHON3) +# define ex_pyx ex_script_ni +# define ex_pyxdo ex_ni +# define ex_pyxfile ex_ni +#endif #ifndef FEAT_TCL # define ex_tcl ex_script_ni # define ex_tcldo ex_ni diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -1114,6 +1114,9 @@ ex_python(exarg_T *eap) { char_u *script; + if (p_pyx == 0) + p_pyx = 2; + script = script_get(eap, eap->arg); if (!eap->skip) { @@ -1137,6 +1140,9 @@ ex_pyfile(exarg_T *eap) const char *file = (char *)eap->arg; char *p; + if (p_pyx == 0) + p_pyx = 2; + /* Have to do it like this. PyRun_SimpleFile requires you to pass a * stdio file pointer, but Vim and the Python DLL are compiled with * different options under Windows, meaning that stdio pointers aren't @@ -1175,6 +1181,9 @@ ex_pyfile(exarg_T *eap) void ex_pydo(exarg_T *eap) { + if (p_pyx == 0) + p_pyx = 2; + DoPyCommand((char *)eap->arg, (rangeinitializer) init_range_cmd, (runner)run_do, diff --git a/src/if_python3.c b/src/if_python3.c --- a/src/if_python3.c +++ b/src/if_python3.c @@ -1004,6 +1004,9 @@ ex_py3(exarg_T *eap) { char_u *script; + if (p_pyx == 0) + p_pyx = 3; + script = script_get(eap, eap->arg); if (!eap->skip) { @@ -1028,6 +1031,9 @@ ex_py3file(exarg_T *eap) char *p; int i; + if (p_pyx == 0) + p_pyx = 3; + /* Have to do it like this. PyRun_SimpleFile requires you to pass a * stdio file pointer, but Vim and the Python DLL are compiled with * different options under Windows, meaning that stdio pointers aren't @@ -1080,6 +1086,9 @@ ex_py3file(exarg_T *eap) void ex_py3do(exarg_T *eap) { + if (p_pyx == 0) + p_pyx = 3; + DoPyCommand((char *)eap->arg, (rangeinitializer)init_range_cmd, (runner)run_do, diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -479,6 +479,17 @@ struct vimoption # define HIGHLIGHT_INIT "8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,N:CursorLineNr,r:Question,s:StatusLine,S:StatusLineNC,t:Title,v:Visual,w:WarningMsg,W:WildMenu,>:SignColumn,*:TabLine,#:TabLineSel,_:TabLineFill" #endif +/* Default python version for pyx* commands */ +#if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) +# define DEFAULT_PYTHON_VER 0 +#elif defined(FEAT_PYTHON3) +# define DEFAULT_PYTHON_VER 3 +#elif defined(FEAT_PYTHON) +# define DEFAULT_PYTHON_VER 2 +#else +# define DEFAULT_PYTHON_VER 0 +#endif + /* * options[] is initialized here. * The order of the options MUST be alphabetic for ":set all" and findoption(). @@ -2143,6 +2154,14 @@ static struct vimoption options[] = {(char_u *)DYNAMIC_PYTHON_DLL, (char_u *)0L} SCRIPTID_INIT}, #endif + {"pyxversion", "pyx", P_NUM|P_VI_DEF|P_SECURE, +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) + (char_u *)&p_pyx, PV_NONE, +#else + (char_u *)NULL, PV_NONE, +#endif + {(char_u *)DEFAULT_PYTHON_VER, (char_u *)0L} + SCRIPTID_INIT}, {"quoteescape", "qe", P_STRING|P_ALLOCED|P_VI_DEF, #ifdef FEAT_TEXTOBJ (char_u *)&p_qe, PV_QE, @@ -8826,6 +8845,15 @@ set_num_option( mzvim_reset_timer(); #endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) + /* 'pyxversion' */ + else if (pp == &p_pyx) + { + if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) + errmsg = e_invarg; + } +#endif + /* sync undo before 'undolevels' changes */ else if (pp == &p_ul) { diff --git a/src/option.h b/src/option.h --- a/src/option.h +++ b/src/option.h @@ -694,6 +694,9 @@ EXTERN char_u *p_py3dll; /* 'pythonthree #if defined(DYNAMIC_PYTHON) EXTERN char_u *p_pydll; /* 'pythondll' */ #endif +#if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) +EXTERN long p_pyx; /* 'pyxversion' */ +#endif #ifdef FEAT_RELTIME EXTERN long p_rdt; /* 'redrawtime' */ #endif diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro --- a/src/proto/ex_cmds2.pro +++ b/src/proto/ex_cmds2.pro @@ -75,6 +75,10 @@ int do_in_runtimepath(char_u *name, int void ex_packloadall(exarg_T *eap); void ex_packadd(exarg_T *eap); void ex_options(exarg_T *eap); +void init_pyxversion(void); +void ex_pyxfile(exarg_T *eap); +void ex_pyx(exarg_T *eap); +void ex_pyxdo(exarg_T *eap); void ex_source(exarg_T *eap); linenr_T *source_breakpoint(void *cookie); int *source_dbg_tick(void *cookie); diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -176,6 +176,8 @@ NEW_TESTS = test_arglist.res \ test_packadd.res \ test_perl.res \ test_profile.res \ + test_pyx2.res \ + test_pyx3.res \ test_quickfix.res \ test_retab.res \ test_ruby.res \ diff --git a/src/testdir/pyxfile/py2_magic.py b/src/testdir/pyxfile/py2_magic.py new file mode 100644 --- /dev/null +++ b/src/testdir/pyxfile/py2_magic.py @@ -0,0 +1,4 @@ +# requires python 2.x + +import sys +print(sys.version) diff --git a/src/testdir/pyxfile/py2_shebang.py b/src/testdir/pyxfile/py2_shebang.py new file mode 100644 --- /dev/null +++ b/src/testdir/pyxfile/py2_shebang.py @@ -0,0 +1,4 @@ +#!/usr/bin/python2 + +import sys +print(sys.version) diff --git a/src/testdir/pyxfile/py3_magic.py b/src/testdir/pyxfile/py3_magic.py new file mode 100644 --- /dev/null +++ b/src/testdir/pyxfile/py3_magic.py @@ -0,0 +1,4 @@ +# requires python 3.x + +import sys +print(sys.version) diff --git a/src/testdir/pyxfile/py3_shebang.py b/src/testdir/pyxfile/py3_shebang.py new file mode 100644 --- /dev/null +++ b/src/testdir/pyxfile/py3_shebang.py @@ -0,0 +1,4 @@ +#!/usr/bin/python3 + +import sys +print(sys.version) diff --git a/src/testdir/pyxfile/pyx.py b/src/testdir/pyxfile/pyx.py new file mode 100644 --- /dev/null +++ b/src/testdir/pyxfile/pyx.py @@ -0,0 +1,2 @@ +import sys +print(sys.version) diff --git a/src/testdir/test_pyx2.vim b/src/testdir/test_pyx2.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_pyx2.vim @@ -0,0 +1,74 @@ +" Test for pyx* commands and functions with Python 2. + +set pyx=2 +if !has('python') + finish +endif + +let s:py2pattern = '^2\.[0-7]\.\d\+' +let s:py3pattern = '^3\.\d\+\.\d\+' + + +func Test_has_pythonx() + call assert_true(has('pythonx')) +endfunc + + +func Test_pyx() + redir => var + pyx << EOF +import sys +print(sys.version) +EOF + redir END + call assert_match(s:py2pattern, split(var)[0]) +endfunc + + +func Test_pyxdo() + pyx import sys + enew + pyxdo return sys.version.split("\n")[0] + call assert_match(s:py2pattern, split(getline('.'))[0]) +endfunc + + +func Test_pyxeval() + pyx import sys + call assert_match(s:py2pattern, split(pyxeval('sys.version'))[0]) +endfunc + + +func Test_pyxfile() + " No special comments nor shebangs + redir => var + pyxfile pyxfile/pyx.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + " Python 2 special comment + redir => var + pyxfile pyxfile/py2_magic.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + " Python 2 shebang + redir => var + pyxfile pyxfile/py2_shebang.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + if has('python3') + " Python 3 special comment + redir => var + pyxfile pyxfile/py3_magic.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + " Python 3 shebang + redir => var + pyxfile pyxfile/py3_shebang.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + endif +endfunc diff --git a/src/testdir/test_pyx3.vim b/src/testdir/test_pyx3.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_pyx3.vim @@ -0,0 +1,74 @@ +" Test for pyx* commands and functions with Python 3. + +set pyx=3 +if !has('python3') + finish +endif + +let s:py2pattern = '^2\.[0-7]\.\d\+' +let s:py3pattern = '^3\.\d\+\.\d\+' + + +func Test_has_pythonx() + call assert_true(has('pythonx')) +endfunc + + +func Test_pyx() + redir => var + pyx << EOF +import sys +print(sys.version) +EOF + redir END + call assert_match(s:py3pattern, split(var)[0]) +endfunc + + +func Test_pyxdo() + pyx import sys + enew + pyxdo return sys.version.split("\n")[0] + call assert_match(s:py3pattern, split(getline('.'))[0]) +endfunc + + +func Test_pyxeval() + pyx import sys + call assert_match(s:py3pattern, split(pyxeval('sys.version'))[0]) +endfunc + + +func Test_pyxfile() + " No special comments nor shebangs + redir => var + pyxfile pyxfile/pyx.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + " Python 3 special comment + redir => var + pyxfile pyxfile/py3_magic.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + " Python 3 shebang + redir => var + pyxfile pyxfile/py3_shebang.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + if has('python') + " Python 2 special comment + redir => var + pyxfile pyxfile/py2_magic.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + " Python 2 shebang + redir => var + pyxfile pyxfile/py2_shebang.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + endif +endfunc diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2102,7 +2102,9 @@ ex_function(exarg_T *eap) arg = skipwhite(skiptowhite(p)); if (arg[0] == '<' && arg[1] =='<' && ((p[0] == 'p' && p[1] == 'y' - && (!ASCII_ISALPHA(p[2]) || p[2] == 't')) + && (!ASCII_ISALNUM(p[2]) || p[2] == 't' + || ((p[2] == '3' || p[2] == 'x') + && !ASCII_ISALPHA(p[3])))) || (p[0] == 'p' && p[1] == 'e' && (!ASCII_ISALPHA(p[2]) || p[2] == 'r')) || (p[0] == 't' && p[1] == 'c' diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 251, +/**/ 250, /**/ 249,