From 098072d7c86bd1f5e85244e7ac0ac003c070dca7 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 13:09:13 -0700 Subject: [PATCH 01/12] Address bug with read_info. TODO write_info --- src/main/conversions.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/conversions.c b/src/main/conversions.c index 6576252d5c..79ca54e22e 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -430,6 +430,41 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, goto END; } + PyObject *py_read_info_list = PyList_New(); + if (!py_read_info_list) { + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to process read info list"); + // TODO: need to centralize clean up code + Py_DECREF(py_roles); + Py_DECREF(py_info); + goto END; + } + + for (uint32_t i = 0; i < user->read_info_size; i++) { + PyObject *py_long_value = PyLong_FromUInt32(user->read_info[i]); + if (!py_long_value) { + as_error_update( + err, AEROSPIKE_ERR_CLIENT, + "Unable to process read info list at index %" PRIu32, i); + // TODO: need to centralize clean up code + Py_DECREF(py_roles); + Py_DECREF(py_info); + goto END; + } + + int retval = PyList_Append(py_read_info_list, py_long_value); + Py_DECREF(py_long_value); + if (retval == -1) { + as_error_update( + err, AEROSPIKE_ERR_CLIENT, + "Unable to process read info list at index %" PRIu32, i); + // TODO: need to centralize clean up code + Py_DECREF(py_roles); + Py_DECREF(py_info); + goto END; + } + } + if (PyDict_SetItemString( py_info, "read_info", Py_BuildValue("i", (user->read_info ? *(user->read_info) : 0))) == From efcc6d28396f4c1ebdb292453d0b78df3a8c7f96 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 13:25:57 -0700 Subject: [PATCH 02/12] Fix usage of CPython API --- src/main/conversions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 79ca54e22e..07c4942bfb 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -430,7 +430,7 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, goto END; } - PyObject *py_read_info_list = PyList_New(); + PyObject *py_read_info_list = PyList_New(0); if (!py_read_info_list) { as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unable to process read info list"); From d7bb27f9e0f2aa2da2e602a0042c3f615bb38085 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 14:11:15 -0700 Subject: [PATCH 03/12] Finish fixing both read_info and write_info. TODO memory leaks need to be plugged --- src/main/conversions.c | 99 +++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 07c4942bfb..683741cd95 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -415,6 +415,46 @@ as_status as_partitions_status_to_pyobject( return err->code; } +PyObject *convert_nullable_array_of_uint32_to_py_list(as_error *err, + uint32_t *array, + int array_size) +{ + if (array == NULL) { + Py_RETURN_NONE; + } + + PyObject *py_list = PyList_New(0); + if (!py_list) { + goto error; + } + + for (int i = 0; i < array_size; i++) { + PyObject *py_long_value = PyLong_FromUInt32(array[i]); + if (!py_long_value) { + as_error_update( + err, AEROSPIKE_ERR_CLIENT, + "Unable to process read info list at index %" PRIu32, i); + goto CLEANUP_ON_ERROR; + } + + int retval = PyList_Append(py_read_info_list, py_long_value); + Py_DECREF(py_long_value); + if (retval == -1) { + as_error_update( + err, AEROSPIKE_ERR_CLIENT, + "Unable to process read info list at index %" PRIu32, i); + goto CLEANUP_ON_ERROR; + } + } + + return py_list; + +CLEANUP_ON_ERROR: + Py_DECREF(py_list); +error: + return NULL; +} + as_status as_user_info_to_pyobject(as_error *err, as_user *user, PyObject **py_as_user) { @@ -430,61 +470,32 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, goto END; } - PyObject *py_read_info_list = PyList_New(0); - if (!py_read_info_list) { - as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Unable to process read info list"); - // TODO: need to centralize clean up code - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; - } + uint32_t *arrays[] = {user->read_info, user->write_info}; + const char *array_names = {"read_info", "write_info"}; + int array_sizes[] = {user->read_info_size, user->write_info_size}; - for (uint32_t i = 0; i < user->read_info_size; i++) { - PyObject *py_long_value = PyLong_FromUInt32(user->read_info[i]); - if (!py_long_value) { - as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unable to process read info list at index %" PRIu32, i); - // TODO: need to centralize clean up code - Py_DECREF(py_roles); - Py_DECREF(py_info); + for (unsigned long i = 0; i < sizeof(arrays) / sizeof(arrays[0]); i++) { + PyObject *py_optional_list_of_ints = + convert_nullable_array_of_uint32_to_py_list(err, arrays[i], + array_sizes[i]); + if (!py_optional_list_of_ints) { + // TODO: centralize cleanup code in this func goto END; } - int retval = PyList_Append(py_read_info_list, py_long_value); - Py_DECREF(py_long_value); + int retval = PyDict_SetItemString(py_info, array_names[i], + py_optional_list_of_ints); + Py_DECREF(py_optional_list_of_ints); if (retval == -1) { - as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unable to process read info list at index %" PRIu32, i); - // TODO: need to centralize clean up code + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Failed to set %s in py_info.", array_names[i]); + // TODO: centralize cleanup code in this func Py_DECREF(py_roles); Py_DECREF(py_info); goto END; } } - if (PyDict_SetItemString( - py_info, "read_info", - Py_BuildValue("i", (user->read_info ? *(user->read_info) : 0))) == - -1) { - as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Failed to set %s in py_info.", "read_info"); - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; - } - if (PyDict_SetItemString( - py_info, "write_info", - Py_BuildValue("i", (user->write_info ? *(user->write_info) : 0))) == - -1) { - as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Failed to set %s in py_info.", "write_info"); - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; - } if (PyDict_SetItemString(py_info, "conns_in_use", Py_BuildValue("i", user->conns_in_use)) == -1) { as_error_update(err, AEROSPIKE_ERR_CLIENT, From 2a6748636f9f94a2ebde6820e52dbdbe5cb5917b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 14:39:14 -0700 Subject: [PATCH 04/12] Do more refactoring to make helper functions consistent with convert_nullable_array_of_uint32_to_py_list --- src/include/conversions.h | 6 +-- src/main/conversions.c | 82 ++++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/include/conversions.h b/src/include/conversions.h index 91504bf629..191040c745 100644 --- a/src/include/conversions.h +++ b/src/include/conversions.h @@ -62,9 +62,9 @@ as_status as_udf_file_to_pyobject(as_error *err, as_udf_file *entry, as_status as_udf_files_to_pyobject(as_error *err, as_udf_files *files, PyObject **py_files); -as_status str_array_of_roles_to_py_list(as_error *err, int num_elements, - char str_array_ptr[][AS_ROLE_SIZE], - PyObject *py_list); +PyObject * +convert_array_of_role_strs_to_py_list(as_error *err, int num_elements, + char str_array_ptr[][AS_ROLE_SIZE]); as_status char_double_ptr_to_py_list(as_error *err, int num_elements, int element_size, char **str_array_ptr, diff --git a/src/main/conversions.c b/src/main/conversions.c index 683741cd95..b3773240c6 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -144,12 +144,17 @@ as_status char_double_ptr_to_py_list(as_error *err, int num_elements, return err->code; } -as_status str_array_of_roles_to_py_list(as_error *err, int num_elements, - char str_array_ptr[][AS_ROLE_SIZE], - PyObject *py_list) +PyObject * +convert_array_of_role_strs_to_py_list(as_error *err, int num_elements, + char str_array_ptr[][AS_ROLE_SIZE]) { as_error_reset(err); + PyObject *py_list = PyList_New(0); + if (!py_list) { + goto error; + } + char *str; for (int i = 0; i < num_elements; i++) { @@ -158,14 +163,24 @@ as_status str_array_of_roles_to_py_list(as_error *err, int num_elements, if (py_str == NULL) { as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unable to build string value from %s.", str); - break; + goto CLEANUP_ON_ERROR; } - PyList_Append(py_list, py_str); + int retval = PyList_Append(py_list, py_str); Py_DECREF(py_str); + if (retval == -1) { + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to build append string %s to list.", str); + goto CLEANUP_ON_ERROR; + } } - return err->code; + return py_list; + +CLEANUP_ON_ERROR: + Py_DECREF(py_list); +error: + return NULL; } as_status as_user_info_array_to_pyobject(as_error *err, as_user **users, @@ -460,14 +475,19 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, { as_error_reset(err); - PyObject *py_info = PyDict_New(); - PyObject *py_roles = PyList_New(0); + PyObject *py_user_dict = PyDict_New(); - str_array_of_roles_to_py_list(err, user->roles_size, user->roles, py_roles); - if (err->code != AEROSPIKE_OK) { - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; + PyObject *py_roles = convert_array_of_role_strs_to_py_list( + err, user->roles_size, user->roles); + if (!py_roles) { + goto CLEANUP_ON_ERROR; + } + int retval = PyDict_SetItemString(py_user_dict, "roles", py_roles); + Py_DECREF(py_roles); + if (retval == -1) { + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Failed to set %s in user dictionary.", "roles"); + goto CLEANUP_ON_ERROR; } uint32_t *arrays[] = {user->read_info, user->write_info}; @@ -479,44 +499,34 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, convert_nullable_array_of_uint32_to_py_list(err, arrays[i], array_sizes[i]); if (!py_optional_list_of_ints) { - // TODO: centralize cleanup code in this func - goto END; + goto CLEANUP_ON_ERROR; } - int retval = PyDict_SetItemString(py_info, array_names[i], + int retval = PyDict_SetItemString(py_user_dict, array_names[i], py_optional_list_of_ints); Py_DECREF(py_optional_list_of_ints); if (retval == -1) { as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Failed to set %s in py_info.", array_names[i]); - // TODO: centralize cleanup code in this func - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; + "Failed to set %s in user dictionary.", + array_names[i]); + goto CLEANUP_ON_ERROR; } } - if (PyDict_SetItemString(py_info, "conns_in_use", + if (PyDict_SetItemString(py_user_dict, "conns_in_use", Py_BuildValue("i", user->conns_in_use)) == -1) { as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Failed to set %s in py_info.", "conns_in_use"); - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; - } - if (PyDict_SetItemString(py_info, "roles", py_roles) == -1) { - as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Failed to set %s in py_info.", "roles"); - Py_DECREF(py_roles); - Py_DECREF(py_info); - goto END; + "Failed to set %s in py_user_dict.", "conns_in_use"); + goto CLEANUP_ON_ERROR; } - Py_DECREF(py_roles); + *py_as_user = py_user_dict; - *py_as_user = py_info; +CLEANUP_ON_ERROR: + if (err->code != AEROSPIKE_OK) { + Py_DECREF(py_user_dict); + } -END: return err->code; } From 642024d962eed6aa9988a2d208f139dc65ab2a3a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 14:43:05 -0700 Subject: [PATCH 05/12] fix compiler error --- src/main/conversions.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index b3773240c6..3827de9a7e 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -446,18 +446,16 @@ PyObject *convert_nullable_array_of_uint32_to_py_list(as_error *err, for (int i = 0; i < array_size; i++) { PyObject *py_long_value = PyLong_FromUInt32(array[i]); if (!py_long_value) { - as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unable to process read info list at index %" PRIu32, i); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get list item at index %" PRIu32, i); goto CLEANUP_ON_ERROR; } - int retval = PyList_Append(py_read_info_list, py_long_value); + int retval = PyList_Append(py_list, py_long_value); Py_DECREF(py_long_value); if (retval == -1) { - as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unable to process read info list at index %" PRIu32, i); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to append list item at index %" PRIu32, i); goto CLEANUP_ON_ERROR; } } From 4239c92cc519545ce51d48c7c31651caa730764d Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 14:45:10 -0700 Subject: [PATCH 06/12] Fix syntax err --- src/main/conversions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 3827de9a7e..6937bc06f9 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -489,7 +489,7 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, } uint32_t *arrays[] = {user->read_info, user->write_info}; - const char *array_names = {"read_info", "write_info"}; + const char *array_names[] = {"read_info", "write_info"}; int array_sizes[] = {user->read_info_size, user->write_info_size}; for (unsigned long i = 0; i < sizeof(arrays) / sizeof(arrays[0]); i++) { From fdbee858aa4627e42cec60faa2656e18dd6e3b35 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 May 2026 15:30:16 -0700 Subject: [PATCH 07/12] Fix tests not following the api docs --- test/new_tests/conftest.py | 24 +++++++++++++++++++ test/new_tests/test_admin_query_user_info.py | 15 +++--------- test/new_tests/test_admin_query_users_info.py | 6 ++--- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/test/new_tests/conftest.py b/test/new_tests/conftest.py index 6d3fe87523..876336330d 100644 --- a/test/new_tests/conftest.py +++ b/test/new_tests/conftest.py @@ -322,3 +322,27 @@ def expect_earlier_than_server_version_to_fail(as_connection, request): else: # InvalidRequest, BinIncompatibleTypes are exceptions that have been raised request.cls.expected_context_for_pos_tests = pytest.raises(e.ServerError) + +def check_user_dictionary(user: dict): + assert set(user["roles"]) == set([ + "read", + "read-write", + "sys-admin" + ]) + + + # The user has not read or written anything, so all r/w stats should be 0 + # NOTE: we don't test the scenario where read_info / write_info is not 0 + # because it takes time and a lot of transactions for the server to actually record non-zero values + dict_keys = [ + "read_info", + "write_info" + ] + for key in dict_keys: + assert type(user[key]) == list + assert len(user[key]) == 4 + for element in user[key]: + assert element == 0 + + # We assume no clients were logged in as this user + assert user.get("conns_in_use") == 0 diff --git a/test/new_tests/test_admin_query_user_info.py b/test/new_tests/test_admin_query_user_info.py index 0138ee4dd7..282a91e65c 100644 --- a/test/new_tests/test_admin_query_user_info.py +++ b/test/new_tests/test_admin_query_user_info.py @@ -4,6 +4,7 @@ import time from .test_base_class import TestBaseClass from aerospike import exception as e +from .conftest import check_user_dictionary import aerospike @@ -59,18 +60,8 @@ def test_query_user_info_with_proper_parameters(self): time.sleep(2) user_details = self.client.admin_query_user_info(self.user) - assert user_details.get("roles") == [ - "read", - "read-write", - "sys-admin" - ] - # The user has not read or written anything, so all r/w stats should be 0 - # NOTE: we don't test the scenario where read_info / write_info is not 0 - # because it takes time and a lot of transactions for the server to actually record non-zero values - assert user_details.get("read_info") == 0 - assert user_details.get("write_info") == 0 - # No clients were logged in as this user - assert user_details.get("conns_in_use") == 0 + + check_user_dictionary(user_details) def test_query_user_info_with_invalid_timeout_policy_value(self): policy = {"timeout": 0.1} diff --git a/test/new_tests/test_admin_query_users_info.py b/test/new_tests/test_admin_query_users_info.py index 7cb61d97e5..3dd3489564 100644 --- a/test/new_tests/test_admin_query_users_info.py +++ b/test/new_tests/test_admin_query_users_info.py @@ -4,6 +4,7 @@ import time from .test_base_class import TestBaseClass from aerospike import exception as e +from .conftest import check_user_dictionary import aerospike @@ -56,10 +57,7 @@ def test_query_users_info_with_proper_parameters(self): # Usage test; doesn't actually test if the server records user data user_details = user_details.get("example-test") - assert user_details.get("roles") == ["read", "read-write", "sys-admin"] - assert user_details.get("read_info") == 0 - assert user_details.get("write_info") == 0 - assert user_details.get("conns_in_use") == 0 + check_user_dictionary(user_details) def test_query_users_info_with_invalid_timeout_policy_value(self): From fcb3549de89d92658dc55e7aacdb4de60c1a9eb2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 26 May 2026 16:30:25 -0700 Subject: [PATCH 08/12] We can remove this declaration in header if nowhere else is using it. --- src/include/conversions.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/include/conversions.h b/src/include/conversions.h index 191040c745..2af657e6de 100644 --- a/src/include/conversions.h +++ b/src/include/conversions.h @@ -62,10 +62,6 @@ as_status as_udf_file_to_pyobject(as_error *err, as_udf_file *entry, as_status as_udf_files_to_pyobject(as_error *err, as_udf_files *files, PyObject **py_files); -PyObject * -convert_array_of_role_strs_to_py_list(as_error *err, int num_elements, - char str_array_ptr[][AS_ROLE_SIZE]); - as_status char_double_ptr_to_py_list(as_error *err, int num_elements, int element_size, char **str_array_ptr, PyObject *py_list); From 491d0fcf199db2f0905a199a20cbc572e1f08d95 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 26 May 2026 16:32:10 -0700 Subject: [PATCH 09/12] Improve var naming --- src/main/conversions.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 6937bc06f9..2f29fca9d6 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -144,21 +144,21 @@ as_status char_double_ptr_to_py_list(as_error *err, int num_elements, return err->code; } -PyObject * -convert_array_of_role_strs_to_py_list(as_error *err, int num_elements, - char str_array_ptr[][AS_ROLE_SIZE]) +PyObject *convert_array_of_role_strs_to_py_list(as_error *err, int num_elements, + char str_array[][AS_ROLE_SIZE]) { as_error_reset(err); PyObject *py_list = PyList_New(0); if (!py_list) { + as_error_update("Failed to create python list"); goto error; } char *str; for (int i = 0; i < num_elements; i++) { - str = str_array_ptr[i]; + str = str_array[i]; PyObject *py_str = Py_BuildValue("s", str); if (py_str == NULL) { as_error_update(err, AEROSPIKE_ERR_CLIENT, From ffd521231e6f1e06e172ddef7e6a24cb90caf652 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 26 May 2026 16:33:05 -0700 Subject: [PATCH 10/12] Fix error message --- src/main/conversions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 2f29fca9d6..b7245ef134 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -170,7 +170,7 @@ PyObject *convert_array_of_role_strs_to_py_list(as_error *err, int num_elements, Py_DECREF(py_str); if (retval == -1) { as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Unable to build append string %s to list.", str); + "Unable to append string %s to list.", str); goto CLEANUP_ON_ERROR; } } From 1101abe80865930526417fd7ccb53d304566e0f5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 26 May 2026 16:35:06 -0700 Subject: [PATCH 11/12] Make helper function naming more accurate. --- src/main/conversions.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index b7245ef134..4ce959c019 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -430,9 +430,9 @@ as_status as_partitions_status_to_pyobject( return err->code; } -PyObject *convert_nullable_array_of_uint32_to_py_list(as_error *err, - uint32_t *array, - int array_size) +PyObject *convert_nullable_array_of_uint32_to_py_optional_list(as_error *err, + uint32_t *array, + int array_size) { if (array == NULL) { Py_RETURN_NONE; @@ -494,8 +494,8 @@ as_status as_user_info_to_pyobject(as_error *err, as_user *user, for (unsigned long i = 0; i < sizeof(arrays) / sizeof(arrays[0]); i++) { PyObject *py_optional_list_of_ints = - convert_nullable_array_of_uint32_to_py_list(err, arrays[i], - array_sizes[i]); + convert_nullable_array_of_uint32_to_py_optional_list( + err, arrays[i], array_sizes[i]); if (!py_optional_list_of_ints) { goto CLEANUP_ON_ERROR; } From 7a31fbf878e0d78c63a455e58a4de94179a5e88c Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 26 May 2026 16:36:42 -0700 Subject: [PATCH 12/12] Add missing as_error_update to convert_nullable_array_of_uint32_to_py_optional_list --- src/main/conversions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/conversions.c b/src/main/conversions.c index 4ce959c019..4cb3701beb 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -440,6 +440,7 @@ PyObject *convert_nullable_array_of_uint32_to_py_optional_list(as_error *err, PyObject *py_list = PyList_New(0); if (!py_list) { + as_error_update("Failed to create python list"); goto error; }