changeset 10722:7598ce51bf2a v8.0.0251

patch 8.0.0251: not easy to select Python 2 or 3 commit https://github.com/vim/vim/commit/f42dd3c3901ea0ba38e67a616aea9953cae81b8d Author: Bram Moolenaar <Bram@vim.org> 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)
author Christian Brabandt <cb@256bit.org>
date Sat, 28 Jan 2017 16:15:04 +0100
parents 9177c4f6a229
children 72cb227772e7
files Filelist runtime/doc/eval.txt runtime/doc/if_pyth.txt runtime/doc/index.txt runtime/doc/options.txt runtime/doc/quickref.txt runtime/optwin.vim src/Makefile src/evalfunc.c src/ex_cmds.h src/ex_cmds2.c src/ex_docmd.c src/if_python.c src/if_python3.c src/option.c src/option.h src/proto/ex_cmds2.pro src/testdir/Make_all.mak src/testdir/pyxfile/py2_magic.py src/testdir/pyxfile/py2_shebang.py src/testdir/pyxfile/py3_magic.py src/testdir/pyxfile/py3_shebang.py src/testdir/pyxfile/pyx.py src/testdir/test_pyx2.vim src/testdir/test_pyx3.vim src/userfunc.c src/version.c
diffstat 27 files changed, 603 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- 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 \
--- 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.
--- 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:
--- 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
--- 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
--- 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
--- 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 <SID>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 <SID>OptionL("fdn")
 endif
@@ -1324,6 +1324,10 @@ if exists("&perldll")
   call append("$", "perldll\tname of the Perl dynamic library")
   call <SID>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 <SID>OptionG("pythondll", &pythondll)
--- 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? \
--- 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
  */
--- 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),
--- 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}"
  */
--- 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
--- 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,
--- 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,
--- 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)
     {
--- 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
--- 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);
--- 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 \
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)
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)
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)
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)
new file mode 100644
--- /dev/null
+++ b/src/testdir/pyxfile/pyx.py
@@ -0,0 +1,2 @@
+import sys
+print(sys.version)
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
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
--- 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'
--- 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,