shadow: ./src/shadow
- features: huge
coverage: true
+ - features: huge
+ compiler: clang
+ extra: none
+ interface: dynamic
+ python3: stable-abi
- features: huge
compiler: gcc
coverage: true
+ interface: dynamic
extra: testgui
uchar: true
luaver: lua5.4
;;
huge)
echo "TEST=scripttests test_libvterm"
- echo "CONFOPT=--enable-perlinterp --enable-pythoninterp --enable-python3interp --enable-rubyinterp --enable-luainterp --enable-tclinterp"
+ if ${{ matrix.interface == 'dynamic' }}; then
+ if ${{ matrix.python3 == 'stable-abi' }}; then
+ PYTHON3_FLAGS="--with-python3-stable-abi=3.8"
+ else
+ PYTHON3_FLAGS=""
+ fi
+ echo "CONFOPT=--enable-perlinterp=dynamic --enable-pythoninterp=dynamic --enable-python3interp=dynamic --enable-rubyinterp=dynamic --enable-luainterp=dynamic --enable-tclinterp=dynamic ${PYTHON3_FLAGS}"
+ else
+ echo "CONFOPT=--enable-perlinterp --enable-pythoninterp --enable-python3interp --enable-rubyinterp --enable-luainterp --enable-tclinterp"
+ fi
;;
esac
fail-fast: false
matrix:
include:
- - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: no, arch: x64 }
- - { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: yes, arch: x86, coverage: yes }
+ - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: no, arch: x64, python3: stable }
+ - { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: yes, arch: x86, python3: stable, coverage: yes }
- { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: yes, arch: x86 }
- { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: no, arch: x64, coverage: yes }
- { features: NORMAL, toolchain: msvc, VIMDLL: yes, GUI: no, arch: x86 }
) else (
set GUI=${{ matrix.GUI }}
)
+ if "${{ matrix.python3 }}"=="stable" (
+ set PYTHON3_STABLE=yes
+ ) else (
+ set PYTHON3_STABLE=no
+ )
if "${{ matrix.features }}"=="HUGE" (
nmake -nologo -f Make_mvc.mak ^
FEATURES=${{ matrix.features }} ^
DYNAMIC_LUA=yes LUA=%LUA_DIR% ^
DYNAMIC_PYTHON=yes PYTHON=%PYTHON_DIR% ^
DYNAMIC_PYTHON3=yes PYTHON3=%PYTHON3_DIR% ^
+ DYNAMIC_PYTHON3_STABLE_ABI=%PYTHON3_STABLE% ^
DYNAMIC_SODIUM=yes SODIUM=%SODIUM_DIR%
) else (
nmake -nologo -f Make_mvc.mak ^
else
GUI=${{ matrix.GUI }}
fi
+ if [ "${{ matrix.python3 }}" = "stable" ]; then
+ PYTHON3_STABLE=yes
+ else
+ PYTHON3_STABLE=no
+ fi
if [ "${{ matrix.features }}" = "HUGE" ]; then
mingw32-make -f Make_ming.mak -j2 \
FEATURES=${{ matrix.features }} \
DYNAMIC_LUA=yes LUA=${LUA_DIR_SLASH} \
DYNAMIC_PYTHON=yes PYTHON=${PYTHON_DIR} \
DYNAMIC_PYTHON3=yes PYTHON3=${PYTHON3_DIR} \
+ DYNAMIC_PYTHON3_STABLE_ABI=${PYTHON3_STABLE} \
DYNAMIC_SODIUM=yes SODIUM=${SODIUM_DIR} \
STATIC_STDCPLUS=yes COVERAGE=${{ matrix.coverage }}
else
python3 Python 3.x interface available. |has-python|
python3_compiled Compiled with Python 3.x interface. |has-python|
python3_dynamic Python 3.x interface is dynamically loaded. |has-python|
+python3_stable Python 3.x interface is using Python Stable ABI. |has-python|
pythonx Python 2.x and/or 3.x interface available. |python_x|
qnx QNX version of Vim.
quickfix Compiled with |quickfix| support.
".exe" is not added to v:progpath.
Read-only.
+ *v:python3_version* *python3-version-variable*
+v:python3_version
+ Version of Python 3 that Vim was built against. When
+ Python is loaded dynamically (|python-dynamic|), this version
+ should exactly match the Python library up to the minor
+ version (e.g. 3.10.2 and 3.10.3 are compatible as the minor
+ version is "10", whereas 3.9.4 and 3.10.3 are not compatible).
+ When |python-stable-abi| is used, this will be the minimum Python
+ version that you can use instead. (e.g. if v:python3_version
+ indicates 3.9, you can use 3.9, 3.10, or anything above).
+
+ This number is encoded as a hex number following Python ABI
+ versioning conventions. Do the following to have a
+ human-readable full version in hex: >
+ echo printf("%08X", v:python3_version)
+< You can obtain only the minor version by doing: >
+ echo and(v:python3_version>>16,0xff)
+< Read-only.
+
*v:register* *register-variable*
v:register The name of the register in effect for the current normal mode
command (regardless of whether that command actually used a
The 'pythondll' or 'pythonthreedll' option can be used to specify the Python
shared library file instead of DYNAMIC_PYTHON_DLL or DYNAMIC_PYTHON3_DLL file
what were specified at compile time. The version of the shared library must
-match the Python 2.x or Python 3 version Vim was compiled with.
+match the Python 2.x or Python 3 version (|v:python3_version|) Vim was
+compiled with unless using |python3-stable-abi|.
+
+
+Stable ABI and mixing Python versions ~
+ *python-stable* *python-stable-abi* *python3-stable-abi*
+If Vim was not compiled with Stable ABI (only available for Python 3), the
+version of the Python shared library must match the version that Vim was
+compiled with. Otherwise, mixing versions could result in unexpected crashes
+and failures. With Stable ABI, this restriction is relaxed, and any Python 3
+library with version of at least |v:python3_version| will work. See
+|has-python| for how to check if Stable ABI is supported, or see if version
+output includes |+python3/dyn-stable|.
==============================================================================
10. Python 3 *python3*
endif
endif
+When loading the library dynamically, Vim can be compiled to support Python 3
+Stable ABI (|python3-stable-abi|) which allows you to load a different version
+of Python 3 library than the one Vim was compiled with. To check it: >
+ if has('python3_dynamic')
+ if has('python3_stable')
+ echo 'support Python 3 Stable ABI.'
+ else
+ echo 'does not support Python 3 Stable ABI.'
+ echo 'only use Python 3 version ' .. v:python3_version
+ endif
+ endif
+
This also tells you whether Python is dynamically loaded, which will fail if
the runtime library cannot be found.
+python/dyn various.txt /*+python\/dyn*
+python3 various.txt /*+python3*
+python3/dyn various.txt /*+python3\/dyn*
++python3/dyn-stable various.txt /*+python3\/dyn-stable*
+quickfix various.txt /*+quickfix*
+reltime various.txt /*+reltime*
+rightleft various.txt /*+rightleft*
python-pyeval if_pyth.txt /*python-pyeval*
python-range if_pyth.txt /*python-range*
python-special-path if_pyth.txt /*python-special-path*
+python-stable if_pyth.txt /*python-stable*
+python-stable-abi if_pyth.txt /*python-stable-abi*
python-strwidth if_pyth.txt /*python-strwidth*
python-tabpage if_pyth.txt /*python-tabpage*
python-tabpages if_pyth.txt /*python-tabpages*
python2-directory if_pyth.txt /*python2-directory*
python3 if_pyth.txt /*python3*
python3-directory if_pyth.txt /*python3-directory*
+python3-stable-abi if_pyth.txt /*python3-stable-abi*
+python3-version-variable eval.txt /*python3-version-variable*
python_x if_pyth.txt /*python_x*
python_x-special-comments if_pyth.txt /*python_x-special-comments*
pythonx if_pyth.txt /*pythonx*
v:profiling eval.txt /*v:profiling*
v:progname eval.txt /*v:progname*
v:progpath eval.txt /*v:progpath*
+v:python3_version eval.txt /*v:python3_version*
v:register eval.txt /*v:register*
v:scrollstart eval.txt /*v:scrollstart*
v:searchforward eval.txt /*v:searchforward*
m *+python/dyn* Python 2 interface |python-dynamic| |/dyn|
m *+python3* Python 3 interface |python|
m *+python3/dyn* Python 3 interface |python-dynamic| |/dyn|
+m *+python3/dyn-stable*
+ Python 3 interface |python-dynamic| |python-stable| |/dyn|
N *+quickfix* |:make| and |quickfix| commands
N *+reltime* |reltime()| function, 'hlsearch'/'incsearch' timeout,
'redrawtime' option
else
PYTHON3INC=-I $(PYTHON3)/win32inc
endif
+ ifeq ($(DYNAMIC_PYTHON3_STABLE_ABI),yes)
+PYTHON3INC += -DPy_LIMITED_API=0x3080000
+ endif
endif
endif
CFLAGS += -DFEAT_PYTHON3
ifeq (yes, $(DYNAMIC_PYTHON3))
CFLAGS += -DDYNAMIC_PYTHON3 -DDYNAMIC_PYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
+ ifeq (yes, $(DYNAMIC_PYTHON3_STABLE_ABI))
+CFLAGS += -DDYNAMIC_PYTHON3_STABLE_ABI
+ endif
else
CFLAGS += -DPYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
endif
! if "$(DYNAMIC_PYTHON3)" == "yes"
CFLAGS = $(CFLAGS) -DDYNAMIC_PYTHON3 \
-DDYNAMIC_PYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
+! if "$(DYNAMIC_PYTHON3_STABLE_ABI)" == "yes"
+CFLAGS = $(CFLAGS) -DDYNAMIC_PYTHON3_STABLE_ABI
+PYTHON3_INC = $(PYTHON3_INC) -DPy_LIMITED_API=0x3080000
+PYTHON3_LIB = /nodefaultlib:python3.lib
+! else
PYTHON3_LIB = /nodefaultlib:python$(PYTHON3_VER).lib
+! endif
! else
CFLAGS = $(CFLAGS) -DPYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
PYTHON3_LIB = "$(PYTHON3)\libs\python$(PYTHON3_VER).lib"
PYTHON3_CFLAGS_EXTRA
PYTHON3_CFLAGS
PYTHON3_LIBS
+vi_cv_var_python3_stable_abi
vi_cv_path_python3
PYTHON_OBJ
PYTHON_SRC
with_python_config_dir
enable_python3interp
with_python3_command
+with_python3_stable_abi
with_python3_config_dir
enable_tclinterp
with_tclsh
--with-python-command=NAME name of the Python 2 command (default: python2 or python)
--with-python-config-dir=PATH Python's config directory (deprecated)
--with-python3-command=NAME name of the Python 3 command (default: python3 or python)
+ --with-python3-stable-abi=VERSION stable ABI version to target (e.g. 3.8)
--with-python3-config-dir=PATH Python's config directory (deprecated)
--with-tclsh=PATH which tclsh to use (default: tclsh8.0)
--with-ruby-command=RUBY name of the Ruby command (default: ruby)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yep" >&5
$as_echo "yep" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-python3-stable-abi argument" >&5
+$as_echo_n "checking --with-python3-stable-abi argument... " >&6; }
+
+
+# Check whether --with-python3-stable-abi was given.
+if test "${with_python3_stable_abi+set}" = set; then :
+ withval=$with_python3_stable_abi; vi_cv_var_python3_stable_abi="$withval"; { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_var_python3_stable_abi" >&5
+$as_echo "$vi_cv_var_python3_stable_abi" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "X$vi_cv_var_python3_stable_abi" != "X"; then
+ if ${vi_cv_var_python3_stable_abi_hex+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ vi_cv_var_python3_stable_abi_hex=`
+ ${vi_cv_path_python3} -c \
+ "major_minor='${vi_cv_var_python3_stable_abi}'.split('.'); print('0x{0:X}'.format( (int(major_minor.__getitem__(0))<<24) + (int(major_minor.__getitem__(1))<<16) ))"`
+fi
+
+ if test "X$vi_cv_var_python3_stable_abi_hex" == "X"; then
+ as_fn_error $? "can't parse Python 3 stable ABI version. It should be \"<major>.<minor>\"" "$LINENO" 5
+ fi
+ fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's abiflags" >&5
$as_echo_n "checking Python's abiflags... " >&6; }
if ${vi_cv_var_python3_abiflags+:} false; then :
else
PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags} -I${vi_cv_path_python3_epfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}"
fi
- if test "X$have_python3_config_dir" = "X1" -a "$enable_python3interp" = "dynamic"; then
- PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPYTHON3_HOME='L\"${vi_cv_path_python3_pfx}\"'"
- fi
+ if test "X$have_python3_config_dir" = "X1" -a "$enable_python3interp" = "dynamic"; then
+ PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPYTHON3_HOME='L\"${vi_cv_path_python3_pfx}\"'"
+ fi
+ if test "X$vi_cv_var_python3_stable_abi_hex" != "X"; then
+ PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPy_LIMITED_API=${vi_cv_var_python3_stable_abi_hex}"
+ fi
PYTHON3_SRC="if_python3.c"
PYTHON3_OBJ="objects/if_python3.o"
$as_echo "#define DYNAMIC_PYTHON3 1" >>confdefs.h
+ if test "X$vi_cv_var_python3_stable_abi_hex" != "X"; then
+ $as_echo "#define DYNAMIC_PYTHON3_STABLE_ABI 1" >>confdefs.h
+
+ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can do without RTLD_GLOBAL for Python" >&5
$as_echo_n "checking whether we can do without RTLD_GLOBAL for Python... " >&6; }
cflags_save=$CFLAGS
elif test "$python3_ok" = yes && test "$enable_python3interp" = "dynamic"; then
$as_echo "#define DYNAMIC_PYTHON3 1" >>confdefs.h
+ if test "X$vi_cv_var_python3_stable_abi_hex" != "X"; then
+ $as_echo "#define DYNAMIC_PYTHON3_STABLE_ABI 1" >>confdefs.h
+
+ fi
PYTHON3_SRC="if_python3.c"
PYTHON3_OBJ="objects/if_python3.o"
PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"${vi_cv_dll_name_python3}\\\""
/* Define for linking via dlopen() or LoadLibrary() */
#undef DYNAMIC_PYTHON3
+/* Define if compiled against Python 3 stable ABI / limited API */
+#undef DYNAMIC_PYTHON3_STABLE_ABI
+
/* Define if dynamic python does not require RTLD_GLOBAL */
#undef PY_NO_RTLD_GLOBAL
then
AC_MSG_RESULT(yep)
+ dnl -- get the stable ABI version if passed in
+ AC_MSG_CHECKING(--with-python3-stable-abi argument)
+ AC_SUBST(vi_cv_var_python3_stable_abi)
+ AC_ARG_WITH(python3-stable-abi, [ --with-python3-stable-abi=VERSION stable ABI version to target (e.g. 3.8)],
+ vi_cv_var_python3_stable_abi="$withval"; AC_MSG_RESULT($vi_cv_var_python3_stable_abi),
+ AC_MSG_RESULT(no))
+ if test "X$vi_cv_var_python3_stable_abi" != "X"; then
+ AC_CACHE_VAL(vi_cv_var_python3_stable_abi_hex,
+ [
+ vi_cv_var_python3_stable_abi_hex=`
+ ${vi_cv_path_python3} -c \
+ "major_minor='${vi_cv_var_python3_stable_abi}'.split('.'); print('0x{0:X}'.format( (int(major_minor.__getitem__(0))<<24) + (int(major_minor.__getitem__(1))<<16) ))"` ])
+ if test "X$vi_cv_var_python3_stable_abi_hex" == "X"; then
+ AC_MSG_ERROR([can't parse Python 3 stable ABI version. It should be "<major>.<minor>"])
+ fi
+ fi
+
dnl -- get abiflags for python 3.2 or higher (PEP 3149)
AC_CACHE_CHECK(Python's abiflags,vi_cv_var_python3_abiflags,
[
else
PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags} -I${vi_cv_path_python3_epfx}/include/python${vi_cv_var_python3_version}${vi_cv_var_python3_abiflags}"
fi
- if test "X$have_python3_config_dir" = "X1" -a "$enable_python3interp" = "dynamic"; then
- dnl Define PYTHON3_HOME if --with-python-config-dir was used
- PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPYTHON3_HOME='L\"${vi_cv_path_python3_pfx}\"'"
- fi
+ if test "X$have_python3_config_dir" = "X1" -a "$enable_python3interp" = "dynamic"; then
+ dnl Define PYTHON3_HOME if --with-python-config-dir was used
+ PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPYTHON3_HOME='L\"${vi_cv_path_python3_pfx}\"'"
+ fi
+ if test "X$vi_cv_var_python3_stable_abi_hex" != "X"; then
+ PYTHON3_CFLAGS="${PYTHON3_CFLAGS} -DPy_LIMITED_API=${vi_cv_var_python3_stable_abi_hex}"
+ fi
PYTHON3_SRC="if_python3.c"
PYTHON3_OBJ="objects/if_python3.o"
if test "$python_ok" = yes && test "$python3_ok" = yes; then
AC_DEFINE(DYNAMIC_PYTHON)
AC_DEFINE(DYNAMIC_PYTHON3)
+ if test "X$vi_cv_var_python3_stable_abi_hex" != "X"; then
+ AC_DEFINE(DYNAMIC_PYTHON3_STABLE_ABI)
+ fi
AC_MSG_CHECKING(whether we can do without RTLD_GLOBAL for Python)
cflags_save=$CFLAGS
CFLAGS="$CFLAGS $PYTHON_CFLAGS"
fi
elif test "$python3_ok" = yes && test "$enable_python3interp" = "dynamic"; then
AC_DEFINE(DYNAMIC_PYTHON3)
+ if test "X$vi_cv_var_python3_stable_abi_hex" != "X"; then
+ AC_DEFINE(DYNAMIC_PYTHON3_STABLE_ABI)
+ fi
PYTHON3_SRC="if_python3.c"
PYTHON3_OBJ="objects/if_python3.o"
PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"${vi_cv_dll_name_python3}\\\""
1
#else
0
+#endif
+ },
+ {"python3_stable",
+#if defined(FEAT_PYTHON3) && defined(DYNAMIC_PYTHON3_STABLE_ABI)
+ 1
+#else
+ 0
#endif
},
{"python3",
{VV_NAME("sizeoflong", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("sizeofpointer", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("maxcol", VAR_NUMBER), NULL, VV_RO},
+ {VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO},
};
// shorthand
set_vim_var_dict(VV_COLORNAMES, dict_alloc());
+#ifdef FEAT_PYTHON3
+ set_vim_var_nr(VV_PYTHON3_VERSION, python3_version());
+#endif
+
// Default for v:register is not 0 but '"'. This is adjusted once the
// clipboard has been setup by calling reset_reg_var().
set_reg_var(0);
#define PyErr_FORMAT2(exc, str, arg1, arg2) PyErr_Format(exc, _(str), arg1,arg2)
#define PyErr_VIM_FORMAT(str, arg) PyErr_FORMAT(VimError, str, arg)
-#define Py_TYPE_NAME(obj) ((obj)->ob_type->tp_name == NULL \
+#ifdef USE_LIMITED_API
+// Limited Python API. Need to call only exposed functions and remap macros.
+// PyTypeObject is an opaque struct.
+
+typedef struct {
+ lenfunc sq_length;
+ binaryfunc sq_concat;
+ ssizeargfunc sq_repeat;
+ ssizeargfunc sq_item;
+ void *was_sq_slice;
+ ssizeobjargproc sq_ass_item;
+ void *was_sq_ass_slice;
+ objobjproc sq_contains;
+
+ binaryfunc sq_inplace_concat;
+ ssizeargfunc sq_inplace_repeat;
+} PySequenceMethods;
+
+typedef struct {
+ lenfunc mp_length;
+ binaryfunc mp_subscript;
+ objobjargproc mp_ass_subscript;
+} PyMappingMethods;
+
+// This struct emulates the concrete _typeobject struct to allow the code to
+// work the same way in both limited and full Python APIs.
+struct typeobject_wrapper {
+ const char *tp_name;
+ Py_ssize_t tp_basicsize;
+ unsigned long tp_flags;
+
+ // When adding new slots below, also need to make sure we add ADD_TP_SLOT
+ // call in AddHeapType for it.
+
+ destructor tp_dealloc;
+ reprfunc tp_repr;
+
+ PySequenceMethods *tp_as_sequence;
+ PyMappingMethods *tp_as_mapping;
+
+ ternaryfunc tp_call;
+ getattrofunc tp_getattro;
+ setattrofunc tp_setattro;
+
+ const char *tp_doc;
+
+ traverseproc tp_traverse;
+
+ inquiry tp_clear;
+
+ getiterfunc tp_iter;
+ iternextfunc tp_iternext;
+
+ struct PyMethodDef *tp_methods;
+ struct _typeobject *tp_base;
+ allocfunc tp_alloc;
+ newfunc tp_new;
+ freefunc tp_free;
+};
+
+# define DEFINE_PY_TYPE_OBJECT(type) \
+ static struct typeobject_wrapper type; \
+ static PyTypeObject* type##Ptr = NULL
+
+// PyObject_HEAD_INIT_TYPE and PyObject_FINISH_INIT_TYPE need to come in pairs
+// We first initialize with NULL because the type is not allocated until
+// init_types() is called later. It's in FINISH_INIT_TYPE where we fill the
+// type in with the newly allocated type.
+# define PyObject_HEAD_INIT_TYPE(type) PyObject_HEAD_INIT(NULL)
+# define PyObject_FINISH_INIT_TYPE(obj, type) obj.ob_base.ob_type = type##Ptr
+
+# define Py_TYPE_GET_TP_ALLOC(type) ((allocfunc)PyType_GetSlot(type, Py_tp_alloc))
+# define Py_TYPE_GET_TP_METHODS(type) ((PyMethodDef *)PyType_GetSlot(type, Py_tp_methods))
+
+// PyObject_NEW is not part of stable ABI, but PyObject_Malloc/Init are.
+PyObject* Vim_PyObject_New(PyTypeObject *type, size_t objsize)
+{
+ PyObject *obj = (PyObject *)PyObject_Malloc(objsize);
+ if (obj == NULL)
+ return PyErr_NoMemory();
+ return PyObject_Init(obj, type);
+}
+# undef PyObject_NEW
+# define PyObject_NEW(type, typeobj) ((type *)Vim_PyObject_New(typeobj, sizeof(type)))
+
+// This is a somewhat convoluted because limited API doesn't expose an easy way
+// to get the tp_name field, and so we have to manually reconstruct it as
+// "__module__.__name__" (with __module__ omitted for builtins to emulate
+// Python behavior). Also, some of the more convenient functions like
+// PyUnicode_AsUTF8AndSize and PyType_GetQualName() are not available until
+// late Python 3 versions, and won't be available if you set Py_LIMITED_API too
+// low.
+# define PyErr_FORMAT_TYPE(msg, obj) \
+ do { \
+ PyObject* qualname = PyObject_GetAttrString((PyObject*)(obj)->ob_type, "__qualname__"); \
+ if (qualname == NULL) \
+ { \
+ PyErr_FORMAT(PyExc_TypeError, msg, "(NULL)"); \
+ break; \
+ } \
+ PyObject* module = PyObject_GetAttrString((PyObject*)(obj)->ob_type, "__module__"); \
+ PyObject* full; \
+ if (module == NULL || PyUnicode_CompareWithASCIIString(module, "builtins") == 0 \
+ || PyUnicode_CompareWithASCIIString(module, "__main__") == 0) \
+ { \
+ full = qualname; \
+ Py_INCREF(full); \
+ } \
+ else \
+ full = PyUnicode_FromFormat("%U.%U", module, qualname); \
+ PyObject* full_bytes = PyUnicode_AsUTF8String(full); \
+ const char* full_str = PyBytes_AsString(full_bytes); \
+ full_str = full_str == NULL ? "(NULL)" : full_str; \
+ PyErr_FORMAT(PyExc_TypeError, msg, full_str); \
+ Py_DECREF(qualname); \
+ Py_XDECREF(module); \
+ Py_XDECREF(full); \
+ Py_XDECREF(full_bytes); \
+ } while(0)
+
+# define PyList_GET_ITEM(list, i) PyList_GetItem(list, i)
+# define PyList_GET_SIZE(o) PyList_Size(o)
+# define PyTuple_GET_ITEM(o, pos) PyTuple_GetItem(o, pos)
+# define PyTuple_GET_SIZE(o) PyTuple_Size(o)
+
+// PyList_SET_ITEM and PyList_SetItem have slightly different behaviors. The
+// former will leave the old item dangling, and the latter will decref on it.
+// Since we only use this on new lists, this difference doesn't matter.
+# define PyList_SET_ITEM(list, i, item) PyList_SetItem(list, i, item)
+
+# if Py_LIMITED_API < 0x03080000
+// PyIter_check only became part of stable ABI in 3.8, and there is no easy way
+// to check for it in the API. We simply return false as a compromise. This
+// does mean we should avoid compiling with stable ABI < 3.8.
+# undef PyIter_Check
+# define PyIter_Check(obj) (FALSE)
+# endif
+
+PyTypeObject* AddHeapType(struct typeobject_wrapper* type_object)
+{
+ PyType_Spec type_spec;
+ type_spec.name = type_object->tp_name;
+ type_spec.basicsize = type_object->tp_basicsize;
+ type_spec.itemsize = 0;
+ type_spec.flags = type_object->tp_flags;
+
+ // We just need to statically allocate a large enough buffer that can hold
+ // all slots. We need to leave a null-terminated slot at the end.
+ PyType_Slot slots[40] = { {0, NULL} };
+ size_t slot_i = 0;
+
+# define ADD_TP_SLOT(slot_name) \
+ if (slot_i >= 40) return NULL; /* this should never happen */ \
+ if (type_object->slot_name != NULL) \
+ { \
+ slots[slot_i].slot = Py_##slot_name; \
+ slots[slot_i].pfunc = (void*)type_object->slot_name; \
+ ++slot_i; \
+ }
+# define ADD_TP_SUB_SLOT(sub_slot, slot_name) \
+ if (slot_i >= 40) return NULL; /* this should never happen */ \
+ if (type_object->sub_slot != NULL && type_object->sub_slot->slot_name != NULL) \
+ { \
+ slots[slot_i].slot = Py_##slot_name; \
+ slots[slot_i].pfunc = (void*)type_object->sub_slot->slot_name; \
+ ++slot_i; \
+ }
+
+ ADD_TP_SLOT(tp_dealloc)
+ ADD_TP_SLOT(tp_repr)
+ ADD_TP_SLOT(tp_call)
+ ADD_TP_SLOT(tp_getattro)
+ ADD_TP_SLOT(tp_setattro)
+ ADD_TP_SLOT(tp_doc)
+ ADD_TP_SLOT(tp_traverse)
+ ADD_TP_SLOT(tp_clear)
+ ADD_TP_SLOT(tp_iter)
+ ADD_TP_SLOT(tp_iternext)
+ ADD_TP_SLOT(tp_methods)
+ ADD_TP_SLOT(tp_base)
+ ADD_TP_SLOT(tp_alloc)
+ ADD_TP_SLOT(tp_new)
+ ADD_TP_SLOT(tp_free)
+
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_length)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_concat)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_repeat)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_item)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_ass_item)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_contains)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_inplace_concat)
+ ADD_TP_SUB_SLOT(tp_as_sequence, sq_inplace_repeat)
+
+ ADD_TP_SUB_SLOT(tp_as_mapping, mp_length)
+ ADD_TP_SUB_SLOT(tp_as_mapping, mp_subscript)
+ ADD_TP_SUB_SLOT(tp_as_mapping, mp_ass_subscript)
+# undef ADD_TP_SLOT
+# undef ADD_TP_SUB_SLOT
+
+ type_spec.slots = slots;
+
+ PyObject* newtype = PyType_FromSpec(&type_spec);
+ return (PyTypeObject*)newtype;
+}
+
+// Add a heap type, since static types do not work in limited API
+// Each PYTYPE_READY is paired with PYTYPE_CLEANUP.
+//
+// Note that we don't call Py_DECREF(type##Ptr) in clean up. The reason for
+// that in 3.7, it's possible to de-allocate a heap type before all instances
+// are cleared, leading to a crash, whereas in 3.8 the semantics were changed
+// and instances hold strong references to types. Since these types are
+// designed to be static, just keep them around to avoid having to write
+// version-specific handling. Vim does not re-start the Python runtime so there
+// will be no long-term leak.
+# define PYTYPE_READY(type) \
+ type##Ptr = AddHeapType(&(type)); \
+ if (type##Ptr == NULL) \
+ return -1;
+# define PYTYPE_CLEANUP(type) \
+ type##Ptr = NULL;
+
+// Limited API does not provide PyRun_* functions. Need to implement manually
+// using PyCompile and PyEval.
+PyObject* Vim_PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
+{
+ // Just pass "" for filename for now.
+ PyObject* compiled = Py_CompileString(str, "", start);
+ if (compiled == NULL)
+ return NULL;
+
+ PyObject* eval_result = PyEval_EvalCode(compiled, globals, locals);
+ Py_DECREF(compiled);
+ return eval_result;
+}
+int Vim_PyRun_SimpleString(const char *str)
+{
+ // This function emulates CPython's implementation.
+ PyObject* m = PyImport_AddModule("__main__");
+ if (m == NULL)
+ return -1;
+ PyObject* d = PyModule_GetDict(m);
+ PyObject* output = Vim_PyRun_String(str, Py_file_input, d, d);
+ if (output == NULL)
+ {
+ PyErr_PrintEx(TRUE);
+ return -1;
+ }
+ Py_DECREF(output);
+ return 0;
+}
+#define PyRun_String Vim_PyRun_String
+#define PyRun_SimpleString Vim_PyRun_SimpleString
+
+#else // !defined(USE_LIMITED_API)
+
+// Full Python API. Can make use of structs and macros directly.
+# define DEFINE_PY_TYPE_OBJECT(type) \
+ static PyTypeObject type; \
+ static PyTypeObject* type##Ptr = &type
+# define PyObject_HEAD_INIT_TYPE(type) PyObject_HEAD_INIT(&type)
+
+# define Py_TYPE_GET_TP_ALLOC(type) type->tp_alloc
+# define Py_TYPE_GET_TP_METHODS(type) type->tp_methods
+
+# define Py_TYPE_NAME(obj) ((obj)->ob_type->tp_name == NULL \
? "(NULL)" \
: (obj)->ob_type->tp_name)
+# define PyErr_FORMAT_TYPE(msg, obj) \
+ PyErr_FORMAT(PyExc_TypeError, msg, \
+ Py_TYPE_NAME(obj))
+
+// Add a static type
+# define PYTYPE_READY(type) \
+ if (PyType_Ready(type##Ptr)) \
+ return -1;
+
+#endif
+
#define RAISE_NO_EMPTY_KEYS PyErr_SET_STRING(PyExc_ValueError, \
N_("empty keys are not allowed"))
#define RAISE_KEY_ADD_FAIL(key) \
PyErr_VIM_FORMAT(N_("failed to add key '%s' to dictionary"), key)
#define RAISE_INVALID_INDEX_TYPE(idx) \
- PyErr_FORMAT(PyExc_TypeError, N_("index must be int or slice, not %s"), \
- Py_TYPE_NAME(idx));
+ PyErr_FORMAT_TYPE(N_("index must be int or slice, not %s"), idx);
#define INVALID_BUFFER_VALUE ((buf_T *)(-1))
#define INVALID_WINDOW_VALUE ((win_T *)(-1))
else
{
#if PY_MAJOR_VERSION < 3
- PyErr_FORMAT(PyExc_TypeError,
- N_("expected str() or unicode() instance, but got %s"),
- Py_TYPE_NAME(obj));
+ PyErr_FORMAT_TYPE(N_("expected str() or unicode() instance, but got %s"),
+ obj);
#else
- PyErr_FORMAT(PyExc_TypeError,
- N_("expected bytes() or str() instance, but got %s"),
- Py_TYPE_NAME(obj));
+ PyErr_FORMAT_TYPE(N_("expected bytes() or str() instance, but got %s"),
+ obj);
#endif
return NULL;
}
else
{
#if PY_MAJOR_VERSION < 3
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected int(), long() or something supporting "
"coercing to long(), but got %s"),
- Py_TYPE_NAME(obj));
+ obj);
#else
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected int() or something supporting coercing to int(), "
"but got %s"),
- Py_TYPE_NAME(obj));
+ obj);
#endif
return -1;
}
return NULL;
if (self)
- for (method = self->ob_type->tp_methods ; method->ml_name != NULL ; ++method)
+ for (method = Py_TYPE_GET_TP_METHODS(self->ob_type) ; method->ml_name != NULL ; ++method)
if (add_string(ret, (char *)method->ml_name))
{
Py_DECREF(ret);
// Function to write a line, points to either msg() or emsg().
typedef int (*writefn)(char *);
-static PyTypeObject OutputType;
+DEFINE_PY_TYPE_OBJECT(OutputType);
typedef struct
{
static OutputObject Output =
{
- PyObject_HEAD_INIT(&OutputType)
+ PyObject_HEAD_INIT_TYPE(OutputType)
0,
0
};
static OutputObject Error =
{
- PyObject_HEAD_INIT(&OutputType)
+ PyObject_HEAD_INIT_TYPE(OutputType)
0,
1
};
char *fullname;
PyObject *result;
} LoaderObject;
-static PyTypeObject LoaderType;
+DEFINE_PY_TYPE_OBJECT(LoaderType);
static void
LoaderDestructor(LoaderObject *self)
if (!PyTuple_Check(find_module_result))
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected 3-tuple as imp.find_module() result, but got %s"),
- Py_TYPE_NAME(find_module_result));
+ find_module_result);
return NULL;
}
if (PyTuple_GET_SIZE(find_module_result) != 3)
return NULL;
}
- if (!(loader = PyObject_NEW(LoaderObject, &LoaderType)))
+ if (!(loader = PyObject_NEW(LoaderObject, LoaderTypePtr)))
{
vim_free(fullname);
Py_DECREF(result);
* Generic iterator object
*/
-static PyTypeObject IterType;
+DEFINE_PY_TYPE_OBJECT(IterType);
typedef PyObject *(*nextfun)(void **);
typedef void (*destructorfun)(void *);
{
IterObject *self;
- self = PyObject_GC_New(IterObject, &IterType);
+ self = PyObject_GC_New(IterObject, IterTypePtr);
self->cur = start;
self->next = next;
self->destruct = destruct;
*last = ref;
}
-static PyTypeObject DictionaryType;
+DEFINE_PY_TYPE_OBJECT(DictionaryType);
typedef struct
{
static PyObject *DictionaryUpdate(DictionaryObject *, PyObject *, PyObject *);
-#define NEW_DICTIONARY(dict) DictionaryNew(&DictionaryType, dict)
+#define NEW_DICTIONARY(dict) DictionaryNew(DictionaryTypePtr, dict)
static PyObject *
DictionaryNew(PyTypeObject *subtype, dict_T *dict)
{
DictionaryObject *self;
- self = (DictionaryObject *) subtype->tp_alloc(subtype, 0);
+ self = (DictionaryObject *) Py_TYPE_GET_TP_ALLOC(subtype)(subtype, 0);
if (self == NULL)
return NULL;
self->dict = dict;
{ NULL, NULL, 0, NULL}
};
-static PyTypeObject ListType;
+DEFINE_PY_TYPE_OBJECT(ListType);
typedef struct
{
pylinkedlist_T ref;
} ListObject;
-#define NEW_LIST(list) ListNew(&ListType, list)
+#define NEW_LIST(list) ListNew(ListTypePtr, list)
static PyObject *
ListNew(PyTypeObject *subtype, list_T *list)
if (list == NULL)
return NULL;
- self = (ListObject *) subtype->tp_alloc(subtype, 0);
+ self = (ListObject *) Py_TYPE_GET_TP_ALLOC(subtype)(subtype, 0);
if (self == NULL)
return NULL;
self->list = list;
int auto_rebind;
} FunctionObject;
-static PyTypeObject FunctionType;
+DEFINE_PY_TYPE_OBJECT(FunctionType);
#define NEW_FUNCTION(name, argc, argv, self, pt_auto) \
- FunctionNew(&FunctionType, (name), (argc), (argv), (self), (pt_auto))
+ FunctionNew(FunctionTypePtr, (name), (argc), (argv), (self), (pt_auto))
static PyObject *
FunctionNew(PyTypeObject *subtype, char_u *name, int argc, typval_T *argv,
{
FunctionObject *self;
- self = (FunctionObject *)subtype->tp_alloc(subtype, 0);
+ self = (FunctionObject *) Py_TYPE_GET_TP_ALLOC(subtype)(subtype, 0);
if (self == NULL)
return NULL;
* Options object
*/
-static PyTypeObject OptionsType;
+DEFINE_PY_TYPE_OBJECT(OptionsType);
typedef int (*checkfun)(void *);
{
OptionsObject *self;
- self = PyObject_GC_New(OptionsObject, &OptionsType);
+ self = PyObject_GC_New(OptionsObject, OptionsTypePtr);
if (self == NULL)
return NULL;
static PyObject *WinListNew(TabPageObject *tabObject);
-static PyTypeObject TabPageType;
+DEFINE_PY_TYPE_OBJECT(TabPageType);
static int
CheckTabPage(TabPageObject *self)
}
else
{
- self = PyObject_NEW(TabPageObject, &TabPageType);
+ self = PyObject_NEW(TabPageObject, TabPageTypePtr);
if (self == NULL)
return NULL;
self->tab = tab;
* Window list object
*/
-static PyTypeObject TabListType;
+DEFINE_PY_TYPE_OBJECT(TabListType);
static PySequenceMethods TabListAsSeq;
typedef struct
PyObject_HEAD
} TabListObject;
+static TabListObject TheTabPageList =
+{
+ PyObject_HEAD_INIT_TYPE(TabListType)
+};
+
static PyInt
TabListLength(PyObject *self UNUSED)
{
TabPageObject *tabObject;
} WindowObject;
-static PyTypeObject WindowType;
+DEFINE_PY_TYPE_OBJECT(WindowType);
static int
CheckWindow(WindowObject *self)
}
else
{
- self = PyObject_GC_New(WindowObject, &WindowType);
+ self = PyObject_GC_New(WindowObject, WindowTypePtr);
if (self == NULL)
return NULL;
self->win = win;
* Window list object
*/
-static PyTypeObject WinListType;
+DEFINE_PY_TYPE_OBJECT(WinListType);
static PySequenceMethods WinListAsSeq;
typedef struct
TabPageObject *tabObject;
} WinListObject;
+static WinListObject TheWindowList =
+{
+ PyObject_HEAD_INIT_TYPE(WinListType)
+ NULL
+};
+
static PyObject *
WinListNew(TabPageObject *tabObject)
{
WinListObject *self;
- self = PyObject_NEW(WinListObject, &WinListType);
+ self = PyObject_NEW(WinListObject, WinListTypePtr);
self->tabObject = tabObject;
Py_INCREF(tabObject);
else
{
#if PY_MAJOR_VERSION < 3
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected str() or unicode() instance, but got %s"),
- Py_TYPE_NAME(obj));
+ obj);
#else
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected bytes() or str() instance, but got %s"),
- Py_TYPE_NAME(obj));
+ obj);
#endif
return NULL;
}
// Range object
-static PyTypeObject RangeType;
+DEFINE_PY_TYPE_OBJECT(RangeType);
static PySequenceMethods RangeAsSeq;
static PyMappingMethods RangeAsMapping;
{
BufferObject *bufr;
RangeObject *self;
- self = PyObject_GC_New(RangeObject, &RangeType);
+ self = PyObject_GC_New(RangeObject, RangeTypePtr);
if (self == NULL)
return NULL;
{ NULL, NULL, 0, NULL}
};
-static PyTypeObject BufferType;
+DEFINE_PY_TYPE_OBJECT(BufferType);
static PySequenceMethods BufferAsSeq;
static PyMappingMethods BufferAsMapping;
}
else
{
- self = PyObject_NEW(BufferObject, &BufferType);
+ self = PyObject_NEW(BufferObject, BufferTypePtr);
if (self == NULL)
return NULL;
self->buf = buf;
* Buffer list object - Implementation
*/
-static PyTypeObject BufMapType;
+DEFINE_PY_TYPE_OBJECT(BufMapType);
typedef struct
{
PyObject_HEAD
} BufMapObject;
+static BufMapObject TheBufferMap =
+{
+ PyObject_HEAD_INIT_TYPE(BufMapType)
+};
+
static PyInt
BufMapLength(PyObject *self UNUSED)
{
{
int count;
- if (valObject->ob_type != &BufferType)
+ if (valObject->ob_type != BufferTypePtr)
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected vim.Buffer object, but got %s"),
- Py_TYPE_NAME(valObject));
+ valObject);
return -1;
}
{
int count;
- if (valObject->ob_type != &WindowType)
+ if (valObject->ob_type != WindowTypePtr)
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected vim.Window object, but got %s"),
- Py_TYPE_NAME(valObject));
+ valObject);
return -1;
}
}
else if (strcmp(name, "tabpage") == 0)
{
- if (valObject->ob_type != &TabPageType)
+ if (valObject->ob_type != TabPageTypePtr)
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("expected vim.TabPage object, but got %s"),
- Py_TYPE_NAME(valObject));
+ valObject);
return -1;
}
if (!(lookup_dict = PyDict_New()))
return -1;
- if (PyType_IsSubtype(obj->ob_type, &DictionaryType))
+ if (PyType_IsSubtype(obj->ob_type, DictionaryTypePtr))
{
tv->v_type = VAR_DICT;
tv->vval.v_dict = (((DictionaryObject *)(obj))->dict);
ret = convert_dl(obj, tv, pymap_to_tv, lookup_dict);
else
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("unable to convert %s to a Vim dictionary"),
- Py_TYPE_NAME(obj));
+ obj);
ret = -1;
}
Py_DECREF(lookup_dict);
if (!(lookup_dict = PyDict_New()))
return -1;
- if (PyType_IsSubtype(obj->ob_type, &ListType))
+ if (PyType_IsSubtype(obj->ob_type, ListTypePtr))
{
tv->v_type = VAR_LIST;
tv->vval.v_list = (((ListObject *)(obj))->list);
ret = convert_dl(obj, tv, pyseq_to_tv, lookup_dict);
else
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("unable to convert %s to a Vim list"),
- Py_TYPE_NAME(obj));
+ obj);
ret = -1;
}
Py_DECREF(lookup_dict);
static int
_ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict)
{
- if (PyType_IsSubtype(obj->ob_type, &DictionaryType))
+ if (PyType_IsSubtype(obj->ob_type, DictionaryTypePtr))
{
tv->v_type = VAR_DICT;
tv->vval.v_dict = (((DictionaryObject *)(obj))->dict);
++tv->vval.v_dict->dv_refcount;
}
- else if (PyType_IsSubtype(obj->ob_type, &ListType))
+ else if (PyType_IsSubtype(obj->ob_type, ListTypePtr))
{
tv->v_type = VAR_LIST;
tv->vval.v_list = (((ListObject *)(obj))->list);
++tv->vval.v_list->lv_refcount;
}
- else if (PyType_IsSubtype(obj->ob_type, &FunctionType))
+ else if (PyType_IsSubtype(obj->ob_type, FunctionTypePtr))
{
FunctionObject *func = (FunctionObject *) obj;
if (func->self != NULL || func->argv != NULL)
}
else
{
- PyErr_FORMAT(PyExc_TypeError,
+ PyErr_FORMAT_TYPE(
N_("unable to convert %s to a Vim structure"),
- Py_TYPE_NAME(obj));
+ obj);
return -1;
}
return 0;
return NULL;
}
+DEFINE_PY_TYPE_OBJECT(CurrentType);
+
typedef struct
{
PyObject_HEAD
} CurrentObject;
-static PyTypeObject CurrentType;
+
+static CurrentObject TheCurrent =
+{
+ PyObject_HEAD_INIT_TYPE(CurrentType)
+};
static void
init_structs(void)
OutputType.tp_alloc = call_PyType_GenericAlloc;
OutputType.tp_new = call_PyType_GenericNew;
OutputType.tp_free = call_PyObject_Free;
+# ifndef USE_LIMITED_API
+ // The std printer type is only exposed in full API. It is not essential
+ // anyway and so in limited API we don't set it.
OutputType.tp_base = &PyStdPrinter_Type;
+# endif
#else
OutputType.tp_getattr = (getattrfunc)OutputGetattr;
OutputType.tp_setattr = (setattrfunc)OutputSetattr;
CLEAR_FIELD(BufferType);
BufferType.tp_name = "vim.buffer";
- BufferType.tp_basicsize = sizeof(BufferType);
+ BufferType.tp_basicsize = sizeof(BufferObject);
BufferType.tp_dealloc = (destructor)BufferDestructor;
BufferType.tp_repr = (reprfunc)BufferRepr;
BufferType.tp_as_sequence = &BufferAsSeq;
BufMapType.tp_as_mapping = &BufMapAsMapping;
BufMapType.tp_flags = Py_TPFLAGS_DEFAULT;
BufMapType.tp_iter = BufMapIter;
- BufferType.tp_doc = "vim buffer list";
+ BufMapType.tp_doc = "vim buffer list";
CLEAR_FIELD(WinListType);
WinListType.tp_name = "vim.windowlist";
- WinListType.tp_basicsize = sizeof(WinListType);
+ WinListType.tp_basicsize = sizeof(WinListObject);
WinListType.tp_as_sequence = &WinListAsSeq;
WinListType.tp_flags = Py_TPFLAGS_DEFAULT;
WinListType.tp_doc = "vim window list";
CLEAR_FIELD(TabListType);
TabListType.tp_name = "vim.tabpagelist";
- TabListType.tp_basicsize = sizeof(TabListType);
+ TabListType.tp_basicsize = sizeof(TabListObject);
TabListType.tp_as_sequence = &TabListAsSeq;
TabListType.tp_flags = Py_TPFLAGS_DEFAULT;
TabListType.tp_doc = "vim tab page list";
#endif
}
-#define PYTYPE_READY(type) \
- if (PyType_Ready(&(type))) \
- return -1;
-
static int
init_types(void)
{
#if PY_VERSION_HEX < 0x030700f0
PYTYPE_READY(LoaderType);
#endif
+
+#ifdef USE_LIMITED_API
+ // We need to finish initializing all the static objects because the types
+ // are only just allocated on the heap now.
+ // Each PyObject_HEAD_INIT_TYPE should correspond to a
+ // PyObject_FINISH_INIT_TYPE below.
+ PyObject_FINISH_INIT_TYPE(Output, OutputType);
+ PyObject_FINISH_INIT_TYPE(Error, OutputType);
+ PyObject_FINISH_INIT_TYPE(TheBufferMap, BufMapType);
+ PyObject_FINISH_INIT_TYPE(TheWindowList, WinListType);
+ PyObject_FINISH_INIT_TYPE(TheCurrent, CurrentType);
+ PyObject_FINISH_INIT_TYPE(TheTabPageList, TabListType);
+#endif
return 0;
}
+#ifdef USE_LIMITED_API
+ static void
+shutdown_types(void)
+{
+ PYTYPE_CLEANUP(IterType);
+ PYTYPE_CLEANUP(BufferType);
+ PYTYPE_CLEANUP(RangeType);
+ PYTYPE_CLEANUP(WindowType);
+ PYTYPE_CLEANUP(TabPageType);
+ PYTYPE_CLEANUP(BufMapType);
+ PYTYPE_CLEANUP(WinListType);
+ PYTYPE_CLEANUP(TabListType);
+ PYTYPE_CLEANUP(CurrentType);
+ PYTYPE_CLEANUP(DictionaryType);
+ PYTYPE_CLEANUP(ListType);
+ PYTYPE_CLEANUP(FunctionType);
+ PYTYPE_CLEANUP(OptionsType);
+ PYTYPE_CLEANUP(OutputType);
+# if PY_VERSION_HEX < 0x030700f0
+ PYTYPE_CLEANUP(LoaderType);
+# endif
+}
+#endif
+
static int
init_sys_path(void)
{
return 0;
}
-static BufMapObject TheBufferMap =
-{
- PyObject_HEAD_INIT(&BufMapType)
-};
-
-static WinListObject TheWindowList =
-{
- PyObject_HEAD_INIT(&WinListType)
- NULL
-};
-
-static CurrentObject TheCurrent =
-{
- PyObject_HEAD_INIT(&CurrentType)
-};
-
-static TabListObject TheTabPageList =
-{
- PyObject_HEAD_INIT(&TabListType)
-};
-
static struct numeric_constant {
char *name;
int val;
{"VAR_DEF_SCOPE", VAR_DEF_SCOPE},
};
-static struct object_constant {
+struct object_constant {
char *name;
PyObject *valObject;
-} object_constants[] = {
- {"buffers", (PyObject *)(void *)&TheBufferMap},
- {"windows", (PyObject *)(void *)&TheWindowList},
- {"tabpages", (PyObject *)(void *)&TheTabPageList},
- {"current", (PyObject *)(void *)&TheCurrent},
-
- {"Buffer", (PyObject *)&BufferType},
- {"Range", (PyObject *)&RangeType},
- {"Window", (PyObject *)&WindowType},
- {"TabPage", (PyObject *)&TabPageType},
- {"Dictionary", (PyObject *)&DictionaryType},
- {"List", (PyObject *)&ListType},
- {"Function", (PyObject *)&FunctionType},
- {"Options", (PyObject *)&OptionsType},
-#if PY_VERSION_HEX < 0x030700f0
- {"_Loader", (PyObject *)&LoaderType},
-#endif
};
#define ADD_OBJECT(m, name, obj) \
ADD_CHECKED_OBJECT(m, numeric_constants[i].name,
PyInt_FromLong(numeric_constants[i].val));
+ struct object_constant object_constants[] = {
+ {"buffers", (PyObject *)(void *)&TheBufferMap},
+ {"windows", (PyObject *)(void *)&TheWindowList},
+ {"tabpages", (PyObject *)(void *)&TheTabPageList},
+ {"current", (PyObject *)(void *)&TheCurrent},
+
+ {"Buffer", (PyObject *)BufferTypePtr},
+ {"Range", (PyObject *)RangeTypePtr},
+ {"Window", (PyObject *)WindowTypePtr},
+ {"TabPage", (PyObject *)TabPageTypePtr},
+ {"Dictionary", (PyObject *)DictionaryTypePtr},
+ {"List", (PyObject *)ListTypePtr},
+ {"Function", (PyObject *)FunctionTypePtr},
+ {"Options", (PyObject *)OptionsTypePtr},
+#if PY_VERSION_HEX < 0x030700f0
+ {"_Loader", (PyObject *)LoaderTypePtr},
+#endif
+ };
+
for (i = 0; i < (int)(sizeof(object_constants)
/ sizeof(struct object_constant));
++i)
#define PyLong_Type (*py3_PyLong_Type)
#define PyBool_Type (*py3_PyBool_Type)
+#ifdef Py_LIMITED_API
+# define USE_LIMITED_API // Using Python 3 limited ABI
+#endif
+
#include <Python.h>
#undef main // Defined in python.h - aargh
# ifndef PyMapping_Keys
# define PyMapping_Keys py3_PyMapping_Keys
# endif
-# if PY_VERSION_HEX >= 0x030a00b2
+# if (defined(USE_LIMITED_API) && Py_LIMITED_API >= 0x03080000) || \
+ (!defined(USE_LIMITED_API) && PY_VERSION_HEX >= 0x03080000)
+# undef PyIter_Check
# define PyIter_Check py3_PyIter_Check
# endif
# define PyIter_Next py3_PyIter_Next
# define PyObject_GetItem py3_PyObject_GetItem
# define PyObject_IsTrue py3_PyObject_IsTrue
# define PyModule_GetDict py3_PyModule_GetDict
-#undef PyRun_SimpleString
-# define PyRun_SimpleString py3_PyRun_SimpleString
-#undef PyRun_String
-# define PyRun_String py3_PyRun_String
+# ifndef USE_LIMITED_API
+# undef PyRun_SimpleString
+# define PyRun_SimpleString py3_PyRun_SimpleString
+# undef PyRun_String
+# define PyRun_String py3_PyRun_String
+# else
+# define Py_CompileString py3_Py_CompileString
+# define PyEval_EvalCode py3_PyEval_EvalCode
+# endif
# define PyObject_GetAttrString py3_PyObject_GetAttrString
# define PyObject_HasAttrString py3_PyObject_HasAttrString
# define PyObject_SetAttrString py3_PyObject_SetAttrString
# define PySys_GetObject py3_PySys_GetObject
# define PySys_SetArgv py3_PySys_SetArgv
# define PyType_Ready py3_PyType_Ready
-# if PY_VERSION_HEX >= 0x030900b0
+# if PY_VERSION_HEX >= 0x03040000
# define PyType_GetFlags py3_PyType_GetFlags
# endif
#undef Py_BuildValue
# define PyModule_AddObject py3_PyModule_AddObject
# define PyImport_AppendInittab py3_PyImport_AppendInittab
# define PyImport_AddModule py3_PyImport_AddModule
-# if PY_VERSION_HEX >= 0x030300f0
-# undef _PyUnicode_AsString
-# define _PyUnicode_AsString py3_PyUnicode_AsUTF8
+# ifdef USE_LIMITED_API
+# if Py_LIMITED_API >= 0x030a0000
+# define PyUnicode_AsUTF8AndSize py3_PyUnicode_AsUTF8AndSize
+# endif
# else
-# define _PyUnicode_AsString py3__PyUnicode_AsString
+# if PY_VERSION_HEX >= 0x030300f0
+# define PyUnicode_AsUTF8AndSize py3_PyUnicode_AsUTF8AndSize
+# else
+# define _PyUnicode_AsString py3__PyUnicode_AsString
+# endif
# endif
+# undef PyUnicode_CompareWithASCIIString
+# define PyUnicode_CompareWithASCIIString py3_PyUnicode_CompareWithASCIIString
# undef PyUnicode_AsEncodedString
# define PyUnicode_AsEncodedString py3_PyUnicode_AsEncodedString
+# undef PyUnicode_AsUTF8String
+# define PyUnicode_AsUTF8String py3_PyUnicode_AsUTF8String
# undef PyBytes_AsString
# define PyBytes_AsString py3_PyBytes_AsString
# ifndef PyBytes_AsStringAndSize
# define PyBytes_FromString py3_PyBytes_FromString
# undef PyBytes_FromStringAndSize
# define PyBytes_FromStringAndSize py3_PyBytes_FromStringAndSize
-# if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0
+# if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 || defined(USE_LIMITED_API)
# define _Py_Dealloc py3__Py_Dealloc
# endif
# define PyFloat_FromDouble py3_PyFloat_FromDouble
# define PyType_IsSubtype py3_PyType_IsSubtype
# define PyCapsule_New py3_PyCapsule_New
# define PyCapsule_GetPointer py3_PyCapsule_GetPointer
+# ifdef USE_LIMITED_API
+# define PyType_GetSlot py3_PyType_GetSlot
+# define PyType_FromSpec py3_PyType_FromSpec
+# endif
# if defined(Py_DEBUG) && !defined(Py_DEBUG_NO_PYMALLOC)
# undef PyObject_NEW
static void (*py3_PyErr_SetString)(PyObject *, const char *);
static void (*py3_PyErr_SetObject)(PyObject *, PyObject *);
static int (*py3_PyErr_ExceptionMatches)(PyObject *);
+# ifndef USE_LIMITED_API
static int (*py3_PyRun_SimpleString)(char *);
static PyObject* (*py3_PyRun_String)(char *, int, PyObject *, PyObject *);
+# else
+static PyObject* (*py3_Py_CompileString)(const char *, const char *, int);
+static PyObject* (*py3_PyEval_EvalCode)(PyObject *co, PyObject *globals, PyObject *locals);
+# endif
static PyObject* (*py3_PyObject_GetAttrString)(PyObject *, const char *);
static int (*py3_PyObject_HasAttrString)(PyObject *, const char *);
static int (*py3_PyObject_SetAttrString)(PyObject *, const char *, PyObject *);
static int (*py3_PyDict_Next)(PyObject *, Py_ssize_t *, PyObject **, PyObject **);
static PyObject* (*py3_PyLong_FromLong)(long);
static PyObject* (*py3_PyDict_New)(void);
-# if PY_VERSION_HEX >= 0x030a00b2
+# if (defined(USE_LIMITED_API) && Py_LIMITED_API >= 0x03080000) || \
+ (!defined(USE_LIMITED_API) && PY_VERSION_HEX >= 0x03080000)
static int (*py3_PyIter_Check)(PyObject *o);
# endif
static PyObject* (*py3_PyIter_Next)(PyObject *);
static PyObject* (*py3_PyObject_GetItem)(PyObject *, PyObject *);
static int (*py3_PyObject_IsTrue)(PyObject *);
static PyObject* (*py3_Py_BuildValue)(char *, ...);
-# if PY_VERSION_HEX >= 0x030900b0
+# if PY_VERSION_HEX >= 0x03040000
static int (*py3_PyType_GetFlags)(PyTypeObject *o);
# endif
static int (*py3_PyType_Ready)(PyTypeObject *type);
static PyObject* py3__Py_TrueStruct;
static int (*py3_PyModule_AddObject)(PyObject *m, const char *name, PyObject *o);
static int (*py3_PyImport_AppendInittab)(const char *name, PyObject* (*initfunc)(void));
-# if PY_VERSION_HEX >= 0x030300f0
-static char* (*py3_PyUnicode_AsUTF8)(PyObject *unicode);
+# ifdef USE_LIMITED_API
+# if Py_LIMITED_API >= 0x030a0000
+static char* (*py3_PyUnicode_AsUTF8AndSize)(PyObject *unicode, Py_ssize_t *size);
+# endif
# else
+# if PY_VERSION_HEX >= 0x030300f0
+static char* (*py3_PyUnicode_AsUTF8AndSize)(PyObject *unicode, Py_ssize_t *size);
+# else
static char* (*py3__PyUnicode_AsString)(PyObject *unicode);
+# endif
# endif
+static int (*py3_PyUnicode_CompareWithASCIIString)(PyObject *unicode, const char* string);
static PyObject* (*py3_PyUnicode_AsEncodedString)(PyObject *unicode, const char* encoding, const char* errors);
+static PyObject* (*py3_PyUnicode_AsUTF8String)(PyObject *unicode);
static char* (*py3_PyBytes_AsString)(PyObject *bytes);
static int (*py3_PyBytes_AsStringAndSize)(PyObject *bytes, char **buffer, Py_ssize_t *length);
static PyObject* (*py3_PyBytes_FromString)(char *str);
static PyObject* (*py3_PyBytes_FromStringAndSize)(char *str, Py_ssize_t length);
-# if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0
+# if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 || defined(USE_LIMITED_API)
static void (*py3__Py_Dealloc)(PyObject *obj);
# endif
# if PY_VERSION_HEX >= 0x030900b0
static void(*py3_PyObject_GC_Del)(void *);
static void(*py3_PyObject_GC_UnTrack)(void *);
static int (*py3_PyType_IsSubtype)(PyTypeObject *, PyTypeObject *);
+# ifdef USE_LIMITED_API
+static void* (*py3_PyType_GetSlot)(PyTypeObject *, int);
+static PyObject* (*py3_PyType_FromSpec)(PyType_Spec *);
+# endif
// Imported exception objects
static PyObject *p3imp_PyExc_AttributeError;
{"PyErr_SetString", (PYTHON_PROC*)&py3_PyErr_SetString},
{"PyErr_SetObject", (PYTHON_PROC*)&py3_PyErr_SetObject},
{"PyErr_ExceptionMatches", (PYTHON_PROC*)&py3_PyErr_ExceptionMatches},
+# ifndef USE_LIMITED_API
{"PyRun_SimpleString", (PYTHON_PROC*)&py3_PyRun_SimpleString},
{"PyRun_String", (PYTHON_PROC*)&py3_PyRun_String},
+# else
+ {"Py_CompileString", (PYTHON_PROC*)&py3_Py_CompileString},
+ {"PyEval_EvalCode", (PYTHON_PROC*)&PyEval_EvalCode},
+# endif
{"PyObject_GetAttrString", (PYTHON_PROC*)&py3_PyObject_GetAttrString},
{"PyObject_HasAttrString", (PYTHON_PROC*)&py3_PyObject_HasAttrString},
{"PyObject_SetAttrString", (PYTHON_PROC*)&py3_PyObject_SetAttrString},
{"PyDict_Next", (PYTHON_PROC*)&py3_PyDict_Next},
{"PyMapping_Check", (PYTHON_PROC*)&py3_PyMapping_Check},
{"PyMapping_Keys", (PYTHON_PROC*)&py3_PyMapping_Keys},
-# if PY_VERSION_HEX >= 0x030a00b2
+# if (defined(USE_LIMITED_API) && Py_LIMITED_API >= 0x03080000) || \
+ (!defined(USE_LIMITED_API) && PY_VERSION_HEX >= 0x03080000)
{"PyIter_Check", (PYTHON_PROC*)&py3_PyIter_Check},
# endif
{"PyIter_Next", (PYTHON_PROC*)&py3_PyIter_Next},
{"PyObject_IsTrue", (PYTHON_PROC*)&py3_PyObject_IsTrue},
{"PyLong_FromLong", (PYTHON_PROC*)&py3_PyLong_FromLong},
{"PyDict_New", (PYTHON_PROC*)&py3_PyDict_New},
-# if PY_VERSION_HEX >= 0x030900b0
+# if PY_VERSION_HEX >= 0x03040000
{"PyType_GetFlags", (PYTHON_PROC*)&py3_PyType_GetFlags},
# endif
{"PyType_Ready", (PYTHON_PROC*)&py3_PyType_Ready},
{"PyObject_Init", (PYTHON_PROC*)&py3__PyObject_Init},
{"PyModule_AddObject", (PYTHON_PROC*)&py3_PyModule_AddObject},
{"PyImport_AppendInittab", (PYTHON_PROC*)&py3_PyImport_AppendInittab},
-# if PY_VERSION_HEX >= 0x030300f0
- {"PyUnicode_AsUTF8", (PYTHON_PROC*)&py3_PyUnicode_AsUTF8},
+# ifdef USE_LIMITED_API
+# if Py_LIMITED_API >= 0x030a0000
+ {"PyUnicode_AsUTF8AndSize", (PYTHON_PROC*)&py3_PyUnicode_AsUTF8AndSize},
+# endif
# else
+# if PY_VERSION_HEX >= 0x030300f0
+ {"PyUnicode_AsUTF8AndSize", (PYTHON_PROC*)&py3_PyUnicode_AsUTF8AndSize},
+# else
{"_PyUnicode_AsString", (PYTHON_PROC*)&py3__PyUnicode_AsString},
+# endif
# endif
+ {"PyUnicode_CompareWithASCIIString", (PYTHON_PROC*)&py3_PyUnicode_CompareWithASCIIString},
+ {"PyUnicode_AsUTF8String", (PYTHON_PROC*)&py3_PyUnicode_AsUTF8String},
# ifndef Py_UNICODE_USE_UCS_FUNCTIONS
{"PyUnicode_FromFormat", (PYTHON_PROC*)&py3_PyUnicode_FromFormat},
# else
{"PyBytes_AsStringAndSize", (PYTHON_PROC*)&py3_PyBytes_AsStringAndSize},
{"PyBytes_FromString", (PYTHON_PROC*)&py3_PyBytes_FromString},
{"PyBytes_FromStringAndSize", (PYTHON_PROC*)&py3_PyBytes_FromStringAndSize},
-# if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0
+# if defined(Py_DEBUG) || PY_VERSION_HEX >= 0x030900b0 || defined(USE_LIMITED_API)
{"_Py_Dealloc", (PYTHON_PROC*)&py3__Py_Dealloc},
# endif
# if PY_VERSION_HEX >= 0x030900b0
{"PyType_IsSubtype", (PYTHON_PROC*)&py3_PyType_IsSubtype},
{"PyCapsule_New", (PYTHON_PROC*)&py3_PyCapsule_New},
{"PyCapsule_GetPointer", (PYTHON_PROC*)&py3_PyCapsule_GetPointer},
+# ifdef USE_LIMITED_API
+# if PY_VERSION_HEX >= 0x03040000
+ {"PyType_GetSlot", (PYTHON_PROC*)&py3_PyType_GetSlot},
+# endif
+ {"PyType_FromSpec", (PYTHON_PROC*)&py3_PyType_FromSpec},
+# endif
{"", NULL},
};
#define PYINITIALISED py3initialised
static int python_end_called = FALSE;
-#define DESTRUCTOR_FINISH(self) Py_TYPE(self)->tp_free((PyObject*)self)
+#ifdef USE_LIMITED_API
+# define DESTRUCTOR_FINISH(self) \
+ ((freefunc)PyType_GetSlot(Py_TYPE(self), Py_tp_free))((PyObject*)self)
+#else
+# define DESTRUCTOR_FINISH(self) Py_TYPE(self)->tp_free((PyObject*)self)
+#endif
#define WIN_PYTHON_REF(win) win->w_python3_ref
#define BUF_PYTHON_REF(buf) buf->b_python3_ref
*/
#include "if_py_both.h"
+#ifndef USE_LIMITED_API
+# if PY_VERSION_HEX >= 0x030300f0
+# define PY_UNICODE_GET_UTF8_CHARS(obj) PyUnicode_AsUTF8AndSize(obj, NULL)
+# else
+# define PY_UNICODE_GET_UTF8_CHARS _PyUnicode_AsString
+# endif
+#else
+# if Py_LIMITED_API >= 0x030A0000
+# define PY_UNICODE_GET_UTF8_CHARS(obj) PyUnicode_AsUTF8AndSize(obj, NULL)
+# else
+// Python limited API before 3.10 lack easy ways to query the raw UTF-8 chars.
+// We need to first convert the string to bytes, and then extract the chars.
+// This function is only used for attribute string comparisons, which have
+// known short length. As such, just allocate a short static buffer to hold
+// the characters instead of having to allocate/deallcoate it.
+//
+// An alternative would be to convert all attribute string comparisons to use
+// PyUnicode_CompareWithASCIIString to skip having to extract the chars.
+static char py3_unicode_utf8_chars[20];
+char* PY_UNICODE_GET_UTF8_CHARS(PyObject* str)
+{
+ py3_unicode_utf8_chars[0] = '\0';
+ PyObject* bytes = PyUnicode_AsUTF8String(str);
+ if (bytes)
+ {
+ char *chars;
+ Py_ssize_t len;
+ if (PyBytes_AsStringAndSize(bytes, &chars, &len) != -1)
+ {
+ if (len < (Py_ssize_t)sizeof(py3_unicode_utf8_chars))
+ // PyBytes_AsStringAndSize guarantees null-termination
+ memcpy(py3_unicode_utf8_chars, chars, len + 1);
+ }
+ Py_DECREF(bytes);
+ }
+ return py3_unicode_utf8_chars;
+}
+# endif
+#endif
+
// NOTE: Must always be used at the start of a block, since it declares "name".
#define GET_ATTR_STRING(name, nameobj) \
char *name = ""; \
if (PyUnicode_Check(nameobj)) \
- name = (char *)_PyUnicode_AsString(nameobj)
+ name = (char *)PY_UNICODE_GET_UTF8_CHARS(nameobj)
#define PY3OBJ_DELETED(obj) (obj->ob_base.ob_refcnt<=0)
#endif
if (Py_IsInitialized())
{
+#ifdef USE_LIMITED_API
+ shutdown_types();
+#endif
+
// acquire lock before finalizing
PyGILState_Ensure();
set_ref_in_python3(int copyID)
{
return set_ref_in_py(copyID);
+}
+
+ int
+python3_version()
+{
+#ifdef USE_LIMITED_API
+ return Py_LIMITED_API;
+#else
+ return PY_VERSION_HEX;
+#endif
}
void python3_tabpage_free(tabpage_T *tab);
void do_py3eval(char_u *str, typval_T *rettv);
int set_ref_in_python3(int copyID);
+int python3_version();
/* vim: set ft=c : */
#endif
#ifdef FEAT_PYTHON3
# ifdef DYNAMIC_PYTHON3
+# ifdef DYNAMIC_PYTHON3_STABLE_ABI
+ "+python3/dyn-stable",
+# else
"+python3/dyn",
+# endif
# else
"+python3",
# endif
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1776,
/**/
1775,
/**/
#define VV_SIZEOFLONG 103
#define VV_SIZEOFPOINTER 104
#define VV_MAXCOL 105
-#define VV_LEN 106 // number of v: vars
+#define VV_PYTHON3_VERSION 106
+#define VV_LEN 107 // number of v: vars
// used for v_number in VAR_BOOL and VAR_SPECIAL
#define VVAL_FALSE 0L // VAR_BOOL