diff --git a/bindings/python/tree_sitter_ruby/binding.c b/bindings/python/tree_sitter_ruby/binding.c index 6a02dfd1..2d01d92d 100644 --- a/bindings/python/tree_sitter_ruby/binding.c +++ b/bindings/python/tree_sitter_ruby/binding.c @@ -8,6 +8,13 @@ static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSE return PyCapsule_New(tree_sitter_ruby(), "tree_sitter.Language", NULL); } +static struct PyModuleDef_Slot slots[] = { +#ifdef Py_GIL_DISABLED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif + {0, NULL} +}; + static PyMethodDef methods[] = { {"language", _binding_language, METH_NOARGS, "Get the tree-sitter language for this grammar."}, @@ -18,10 +25,11 @@ static struct PyModuleDef module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_binding", .m_doc = NULL, - .m_size = -1, - .m_methods = methods + .m_size = 0, + .m_methods = methods, + .m_slots = slots, }; PyMODINIT_FUNC PyInit__binding(void) { - return PyModule_Create(&module); + return PyModuleDef_Init(&module); } diff --git a/setup.py b/setup.py index 2c0384fa..ef6fcde6 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,31 @@ from os.path import isdir, join from platform import system +from sysconfig import get_config_var from setuptools import Extension, find_packages, setup from setuptools.command.build import build from wheel.bdist_wheel import bdist_wheel +# True when the interpreter running setup.py is a free-threaded build. Such a +# build cannot use the stable ABI, because the module has to declare the +# Py_mod_gil slot and that slot is not part of the limited API. +FREE_THREADED = bool(get_config_var("Py_GIL_DISABLED")) + +# CPython defines Py_GIL_DISABLED in pyconfig.h on POSIX free-threaded builds, +# but not on Windows, where it has to be passed to the compiler instead. Pass it +# unconditionally on free-threaded builds so binding.c sees the same macro +# everywhere; on POSIX this repeats the existing definition with the same value, +# which is a legal redefinition. +DEFINE_MACROS = [ + ("PY_SSIZE_T_CLEAN", None), + ("TREE_SITTER_HIDE_SYMBOLS", None), +] +DEFINE_MACROS += ( + [("Py_GIL_DISABLED", "1")] if FREE_THREADED else [("Py_LIMITED_API", "0x03090000")] +) + + class Build(build): def run(self): if isdir("queries"): @@ -17,7 +37,7 @@ def run(self): class BdistWheel(bdist_wheel): def get_tag(self): python, abi, platform = super().get_tag() - if python.startswith("cp"): + if python.startswith("cp") and not FREE_THREADED: python, abi = "cp39", "abi3" return python, abi, platform @@ -45,13 +65,9 @@ def get_tag(self): "/std:c11", "/utf-8", ], - define_macros=[ - ("Py_LIMITED_API", "0x03090000"), - ("PY_SSIZE_T_CLEAN", None), - ("TREE_SITTER_HIDE_SYMBOLS", None), - ], + define_macros=DEFINE_MACROS, include_dirs=["src"], - py_limited_api=True, + py_limited_api=not FREE_THREADED, ) ], cmdclass={