diff --git a/cli/src/init.rs b/cli/src/init.rs index f2a8558b..73a355ce 100644 --- a/cli/src/init.rs +++ b/cli/src/init.rs @@ -445,9 +445,44 @@ pub fn generate_grammar_files( let lang_path = path.join(format!("tree_sitter_{}", language_name.to_snake_case())); missing_path(&lang_path, create_dir)?; - missing_path(lang_path.join("binding.c"), |path| { - generate_file(path, PY_BINDING_C_TEMPLATE, language_name, &generate_opts) - })?; + missing_path_else( + lang_path.join("binding.c"), + allow_update, + |path| generate_file(path, PY_BINDING_C_TEMPLATE, language_name, &generate_opts), + |path| { + let mut contents = fs::read_to_string(path)?; + if !contents.contains("PyModuleDef_Init") { + contents = contents + .replace("PyModule_Create", "PyModuleDef_Init") + .replace( + "static PyMethodDef methods[] = {\n", + indoc! {" + static struct PyModuleDef_Slot slots[] = { + #ifdef Py_GIL_DISABLED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + #endif + {0, NULL} + }; + + static PyMethodDef methods[] = { + "}, + ) + .replace( + indoc! {" + .m_size = -1, + .m_methods = methods + "}, + indoc! {" + .m_size = 0, + .m_methods = methods, + .m_slots = slots, + "}, + ); + write_file(path, contents)?; + } + Ok(()) + }, + )?; missing_path(lang_path.join("__init__.py"), |path| { generate_file(path, INIT_PY_TEMPLATE, language_name, &generate_opts) @@ -478,51 +513,27 @@ pub fn generate_grammar_files( allow_update, |path| generate_file(path, SETUP_PY_TEMPLATE, language_name, &generate_opts), |path| { - let mut contents = fs::read_to_string(path)?; - if contents.contains("sources.extend") || !contents.contains("egg_info") { - contents = contents - .replace("sources.extend", "sources.append") - .replace( - "from setuptools.command.build import build\n", - indoc! {" - from setuptools.command.build import build - from setuptools.command.egg_info import egg_info - "}, - ) - .replace( - "setup(\n", - indoc! {r#" - class EggInfo(egg_info): - def find_sources(self): - super().find_sources() - self.filelist.recursive_include("queries", "*.scm") - self.filelist.include("src/tree_sitter/*.h") - - - setup( - "#}, - ) - .replace( - "\"bdist_wheel\": BdistWheel\n", - indoc! {r#" - "bdist_wheel": BdistWheel, - "egg_info": EggInfo, - "#}, - ); - write_file(path, contents)?; + let contents = fs::read_to_string(path)?; + if !contents.contains("egg_info") || !contents.contains("Py_GIL_DISABLED") { + eprintln!("Replacing setup.py"); + generate_file(path, SETUP_PY_TEMPLATE, language_name, &generate_opts)?; } Ok(()) }, )?; - missing_path_else(repo_path.join("pyproject.toml"), allow_update, |path| { - generate_file( - path, - PYPROJECT_TOML_TEMPLATE, - dashed_language_name.as_str(), - &generate_opts, - ) - }, |path| { + missing_path_else( + repo_path.join("pyproject.toml"), + allow_update, + |path| { + generate_file( + path, + PYPROJECT_TOML_TEMPLATE, + dashed_language_name.as_str(), + &generate_opts, + ) + }, + |path| { let mut contents = fs::read_to_string(path)?; if !contents.contains("cp310-*") { contents = contents @@ -532,7 +543,8 @@ pub fn generate_grammar_files( write_file(path, contents)?; } Ok(()) - })?; + }, + )?; Ok(()) })?; diff --git a/cli/src/templates/py-binding.c b/cli/src/templates/py-binding.c index 74309fa8..c74db112 100644 --- a/cli/src/templates/py-binding.c +++ b/cli/src/templates/py-binding.c @@ -8,6 +8,13 @@ static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSE return PyCapsule_New(tree_sitter_LOWER_PARSER_NAME(), "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/cli/src/templates/setup.py b/cli/src/templates/setup.py index 0daba087..534bd9f2 100644 --- a/cli/src/templates/setup.py +++ b/cli/src/templates/setup.py @@ -1,5 +1,6 @@ from os import path from platform import system +from sysconfig import get_config_var from setuptools import Extension, find_packages, setup from setuptools.command.build import build @@ -13,6 +14,13 @@ sources = [ if path.exists("src/scanner.c"): sources.append("src/scanner.c") +macros: list[tuple[str, str | None]] = [ + ("PY_SSIZE_T_CLEAN", None), + ("TREE_SITTER_HIDE_SYMBOLS", None), +] +if limited_api := not get_config_var("Py_GIL_DISABLED"): + macros.append(("Py_LIMITED_API", "0x030A0000")) + if system() != "Windows": cflags = ["-std=c11", "-fvisibility=hidden"] else: @@ -55,13 +63,9 @@ setup( name="_binding", sources=sources, extra_compile_args=cflags, - define_macros=[ - ("Py_LIMITED_API", "0x03090000"), - ("PY_SSIZE_T_CLEAN", None), - ("TREE_SITTER_HIDE_SYMBOLS", None), - ], + define_macros=macros, include_dirs=["src"], - py_limited_api=True, + py_limited_api=limited_api, ) ], cmdclass={