nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at r-updates 191 lines 5.4 kB view raw
1{ 2 lib, 3 buildPythonPackage, 4 setuptools, 5 pytestCheckHook, 6 tree-sitter, 7 symlinkJoin, 8 writeTextDir, 9 # `name`: grammar derivation pname in the format of `tree-sitter-<lang>` 10 name, 11 grammarDrv, 12}: 13let 14 # Map nix style `0-unstable-YYYY-MM-DD` version identifiers to a PEP 440 15 # compatible form (`0+unstableYYYYMMDD`). 16 version = lib.pipe grammarDrv [ 17 lib.getVersion 18 (lib.splitString "-") 19 ( 20 parts: 21 let 22 version = lib.head parts; 23 metadata = lib.join "" (lib.tail parts); 24 in 25 if (metadata == "") then version else "${version}+${metadata}" 26 ) 27 ]; 28 snakeCaseName = lib.replaceStrings [ "-" ] [ "_" ] name; 29 drvPrefix = "python-${name}"; 30 # If the name of the grammar attribute differs from the grammar's symbol name, 31 # it could cause a symbol mismatch at load time. This manually curated collection 32 # of overrides ensures the binding can find the correct symbol 33 langIdentOverrides = { 34 tree_sitter_org_nvim = "tree_sitter_org"; 35 }; 36 langIdent = langIdentOverrides.${snakeCaseName} or snakeCaseName; 37in 38buildPythonPackage { 39 inherit version; 40 pname = drvPrefix; 41 pyproject = true; 42 build-system = [ setuptools ]; 43 44 src = symlinkJoin { 45 name = "${drvPrefix}-source"; 46 paths = [ 47 (writeTextDir "${snakeCaseName}/__init__.py" '' 48 # AUTO-GENERATED DO NOT EDIT 49 50 # preload the parser object before importing c binding 51 # this way we can avoid dynamic linker kicking in when 52 # downstream code imports this python module 53 import ctypes 54 import sys 55 import os 56 parser = "${grammarDrv}/parser" 57 try: 58 ctypes.CDLL(parser, mode=ctypes.RTLD_GLOBAL) # cached 59 except OSError as e: 60 raise ImportError(f"cannot load tree-sitter parser object from {parser}: {e}") 61 62 # expose binding 63 from ._binding import language 64 __all__ = ["language"] 65 '') 66 (writeTextDir "${snakeCaseName}/binding.c" '' 67 // AUTO-GENERATED DO NOT EDIT 68 69 #include <Python.h> 70 71 typedef struct TSLanguage TSLanguage; 72 73 TSLanguage *${langIdent}(void); 74 75 static PyObject* _binding_language(PyObject *self, PyObject *args) { 76 return PyLong_FromVoidPtr(${langIdent}()); 77 } 78 79 static PyMethodDef methods[] = { 80 {"language", _binding_language, METH_NOARGS, 81 "Get the tree-sitter language for this grammar."}, 82 {NULL, NULL, 0, NULL} 83 }; 84 85 static struct PyModuleDef module = { 86 .m_base = PyModuleDef_HEAD_INIT, 87 .m_name = "_binding", 88 .m_doc = NULL, 89 .m_size = -1, 90 .m_methods = methods 91 }; 92 93 PyMODINIT_FUNC PyInit__binding(void) { 94 return PyModule_Create(&module); 95 } 96 '') 97 (writeTextDir "setup.py" '' 98 # AUTO-GENERATED DO NOT EDIT 99 100 from platform import system 101 from setuptools import Extension, setup 102 103 104 setup( 105 packages=["${snakeCaseName}"], 106 ext_package="${snakeCaseName}", 107 ext_modules=[ 108 Extension( 109 name="_binding", 110 sources=["${snakeCaseName}/binding.c"], 111 extra_objects = ["${grammarDrv}/parser"], 112 extra_compile_args=( 113 ["-std=c11"] if system() != 'Windows' else [] 114 ), 115 ) 116 ], 117 ) 118 '') 119 (writeTextDir "pyproject.toml" '' 120 # AUTO-GENERATED DO NOT EDIT 121 122 [build-system] 123 requires = ["setuptools", "wheel"] 124 build-backend = "setuptools.build_meta" 125 126 [project] 127 name="${snakeCaseName}" 128 description = "${langIdent} grammar for tree-sitter" 129 version = "${version}" 130 keywords = ["parsing", "incremental", "python"] 131 classifiers = [ 132 "Development Status :: 4 - Beta", 133 "Intended Audience :: Developers", 134 "Topic :: Software Development :: Compilers", 135 "Topic :: Text Processing :: Linguistic", 136 ] 137 138 requires-python = ">=3.8" 139 license = "MIT" 140 readme = "README.md" 141 142 [project.optional-dependencies] 143 core = ["tree-sitter~=0.21"] 144 145 [tool.cibuildwheel] 146 build = "cp38-*" 147 build-frontend = "build" 148 '') 149 (writeTextDir "tests/test_language.py" '' 150 # AUTO-GENERATED DO NOT EDIT 151 152 from ${snakeCaseName} import language 153 from tree_sitter import Language, Parser 154 155 # This test only checks that the binding can load the grammar from the compiled shared object. 156 # It does not verify the grammar itself; that is tested in 157 # `pkgs/development/tools/parsing/tree-sitter/grammar.nix`. 158 159 def test_language(): 160 lang = Language(language()) 161 assert lang is not None 162 parser = Parser(lang) 163 tree = parser.parse(bytes("", "utf-8")) 164 assert tree is not None 165 '') 166 ]; 167 }; 168 169 preCheck = '' 170 # https://github.com/NixOS/nixpkgs/issues/255262 171 rm -r ${snakeCaseName} 172 ''; 173 174 nativeCheckInputs = [ 175 tree-sitter 176 pytestCheckHook 177 ]; 178 179 pythonImportsCheck = [ snakeCaseName ]; 180 181 meta = { 182 description = "Python bindings for ${name}"; 183 license = lib.licenses.mit; 184 maintainers = with lib.maintainers; [ 185 a-jay98 186 adfaure 187 mightyiam 188 stepbrobd 189 ]; 190 }; 191}