diff --git a/python/_jsonnet.c b/python/_jsonnet.c index 00cace76..8101510b 100644 --- a/python/_jsonnet.c +++ b/python/_jsonnet.c @@ -448,10 +448,10 @@ static int handle_native_callbacks(struct JsonnetVm *vm, PyObject *native_callba num_params = PyTuple_Size(params); /* Include space for terminating NULL. */ const char **params_c = malloc(sizeof(const char*) * (num_params + 1)); + struct StrRefAndObj *params_utf8 = malloc(sizeof(*params_utf8) * num_params); for (i = 0; i < num_params ; ++i) { - struct StrRefAndObj param_c_utf8 = get_py_utf8_string(PyTuple_GetItem(params, i)); - params_c[i] = param_c_utf8.cstr; - release_py_utf8_string(¶m_c_utf8); + params_utf8[i] = get_py_utf8_string(PyTuple_GetItem(params, i)); + params_c[i] = params_utf8[i].cstr; } params_c[num_params] = NULL; (*ctxs)[num_natives].vm = vm; @@ -461,6 +461,11 @@ static int handle_native_callbacks(struct JsonnetVm *vm, PyObject *native_callba jsonnet_native_callback(vm, key_utf8.cstr, cpython_native_callback, &(*ctxs)[num_natives], params_c); release_py_utf8_string(&key_utf8); + /* Registration copies the names, so keep their UTF-8 storage alive until now. */ + for (i = 0; i < num_params ; ++i) { + release_py_utf8_string(¶ms_utf8[i]); + } + free(params_utf8); free(params_c); num_natives++; } diff --git a/python/_jsonnet_test.py b/python/_jsonnet_test.py index ddb09c74..4d1549ee 100644 --- a/python/_jsonnet_test.py +++ b/python/_jsonnet_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json import os import sys import unittest @@ -85,6 +86,19 @@ def setUp(self): with open(self.input_filename, "r") as infile: self.input_snippet = infile.read() + def test_native_callback_multichar_parameter_names(self): + for params, arguments, expected in ( + (("target", "path"), '("hello", "world")', ["hello", "world"]), + (("first", "second", "third"), '("one", "two", "three")', ["one", "two", "three"]), + (("target", "path"), '(path="world", target="hello")', ["hello", "world"]), + ): + with self.subTest(params=params, arguments=arguments): + result = _jsonnet.evaluate_snippet( + "test", 'std.native("collect")' + arguments, + native_callbacks={"collect": (params, lambda *values: list(values))}, + ) + self.assertEqual(json.loads(result), expected) + def test_version(self): self.assertEqual(type(_jsonnet.version), str)