From 1aefe3b248a12b6df34dac936b1cc6565c631948 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:58:46 -0700 Subject: [PATCH 01/66] Build for 3.13 --- .github/workflows/build-wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index be0f8f342a..e41187325c 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -27,7 +27,7 @@ on: type: string description: Valid JSON list of Python tags to build the client for required: false - default: '["cp38", "cp39", "cp310", "cp311", "cp312"]' + default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313"]' platform-tag: description: Platform to build the client for. type: choice @@ -76,7 +76,7 @@ on: python-tags: type: string required: false - default: '["cp38", "cp39", "cp310", "cp311", "cp312"]' + default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313"]' platform-tag: type: string required: true From dcddcfe1299f21b03ae62961ffd1fa645664becf Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:03:25 -0700 Subject: [PATCH 02/66] Fix --- .github/workflows/build-wheels.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index e41187325c..edcd4bdee2 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -323,6 +323,8 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} + # We want the flexibility to support upcoming Python releases that aren't GA yet + allow-prereleases: true - name: Install wheel run: python3 -m pip install aerospike --force-reinstall --no-index --find-links=./ From 25c7962db8c9f908021c54a2e06511418fae9a04 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:07:36 -0700 Subject: [PATCH 03/66] rm --- .github/workflows/build-wheels.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index edcd4bdee2..e41187325c 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -323,8 +323,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - # We want the flexibility to support upcoming Python releases that aren't GA yet - allow-prereleases: true - name: Install wheel run: python3 -m pip install aerospike --force-reinstall --no-index --find-links=./ From 46d8945037d186ae718f1666e427c36bce481c24 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:59:43 -0700 Subject: [PATCH 04/66] add 3.13 to smoke tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c308d23015..1974d3f208 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - py-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + py-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] fail-fast: false steps: From 36bca2d4474ef2692186eb545a18f99a2f57b144 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:24:33 -0700 Subject: [PATCH 05/66] free threading --- .github/workflows/build-wheels.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index e41187325c..acb708ff83 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -149,6 +149,10 @@ jobs: strategy: matrix: python-tag: ${{ fromJSON(inputs.python-tags) }} + free-threaded: [false] + include: + - python-tag: '3.13' + free-threaded: true fail-fast: false runs-on: ${{ needs.get-runner-os.outputs.runner-os }} env: @@ -250,6 +254,7 @@ jobs: yum install python-setuptools -y # delvewheel is not enabled by default but we do need to repair the wheel CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" + CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free-threaded }} CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path ./aerospike-client-c/vs/x64/Release -w {dest_dir} {wheel}" CIBW_TEST_COMMAND: ${{ env.TEST_COMMAND }} From 50d2a4640d942feda69beb3e5f67f60bfd87aac0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:25:37 -0700 Subject: [PATCH 06/66] Fix --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index acb708ff83..a802e86793 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -151,7 +151,7 @@ jobs: python-tag: ${{ fromJSON(inputs.python-tags) }} free-threaded: [false] include: - - python-tag: '3.13' + - python-tag: 'cp313' free-threaded: true fail-fast: false runs-on: ${{ needs.get-runner-os.outputs.runner-os }} From 66bf697dc02936d24fd314e55d2740a498420165 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:32:41 -0700 Subject: [PATCH 07/66] fix --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index a802e86793..6224e87faf 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -254,7 +254,7 @@ jobs: yum install python-setuptools -y # delvewheel is not enabled by default but we do need to repair the wheel CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" - CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free-threaded }} + CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free-threaded && 1 || 0 }} CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path ./aerospike-client-c/vs/x64/Release -w {dest_dir} {wheel}" CIBW_TEST_COMMAND: ${{ env.TEST_COMMAND }} From 9e5c66f510216ac1485b0f1e7cdd1fe8bd69e887 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:36:01 -0700 Subject: [PATCH 08/66] use 3.13 final --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 6224e87faf..f8469e4175 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -242,7 +242,7 @@ jobs: run: echo "INCLUDE_DSYM=1" >> $GITHUB_ENV - name: Build wheel - uses: pypa/cibuildwheel@v2.20.0 + uses: pypa/cibuildwheel@v2.21.3 env: CIBW_ENVIRONMENT_PASS_LINUX: ${{ inputs.unoptimized && 'UNOPTIMIZED' || '' }} CIBW_ENVIRONMENT_MACOS: SSL_LIB_PATH="$(brew --prefix openssl@1.1)/lib/" CPATH="$(brew --prefix openssl@1.1)/include/" STATIC_SSL=1 From 376869f178677a81d3dc3faa81a82727dce8b8c2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 18 Oct 2024 08:06:41 -0700 Subject: [PATCH 09/66] set to str? --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index f8469e4175..e661ef5b3b 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -254,7 +254,7 @@ jobs: yum install python-setuptools -y # delvewheel is not enabled by default but we do need to repair the wheel CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" - CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free-threaded && 1 || 0 }} + CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free-threaded && '1' || '0' }} CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path ./aerospike-client-c/vs/x64/Release -w {dest_dir} {wheel}" CIBW_TEST_COMMAND: ${{ env.TEST_COMMAND }} From 20d174f76359174a156599a623cbb6ff917edb26 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 18 Oct 2024 08:10:29 -0700 Subject: [PATCH 10/66] try --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index e661ef5b3b..ce16ddb994 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -151,7 +151,7 @@ jobs: python-tag: ${{ fromJSON(inputs.python-tags) }} free-threaded: [false] include: - - python-tag: 'cp313' + - python-tag: 'cp313t' free-threaded: true fail-fast: false runs-on: ${{ needs.get-runner-os.outputs.runner-os }} From 35278e7c174a0c258827323f3346b5a061fa9995 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 18 Oct 2024 08:13:41 -0700 Subject: [PATCH 11/66] Fix --- .github/workflows/build-wheels.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index ce16ddb994..10380dbb98 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -27,7 +27,7 @@ on: type: string description: Valid JSON list of Python tags to build the client for required: false - default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313"]' + default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313", "cp313t"]' platform-tag: description: Platform to build the client for. type: choice @@ -76,7 +76,7 @@ on: python-tags: type: string required: false - default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313"]' + default: '["cp38", "cp39", "cp310", "cp311", "cp312", "cp313", "cp313t"]' platform-tag: type: string required: true @@ -149,10 +149,6 @@ jobs: strategy: matrix: python-tag: ${{ fromJSON(inputs.python-tags) }} - free-threaded: [false] - include: - - python-tag: 'cp313t' - free-threaded: true fail-fast: false runs-on: ${{ needs.get-runner-os.outputs.runner-os }} env: @@ -254,7 +250,7 @@ jobs: yum install python-setuptools -y # delvewheel is not enabled by default but we do need to repair the wheel CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" - CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free-threaded && '1' || '0' }} + CIBW_FREE_THREADED_SUPPORT: ${{ matrix.python-tag == 'cp313t' && '1' || '0' }} CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path ./aerospike-client-c/vs/x64/Release -w {dest_dir} {wheel}" CIBW_TEST_COMMAND: ${{ env.TEST_COMMAND }} From 828c63c7dfea8a6eb43c87d97cb26937e22ad532 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:27:50 -0700 Subject: [PATCH 12/66] enable flag --- src/main/aerospike.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/aerospike.c b/src/main/aerospike.c index c94bc0fea3..2178710dc8 100644 --- a/src/main/aerospike.c +++ b/src/main/aerospike.c @@ -613,6 +613,10 @@ PyMODINIT_FUNC PyInit_aerospike(void) } } +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + return py_aerospike_module; GLOBAL_HOSTS_CLEANUP_ON_ERROR: From 54961e67528efe7ce9749c3444016e38690a41dd Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:34:15 -0700 Subject: [PATCH 13/66] add cs --- src/main/client/connect.c | 2 ++ src/main/client/operate.c | 2 ++ src/main/conversions.c | 4 ++++ src/main/exception.c | 7 +++++++ src/main/policy.c | 2 ++ 5 files changed, 17 insertions(+) diff --git a/src/main/client/connect.c b/src/main/client/connect.c index 54f0e0cb26..5003227311 100644 --- a/src/main/client/connect.c +++ b/src/main/client/connect.c @@ -100,6 +100,7 @@ int AerospikeClientConnect(AerospikeClient *self) } while (1) { flag = 0; + Py_BEGIN_CRITICAL_SECTION(py_global_hosts); while (PyDict_Next(py_global_hosts, &pos, &py_key, &py_value)) { if (((AerospikeGlobalHosts *)py_value)->as->config.use_shm) { if (((AerospikeGlobalHosts *)py_value)->shm_key == @@ -109,6 +110,7 @@ int AerospikeClientConnect(AerospikeClient *self) } } } + Py_END_CRITICAL_SECTION(); if (!flag) { self->as->config.shm_key = shm_key; break; diff --git a/src/main/client/operate.c b/src/main/client/operate.c index dbc2513e82..c08a1c0d80 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -386,6 +386,7 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, operation, SERIALIZER_PYTHON); } + Py_BEGIN_CRITICAL_SECTION(py_val); while (PyDict_Next(py_val, &pos, &key_op, &value)) { if (!PyUnicode_Check(key_op)) { return as_error_update(err, AEROSPIKE_ERR_CLIENT, @@ -439,6 +440,7 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, } } } + Py_END_CRITICAL_SECTION(); *op = operation; diff --git a/src/main/conversions.c b/src/main/conversions.c index 210c9994e2..3cfb750581 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -747,6 +747,7 @@ as_status pyobject_to_map(AerospikeClient *self, as_error *err, } } + Py_BEGIN_CRITICAL_SECTION(py_dict); while (PyDict_Next(py_dict, &pos, &py_key, &py_val)) { as_val *key = NULL; as_val *val = NULL; @@ -763,6 +764,7 @@ as_status pyobject_to_map(AerospikeClient *self, as_error *err, } as_map_set(*map, key, val); } + Py_END_CRITICAL_SECTION(); if (err->code != AEROSPIKE_OK) { as_map_destroy(*map); @@ -1289,6 +1291,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, as_record_init(rec, size); + Py_BEGIN_CRITICAL_SECTION(py_rec); while (PyDict_Next(py_rec, &pos, &key, &value)) { if (!PyUnicode_Check(key)) { @@ -1462,6 +1465,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, } } } + Py_END_CRITICAL_SECTION(); if (py_meta && py_meta != Py_None) { if (!PyDict_Check(py_meta)) { diff --git a/src/main/exception.c b/src/main/exception.c index 4f9a7ab0d0..f1fede8c21 100644 --- a/src/main/exception.c +++ b/src/main/exception.c @@ -362,9 +362,11 @@ void remove_exception(as_error *err) Py_ssize_t pos = 0; PyObject *py_module_dict = PyModule_GetDict(py_module); + Py_BEGIN_CRITICAL_SECTION(py_module_dict); while (PyDict_Next(py_module_dict, &pos, &py_key, &py_value)) { Py_DECREF(py_value); } + Py_END_CRITICAL_SECTION(); } // TODO: idea. Use python dict to map error code to exception @@ -375,6 +377,7 @@ void raise_exception(as_error *err) PyObject *py_module_dict = PyModule_GetDict(py_module); bool found = false; + Py_BEGIN_CRITICAL_SECTION(py_module_dict); while (PyDict_Next(py_module_dict, &pos, &py_key, &py_value)) { if (PyObject_HasAttrString(py_value, "code")) { PyObject *py_code = PyObject_GetAttrString(py_value, "code"); @@ -416,6 +419,8 @@ void raise_exception(as_error *err) Py_DECREF(py_code); } } + Py_END_CRITICAL_SECTION(); + // We haven't found the right exception, just use AerospikeError if (!found) { PyObject *base_exception = @@ -446,6 +451,7 @@ PyObject *raise_exception_old(as_error *err) PyObject *py_module_dict = PyModule_GetDict(py_module); bool found = false; + Py_BEGIN_CRITICAL_SECTION(py_module_dict); while (PyDict_Next(py_module_dict, &pos, &py_key, &py_value)) { if (PyObject_HasAttrString(py_value, "code")) { PyObject *py_code = PyObject_GetAttrString(py_value, "code"); @@ -486,6 +492,7 @@ PyObject *raise_exception_old(as_error *err) Py_DECREF(py_code); } } + Py_END_CRITICAL_SECTION(); // We haven't found the right exception, just use AerospikeError if (!found) { PyObject *base_exception = diff --git a/src/main/policy.c b/src/main/policy.c index e27d57a6e1..e49d5a355c 100644 --- a/src/main/policy.c +++ b/src/main/policy.c @@ -163,6 +163,7 @@ void set_scan_options(as_error *err, as_scan *scan_p, PyObject *py_options) PyObject *key = NULL, *value = NULL; Py_ssize_t pos = 0; int64_t val = 0; + Py_BEGIN_CRITICAL_SECTION(py_options); while (PyDict_Next(py_options, &pos, &key, &value)) { char *key_name = (char *)PyUnicode_AsUTF8(key); if (!PyUnicode_Check(key)) { @@ -203,6 +204,7 @@ void set_scan_options(as_error *err, as_scan *scan_p, PyObject *py_options) break; } } + Py_END_CRITICAL_SECTION(); } else { as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid option(type)"); From 601a0116f42ef0c72d9a572cd62b33ca66e8f1ef Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:39:12 -0700 Subject: [PATCH 14/66] try --- src/main/aerospike.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/aerospike.c b/src/main/aerospike.c index 2178710dc8..d0dc50450f 100644 --- a/src/main/aerospike.c +++ b/src/main/aerospike.c @@ -614,7 +614,7 @@ PyMODINIT_FUNC PyInit_aerospike(void) } #ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); + PyUnstable_Module_SetGIL(py_aerospike_module, Py_MOD_GIL_NOT_USED); #endif return py_aerospike_module; From 7462be6b643b017bfcecc72b0562f5068451a57b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:54:00 -0700 Subject: [PATCH 15/66] Support 3.13t on self hosted --- .github/workflows/build-wheels.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 10380dbb98..00b536c969 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -322,9 +322,22 @@ jobs: shell: bash - uses: actions/setup-python@v5 + if: ${{ matrix.python-tag != 'cp313t' }} with: python-version: ${{ env.PYTHON_VERSION }} + - if: ${{ matrix.python-tag == 'cp313t' }} + uses: astral-sh/setup-uv@v3 + with: + version: "0.4.x" + + - if: ${{ matrix.python-tag == 'cp313t' }} + run: | + uv python install 3.13t + uv venv --python 3.13t + . .venv/bin/activate + echo PATH=$PATH >> $GITHUB_ENV + - name: Install wheel run: python3 -m pip install aerospike --force-reinstall --no-index --find-links=./ shell: bash From 6bb6c6dce3653940167324e36b6314369f51f832 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:04:07 -0700 Subject: [PATCH 16/66] call pip directly so uv works --- .github/workflows/build-wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 00b536c969..5798a30cf1 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -339,7 +339,7 @@ jobs: echo PATH=$PATH >> $GITHUB_ENV - name: Install wheel - run: python3 -m pip install aerospike --force-reinstall --no-index --find-links=./ + run: pip install aerospike --force-reinstall --no-index --find-links=./ shell: bash - name: Connect to Docker container on remote machine with Docker daemon @@ -350,7 +350,7 @@ jobs: crudini --set config.conf enterprise-edition hosts ${env:DOCKER_HOST_IP}:3000 working-directory: test - - run: python3 -m pip install pytest -c requirements.txt + - run: pip install pytest -c requirements.txt working-directory: test shell: bash From 5a2846e359ca43a775c18eb8d86b771752eef2eb Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:15:46 -0700 Subject: [PATCH 17/66] use alt commands --- .github/workflows/build-wheels.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 5798a30cf1..cc969ef1ae 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -339,7 +339,7 @@ jobs: echo PATH=$PATH >> $GITHUB_ENV - name: Install wheel - run: pip install aerospike --force-reinstall --no-index --find-links=./ + run: uv pip install aerospike --force-reinstall --no-index --find-links=./ shell: bash - name: Connect to Docker container on remote machine with Docker daemon @@ -350,11 +350,11 @@ jobs: crudini --set config.conf enterprise-edition hosts ${env:DOCKER_HOST_IP}:3000 working-directory: test - - run: pip install pytest -c requirements.txt + - run: uv pip install pytest -c requirements.txt working-directory: test shell: bash - - run: python3 -m pytest new_tests/ + - run: uv python -m pytest new_tests/ working-directory: test shell: bash From 36a6e44cda9a0dd0b14a4924fca67753bd7c9aa5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:19:17 -0700 Subject: [PATCH 18/66] run --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index cc969ef1ae..81e226e7a2 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -354,7 +354,7 @@ jobs: working-directory: test shell: bash - - run: uv python -m pytest new_tests/ + - run: uv run python -m pytest new_tests/ working-directory: test shell: bash From 7caa91f254a04031c4a6b366568d2847f79d807a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:26:38 -0700 Subject: [PATCH 19/66] fix --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 81e226e7a2..5499b12290 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -354,7 +354,7 @@ jobs: working-directory: test shell: bash - - run: uv run python -m pytest new_tests/ + - run: uv run --module pytest new_tests/ working-directory: test shell: bash From d0afcbab5b8f43717fdcc698afa3f4e75f1296cb Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:55:58 -0700 Subject: [PATCH 20/66] dont buildf --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 5499b12290..c7e49543ce 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -354,7 +354,7 @@ jobs: working-directory: test shell: bash - - run: uv run --module pytest new_tests/ + - run: uv run --no-build --module pytest new_tests/ working-directory: test shell: bash From 7429c53a948bf4ca38d11c7229613deee55a56d1 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:00:09 -0700 Subject: [PATCH 21/66] dont include pyproject.toml --- .github/workflows/build-wheels.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index c7e49543ce..e9a0b6ed6e 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -295,6 +295,9 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} + sparse-checkout: | + test + .github # Need to be able to save Docker Hub credentials to keychain - if: ${{ inputs.platform-tag == 'macosx_arm64' && inputs.use-server-rc }} @@ -354,7 +357,7 @@ jobs: working-directory: test shell: bash - - run: uv run --no-build --module pytest new_tests/ + - run: uv run --module pytest new_tests/ working-directory: test shell: bash From 7a1f8d76c07f60421c5b3b193819218be04b19e5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:06:21 -0700 Subject: [PATCH 22/66] be more specific --- .github/workflows/build-wheels.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index e9a0b6ed6e..83900c4ca5 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -295,9 +295,11 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} + sparse-checkout-cone-mode: false sparse-checkout: | - test - .github + !pyproject.toml + test/** + .github/** # Need to be able to save Docker Hub credentials to keychain - if: ${{ inputs.platform-tag == 'macosx_arm64' && inputs.use-server-rc }} From c71d8e6f385bad027c5da9ef0785d92eb93dfb1b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:17:01 -0700 Subject: [PATCH 23/66] add cond --- .github/workflows/build-wheels.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 83900c4ca5..6568269abe 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -318,6 +318,7 @@ jobs: name: ${{ env.BUILD_IDENTIFIER }}.build - name: Convert Python tag to Python version + if: ${{ matrix.python-tag != 'cp313t' }} # Don't use sed because we want this command to work on both mac and windows # The command used in GNU sed is different than in macOS sed run: | From 1fe262dfcb9db04062e5cd3e5b1c228694f71085 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:25:01 -0700 Subject: [PATCH 24/66] wip --- .github/workflows/build-wheels.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 6568269abe..21b835ce69 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -338,14 +338,18 @@ jobs: version: "0.4.x" - if: ${{ matrix.python-tag == 'cp313t' }} + # TODO: double check that uv automatically uses python in venv run: | uv python install 3.13t uv venv --python 3.13t - . .venv/bin/activate - echo PATH=$PATH >> $GITHUB_ENV + shell: bash + + # TODO: check if uv is installed, instead + - run: echo PIP_COMMAND="${{ matrix.python-tag == 'cp313t' && 'uv' || '' }} pip" >> $GITHUB_ENV + shell: bash - name: Install wheel - run: uv pip install aerospike --force-reinstall --no-index --find-links=./ + run: ${{ env.PIP_COMMAND }} install aerospike --force-reinstall --no-index --find-links=./ shell: bash - name: Connect to Docker container on remote machine with Docker daemon @@ -356,11 +360,14 @@ jobs: crudini --set config.conf enterprise-edition hosts ${env:DOCKER_HOST_IP}:3000 working-directory: test - - run: uv pip install pytest -c requirements.txt + - run: ${{ env.PIP_COMMAND }} install pytest -c requirements.txt working-directory: test shell: bash - - run: uv run --module pytest new_tests/ + - run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'python3 -m' || 'uv run --module' }} >> $GITHUB_ENV + shell: bash + + - run: ${{ env.PYTHON_M_COMMAND }} pytest new_tests/ working-directory: test shell: bash From f7e79c10c37a111c35360c764f02e6ffd533475f Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:35:51 -0700 Subject: [PATCH 25/66] fix --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 21b835ce69..e19e4556fc 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -364,7 +364,7 @@ jobs: working-directory: test shell: bash - - run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'python3 -m' || 'uv run --module' }} >> $GITHUB_ENV + - run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'uv run --module' || 'python3 -m' }} >> $GITHUB_ENV shell: bash - run: ${{ env.PYTHON_M_COMMAND }} pytest new_tests/ From 26dc7f5004daeda10c4320c14dbcf668077dd1d6 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:36:12 -0700 Subject: [PATCH 26/66] dont need to install python. uv venv will do it for you --- .github/workflows/build-wheels.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index e19e4556fc..959e3b1918 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -338,10 +338,8 @@ jobs: version: "0.4.x" - if: ${{ matrix.python-tag == 'cp313t' }} - # TODO: double check that uv automatically uses python in venv - run: | - uv python install 3.13t - uv venv --python 3.13t + # Will automatically install Python 3.13t if not present + run: uv venv --python 3.13t shell: bash # TODO: check if uv is installed, instead From aab9e2ca971487dd7c3f184d7891e7da1217f6ef Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:08:28 -0700 Subject: [PATCH 27/66] add todo for later --- src/main/exception.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/exception.c b/src/main/exception.c index f1fede8c21..634ec91369 100644 --- a/src/main/exception.c +++ b/src/main/exception.c @@ -377,6 +377,7 @@ void raise_exception(as_error *err) PyObject *py_module_dict = PyModule_GetDict(py_module); bool found = false; + // TODO: this is not atomic enough Py_BEGIN_CRITICAL_SECTION(py_module_dict); while (PyDict_Next(py_module_dict, &pos, &py_key, &py_value)) { if (PyObject_HasAttrString(py_value, "code")) { From 2ea9a9a4a1f2a3f11d877baf50d74030b2220072 Mon Sep 17 00:00:00 2001 From: juliannguyen4 <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:18:23 +0000 Subject: [PATCH 28/66] upgrade_pythoncapi.py --- src/include/pythoncapi_compat.h | 1659 +++++++++++++++++++++++++ src/main/client/admin.c | 6 +- src/main/client/batch_operate.c | 6 +- src/main/client/batch_write.c | 10 +- src/main/client/cdt_operation_utils.c | 4 +- src/main/client/operate.c | 12 +- src/main/client/query.c | 4 +- src/main/client/sec_index.c | 4 +- src/main/client/set_xdr_filter.c | 4 +- src/main/client/truncate.c | 4 +- src/main/client/type.c | 8 +- src/main/conversions.c | 30 +- src/main/convert_expressions.c | 10 +- src/main/exception.c | 6 +- src/main/global_hosts/type.c | 2 +- src/main/nullobject/type.c | 2 +- src/main/policy.c | 62 +- src/main/policy_config.c | 16 +- src/main/query/apply.c | 4 +- src/main/query/type.c | 4 +- src/main/scan/apply.c | 4 +- 21 files changed, 1775 insertions(+), 86 deletions(-) create mode 100644 src/include/pythoncapi_compat.h diff --git a/src/include/pythoncapi_compat.h b/src/include/pythoncapi_compat.h new file mode 100644 index 0000000000..12aac27f11 --- /dev/null +++ b/src/include/pythoncapi_compat.h @@ -0,0 +1,1659 @@ +// Header file providing new C API functions to old Python versions. +// +// File distributed under the Zero Clause BSD (0BSD) license. +// Copyright Contributors to the pythoncapi_compat project. +// +// Homepage: +// https://github.com/python/pythoncapi_compat +// +// Latest version: +// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h +// +// SPDX-License-Identifier: 0BSD + +#ifndef PYTHONCAPI_COMPAT +#define PYTHONCAPI_COMPAT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// Python 3.11.0b4 added PyFrame_Back() to Python.h +#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) + #include "frameobject.h" // PyFrameObject, PyFrame_GetBack() +#endif + +#ifndef _Py_CAST + #define _Py_CAST(type, expr) ((type)(expr)) +#endif + +// Static inline functions should use _Py_NULL rather than using directly NULL +// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, +// _Py_NULL is defined as nullptr. +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L) || \ + (defined(__cplusplus) && __cplusplus >= 201103) + #define _Py_NULL nullptr +#else + #define _Py_NULL NULL +#endif + +// Cast argument to PyObject* type. +#ifndef _PyObject_CAST + #define _PyObject_CAST(op) _Py_CAST(PyObject *, op) +#endif + +#ifndef Py_BUILD_ASSERT + #define Py_BUILD_ASSERT(cond) \ + do { \ + (void)sizeof(char[1 - 2 * !(cond)]); \ + } while (0) +#endif + +// bpo-42262 added Py_NewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) +static inline PyObject *_Py_NewRef(PyObject *obj) +{ + Py_INCREF(obj); + return obj; +} + #define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) +#endif + +// bpo-42262 added Py_XNewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) +static inline PyObject *_Py_XNewRef(PyObject *obj) +{ + Py_XINCREF(obj); + return obj; +} + #define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) +#endif + +// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) +static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +{ + ob->ob_refcnt = refcnt; +} + #define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) +#endif + +// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. +// It is excluded from the limited C API. +#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && \ + !defined(Py_LIMITED_API) + #define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject **, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ + } while (0) + + #define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject **, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ + } while (0) +#endif + +// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() +// to Python 3.10.0b1. +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) + #define Py_Is(x, y) ((x) == (y)) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) + #define Py_IsNone(x) Py_Is(x, Py_None) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && \ + !defined(Py_IsTrue) + #define Py_IsTrue(x) Py_Is(x, Py_True) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && \ + !defined(Py_IsFalse) + #define Py_IsFalse(x) Py_Is(x, Py_False) +#endif + +// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +{ + ob->ob_type = type; +} + #define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) +#endif + +// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +{ + ob->ob_size = size; +} + #define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject *)(ob), size) +#endif + +// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) +static inline PyCodeObject *PyFrame_GetCode(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + assert(frame->f_code != _Py_NULL); + return _Py_CAST(PyCodeObject *, Py_NewRef(frame->f_code)); +} +#endif + +static inline PyCodeObject *_PyFrame_GetCodeBorrow(PyFrameObject *frame) +{ + PyCodeObject *code = PyFrame_GetCode(frame); + Py_DECREF(code); + return code; +} + +// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject *PyFrame_GetBack(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + return _Py_CAST(PyFrameObject *, Py_XNewRef(frame->f_back)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject *_PyFrame_GetBackBorrow(PyFrameObject *frame) +{ + PyFrameObject *back = PyFrame_GetBack(frame); + Py_XDECREF(back); + return back; +} +#endif + +// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject *PyFrame_GetLocals(PyFrameObject *frame) +{ + #if PY_VERSION_HEX >= 0x030400B1 + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } + #else + PyFrame_FastToLocals(frame); + #endif + return Py_NewRef(frame->f_locals); +} +#endif + +// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject *PyFrame_GetGlobals(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_globals); +} +#endif + +// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject *PyFrame_GetBuiltins(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_builtins); +} +#endif + +// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline int PyFrame_GetLasti(PyFrameObject *frame) +{ + #if PY_VERSION_HEX >= 0x030A00A7 + // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, + // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) + // instructions. + if (frame->f_lasti < 0) { + return -1; + } + return frame->f_lasti * 2; + #else + return frame->f_lasti; + #endif +} +#endif + +// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject *PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +{ + PyObject *locals, *value; + + locals = PyFrame_GetLocals(frame); + if (locals == NULL) { + return NULL; + } + #if PY_VERSION_HEX >= 0x03000000 + value = PyDict_GetItemWithError(locals, name); + #else + value = _PyDict_GetItemWithError(locals, name); + #endif + Py_DECREF(locals); + + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } + #if PY_VERSION_HEX >= 0x03000000 + PyErr_Format(PyExc_NameError, "variable %R does not exist", name); + #else + PyErr_SetString(PyExc_NameError, "variable does not exist"); + #endif + return NULL; + } + return Py_NewRef(value); +} +#endif + +// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject *PyFrame_GetVarString(PyFrameObject *frame, + const char *name) +{ + PyObject *name_obj, *value; + #if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(name); + #else + name_obj = PyString_FromString(name); + #endif + if (name_obj == NULL) { + return NULL; + } + value = PyFrame_GetVar(frame, name_obj); + Py_DECREF(name_obj); + return value; +} +#endif + +// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState * +PyThreadState_GetInterpreter(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->interp; +} +#endif + +// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject * +_PyThreadState_GetFrameBorrow(PyThreadState *tstate) +{ + PyFrameObject *frame = PyThreadState_GetFrame(tstate); + Py_XDECREF(frame); + return frame; +} +#endif + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState *PyInterpreterState_Get(void) +{ + PyThreadState *tstate; + PyInterpreterState *interp; + + tstate = PyThreadState_GET(); + if (tstate == _Py_NULL) { + Py_FatalError("GIL released (tstate is NULL)"); + } + interp = tstate->interp; + if (interp == _Py_NULL) { + Py_FatalError("no current interpreter"); + } + return interp; +} +#endif + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 +#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && \ + !defined(PYPY_VERSION) +static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->id; +} +#endif + +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_EnterTracing(PyThreadState *tstate) +{ + tstate->tracing++; + #if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = 0; + #else + tstate->use_tracing = 0; + #endif +} +#endif + +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) +{ + int use_tracing = + (tstate->c_tracefunc != _Py_NULL || tstate->c_profilefunc != _Py_NULL); + tstate->tracing--; + #if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = use_tracing; + #else + tstate->use_tracing = use_tracing; + #endif +} +#endif + +// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 +// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +static inline PyObject *PyObject_CallNoArgs(PyObject *func) +{ + return PyObject_CallFunctionObjArgs(func, NULL); +} +#endif + +// bpo-39245 made PyObject_CallOneArg() public (previously called +// _PyObject_CallOneArg) in Python 3.9.0a4 +// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +static inline PyObject *PyObject_CallOneArg(PyObject *func, PyObject *arg) +{ + return PyObject_CallFunctionObjArgs(func, arg, NULL); +} +#endif + +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 +static inline int PyModule_AddObjectRef(PyObject *module, const char *name, + PyObject *value) +{ + int res; + + if (!value && !PyErr_Occurred()) { + // PyModule_AddObject() raises TypeError in this case + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + return -1; + } + + Py_XINCREF(value); + res = PyModule_AddObject(module, name, value); + if (res < 0) { + Py_XDECREF(value); + } + return res; +} +#endif + +// bpo-40024 added PyModule_AddType() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 +static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +{ + const char *name, *dot; + + if (PyType_Ready(type) < 0) { + return -1; + } + + // inline _PyType_Name() + name = type->tp_name; + assert(name != _Py_NULL); + dot = strrchr(name, '.'); + if (dot != _Py_NULL) { + name = dot + 1; + } + + return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); +} +#endif + +// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. +// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. +#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsTracked(PyObject *obj) +{ + return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); +} +#endif + +// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. +// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. +#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && \ + !defined(PYPY_VERSION) +static inline int PyObject_GC_IsFinalized(PyObject *obj) +{ + PyGC_Head *gc = _Py_CAST(PyGC_Head *, obj) - 1; + return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); +} +#endif + +// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) +static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) +{ + return Py_TYPE(ob) == type; +} + #define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) +#endif + +// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal +// C API: Python 3.11a2-3.11a6 versions are not supported. +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && \ + !defined(PYPY_VERSION) +static inline int PyFloat_Pack2(double x, char *p, int le) +{ + return _PyFloat_Pack2(x, (unsigned char *)p, le); +} + +static inline double PyFloat_Unpack2(const char *p, int le) +{ + return _PyFloat_Unpack2((const unsigned char *)p, le); +} +#endif + +// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and +// PyFloat_Unpack8() to Python 3.11a7. +// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() +// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions +// are not supported. +#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack4(double x, char *p, int le) +{ + return _PyFloat_Pack4(x, (unsigned char *)p, le); +} + +static inline int PyFloat_Pack8(double x, char *p, int le) +{ + return _PyFloat_Pack8(x, (unsigned char *)p, le); +} + +static inline double PyFloat_Unpack4(const char *p, int le) +{ + return _PyFloat_Unpack4((const unsigned char *)p, le); +} + +static inline double PyFloat_Unpack8(const char *p, int le) +{ + return _PyFloat_Unpack8((const unsigned char *)p, le); +} +#endif + +// gh-92154 added PyCode_GetCode() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline PyObject *PyCode_GetCode(PyCodeObject *code) +{ + return Py_NewRef(code->co_code); +} +#endif + +// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject *PyCode_GetVarnames(PyCodeObject *code) +{ + return Py_NewRef(code->co_varnames); +} +#endif + +// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject *PyCode_GetFreevars(PyCodeObject *code) +{ + return Py_NewRef(code->co_freevars); +} +#endif + +// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject *PyCode_GetCellvars(PyCodeObject *code) +{ + return Py_NewRef(code->co_cellvars); +} +#endif + +// Py_UNUSED() was added to Python 3.4.0b2. +#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) + #if defined(__GNUC__) || defined(__clang__) + #define Py_UNUSED(name) _unused_##name __attribute__((unused)) + #else + #define Py_UNUSED(name) _unused_##name + #endif +#endif + +// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A0 +static inline PyObject *PyImport_AddModuleRef(const char *name) +{ + return Py_XNewRef(PyImport_AddModule(name)); +} +#endif + +// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D0000 +static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) +{ + PyObject *obj; + if (ref != NULL && !PyWeakref_Check(ref)) { + *pobj = NULL; + PyErr_SetString(PyExc_TypeError, "expected a weakref"); + return -1; + } + obj = PyWeakref_GetObject(ref); + if (obj == NULL) { + // SystemError if ref is NULL + *pobj = NULL; + return -1; + } + if (obj == Py_None) { + *pobj = NULL; + return 0; + } + *pobj = Py_NewRef(obj); + return (*pobj != NULL); +} +#endif + +// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 +#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET + #define PY_VECTORCALL_ARGUMENTS_OFFSET \ + (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) +#endif + +// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 +#if PY_VERSION_HEX < 0x030800B1 +static inline Py_ssize_t PyVectorcall_NARGS(size_t n) +{ + return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; +} +#endif + +// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 +static inline PyObject *PyObject_Vectorcall(PyObject *callable, + PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + #if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) + // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 + return _PyObject_Vectorcall(callable, args, nargsf, kwnames); + #else + PyObject *posargs = NULL, *kwargs = NULL; + PyObject *res; + Py_ssize_t nposargs, nkwargs, i; + + if (nargsf != 0 && args == NULL) { + PyErr_BadInternalCall(); + goto error; + } + if (kwnames != NULL && !PyTuple_Check(kwnames)) { + PyErr_BadInternalCall(); + goto error; + } + + nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); + if (kwnames) { + nkwargs = PyTuple_GET_SIZE(kwnames); + } + else { + nkwargs = 0; + } + + posargs = PyTuple_New(nposargs); + if (posargs == NULL) { + goto error; + } + if (nposargs) { + for (i = 0; i < nposargs; i++) { + PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); + args++; + } + } + + if (nkwargs) { + kwargs = PyDict_New(); + if (kwargs == NULL) { + goto error; + } + + for (i = 0; i < nkwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = *args; + args++; + if (PyDict_SetItem(kwargs, key, value) < 0) { + goto error; + } + } + } + else { + kwargs = NULL; + } + + res = PyObject_Call(callable, posargs, kwargs); + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return res; + +error: + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return NULL; + #endif +} +#endif + +// gh-106521 added PyObject_GetOptionalAttr() and +// PyObject_GetOptionalAttrString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, + PyObject **result) +{ + // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 + #if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) + return _PyObject_LookupAttr(obj, attr_name, result); + #else + *result = PyObject_GetAttr(obj, attr_name); + if (*result != NULL) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; + #endif +} + +static inline int PyObject_GetOptionalAttrString(PyObject *obj, + const char *attr_name, + PyObject **result) +{ + PyObject *name_obj; + int rc; + #if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(attr_name); + #else + name_obj = PyString_FromString(attr_name); + #endif + if (name_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyObject_GetOptionalAttr(obj, name_obj, result); + Py_DECREF(name_obj); + return rc; +} +#endif + +// gh-106307 added PyObject_GetOptionalAttr() and +// PyMapping_GetOptionalItemString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, + PyObject **result) +{ + *result = PyObject_GetItem(obj, key); + if (*result) { + return 1; + } + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; +} + +static inline int PyMapping_GetOptionalItemString(PyObject *obj, + const char *key, + PyObject **result) +{ + PyObject *key_obj; + int rc; + #if PY_VERSION_HEX >= 0x03000000 + key_obj = PyUnicode_FromString(key); + #else + key_obj = PyString_FromString(key); + #endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyMapping_GetOptionalItem(obj, key_obj, result); + Py_DECREF(key_obj); + return rc; +} +#endif + +// gh-108511 added PyMapping_HasKeyWithError() and +// PyMapping_HasKeyStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItem(obj, key, &res); + Py_XDECREF(res); + return rc; +} + +static inline int PyMapping_HasKeyStringWithError(PyObject *obj, + const char *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItemString(obj, key, &res); + Py_XDECREF(res); + return rc; +} +#endif + +// gh-108511 added PyObject_HasAttrWithError() and +// PyObject_HasAttrStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttr(obj, attr, &res); + Py_XDECREF(res); + return rc; +} + +static inline int PyObject_HasAttrStringWithError(PyObject *obj, + const char *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttrString(obj, attr, &res); + Py_XDECREF(res); + return rc; +} +#endif + +// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_GetItemRef(PyObject *mp, PyObject *key, + PyObject **result) +{ + #if PY_VERSION_HEX >= 0x03000000 + PyObject *item = PyDict_GetItemWithError(mp, key); + #else + PyObject *item = _PyDict_GetItemWithError(mp, key); + #endif + if (item != NULL) { + *result = Py_NewRef(item); + return 1; // found + } + if (!PyErr_Occurred()) { + *result = NULL; + return 0; // not found + } + *result = NULL; + return -1; +} + +static inline int PyDict_GetItemStringRef(PyObject *mp, const char *key, + PyObject **result) +{ + int res; + #if PY_VERSION_HEX >= 0x03000000 + PyObject *key_obj = PyUnicode_FromString(key); + #else + PyObject *key_obj = PyString_FromString(key); + #endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + res = PyDict_GetItemRef(mp, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + +// gh-106307 added PyModule_Add() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + +// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 +// bpo-1856 added _Py_Finalizing to Python 3.2.1b1. +// _Py_IsFinalizing() was added to PyPy 7.3.0. +#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) && \ + (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +static inline int Py_IsFinalizing(void) +{ + #if PY_VERSION_HEX >= 0x030700A1 + // _Py_IsFinalizing() was added to Python 3.7.0a1. + return _Py_IsFinalizing(); + #else + return (_Py_Finalizing != NULL); + #endif +} +#endif + +// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_ContainsString(PyObject *op, const char *key) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + return -1; + } + int res = PyDict_Contains(op, key_obj); + Py_DECREF(key_obj); + return res; +} +#endif + +// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyLong_AsInt(PyObject *obj) +{ + #ifdef PYPY_VERSION + long value = PyLong_AsLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + if (value < (long)INT_MIN || (long)INT_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)value; + #else + return _PyLong_AsInt(obj); + #endif +} +#endif + +// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, + void *arg) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return -1; + } + Py_VISIT(*dict); + return 0; +} + +static inline void PyObject_ClearManagedDict(PyObject *obj) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return; + } + Py_CLEAR(*dict); +} +#endif + +// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 +// Python 3.5.2 added _PyThreadState_UncheckedGet(). +#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 +static inline PyThreadState *PyThreadState_GetUnchecked(void) +{ + return _PyThreadState_UncheckedGet(); +} +#endif + +// gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, + const char *str, + Py_ssize_t str_len) +{ + Py_ssize_t len; + const void *utf8; + PyObject *exc_type, *exc_value, *exc_tb; + int res; + + // API cannot report errors so save/restore the exception + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() + #if PY_VERSION_HEX >= 0x030300A1 + if (PyUnicode_IS_ASCII(unicode)) { + utf8 = PyUnicode_DATA(unicode); + len = PyUnicode_GET_LENGTH(unicode); + } + else { + utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); + if (utf8 == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + } + + if (len != str_len) { + res = 0; + goto done; + } + res = (memcmp(utf8, str, (size_t)len) == 0); + #else + PyObject *bytes = PyUnicode_AsUTF8String(unicode); + if (bytes == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + + #if PY_VERSION_HEX >= 0x03000000 + len = PyBytes_GET_SIZE(bytes); + utf8 = PyBytes_AS_STRING(bytes); + #else + len = PyString_GET_SIZE(bytes); + utf8 = PyString_AS_STRING(bytes); + #endif + if (len != str_len) { + Py_DECREF(bytes); + res = 0; + goto done; + } + + res = (memcmp(utf8, str, (size_t)len) == 0); + Py_DECREF(bytes); + #endif + +done: + PyErr_Restore(exc_type, exc_value, exc_tb); + return res; +} + +static inline int PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) +{ + return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str)); +} +#endif + +// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int PyList_Extend(PyObject *list, PyObject *iterable) +{ + return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable); +} + +static inline int PyList_Clear(PyObject *list) +{ + return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); +} +#endif + +// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) +{ + PyObject *value; + + if (!PyDict_Check(dict)) { + PyErr_BadInternalCall(); + if (result) { + *result = NULL; + } + return -1; + } + + // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. + // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. + // Python 3.13.0a1 removed _PyDict_Pop(). + #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || \ + PY_VERSION_HEX >= 0x030D0000 + value = PyObject_CallMethod(dict, "pop", "O", key); + #elif PY_VERSION_HEX < 0x030600b3 + value = _PyDict_Pop(_Py_CAST(PyDictObject *, dict), key, NULL); + #else + value = _PyDict_Pop(dict, key, NULL); + #endif + if (value == NULL) { + if (result) { + *result = NULL; + } + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; + } + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; +} + +static inline int PyDict_PopString(PyObject *dict, const char *key, + PyObject **result) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + if (result != NULL) { + *result = NULL; + } + return -1; + } + + int res = PyDict_Pop(dict, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + +#if PY_VERSION_HEX < 0x030200A4 +// Python 3.2.0a4 added Py_hash_t type +typedef Py_ssize_t Py_hash_t; +#endif + +// gh-111545 added Py_HashPointer() to Python 3.13.0a3 +#if PY_VERSION_HEX < 0x030D00A3 +static inline Py_hash_t Py_HashPointer(const void *ptr) +{ + #if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) + return _Py_HashPointer(ptr); + #else + return _Py_HashPointer(_Py_CAST(void *, ptr)); + #endif +} +#endif + +// Python 3.13a4 added a PyTime API. +// Use the private API added to Python 3.5. +#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 +typedef _PyTime_t PyTime_t; + #define PyTime_MIN _PyTime_MIN + #define PyTime_MAX _PyTime_MAX + +static inline double PyTime_AsSecondsDouble(PyTime_t t) +{ + return _PyTime_AsSecondsDouble(t); +} + +static inline int PyTime_Monotonic(PyTime_t *result) +{ + return _PyTime_GetMonotonicClockWithInfo(result, NULL); +} + +static inline int PyTime_Time(PyTime_t *result) +{ + return _PyTime_GetSystemClockWithInfo(result, NULL); +} + +static inline int PyTime_PerfCounter(PyTime_t *result) +{ + #if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) + return _PyTime_GetPerfCounterWithInfo(result, NULL); + #elif PY_VERSION_HEX >= 0x03070000 + // Call time.perf_counter_ns() and convert Python int object to PyTime_t. + // Cache time.perf_counter_ns() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter_ns"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + long long value = PyLong_AsLongLong(res); + Py_DECREF(res); + + if (value == -1 && PyErr_Occurred()) { + return -1; + } + + Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t)); + *result = (PyTime_t)value; + return 0; + #else + // Call time.perf_counter() and convert C double to PyTime_t. + // Cache time.perf_counter() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + double d = PyFloat_AsDouble(res); + Py_DECREF(res); + + if (d == -1.0 && PyErr_Occurred()) { + return -1; + } + + // Avoid floor() to avoid having to link to libm + *result = (PyTime_t)(d * 1e9); + return 0; + #endif +} + +#endif + +// gh-111389 added hash constants to Python 3.13.0a5. These constants were +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9. +#if (!defined(PyHASH_BITS) && \ + ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) || \ + (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 && \ + PYPY_VERSION_NUM >= 0x07090000))) + #define PyHASH_BITS _PyHASH_BITS + #define PyHASH_MODULUS _PyHASH_MODULUS + #define PyHASH_INF _PyHASH_INF + #define PyHASH_IMAG _PyHASH_IMAG +#endif + +// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() +// to Python 3.13.0a6 +#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) + + #define Py_CONSTANT_NONE 0 + #define Py_CONSTANT_FALSE 1 + #define Py_CONSTANT_TRUE 2 + #define Py_CONSTANT_ELLIPSIS 3 + #define Py_CONSTANT_NOT_IMPLEMENTED 4 + #define Py_CONSTANT_ZERO 5 + #define Py_CONSTANT_ONE 6 + #define Py_CONSTANT_EMPTY_STR 7 + #define Py_CONSTANT_EMPTY_BYTES 8 + #define Py_CONSTANT_EMPTY_TUPLE 9 + +static inline PyObject *Py_GetConstant(unsigned int constant_id) +{ + static PyObject *constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; + + if (constants[Py_CONSTANT_NONE] == NULL) { + constants[Py_CONSTANT_NONE] = Py_None; + constants[Py_CONSTANT_FALSE] = Py_False; + constants[Py_CONSTANT_TRUE] = Py_True; + constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis; + constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented; + + constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0); + if (constants[Py_CONSTANT_ZERO] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_ONE] = PyLong_FromLong(1); + if (constants[Py_CONSTANT_ONE] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_STR] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0); + if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) { + goto fatal_error; + } + // goto dance to avoid compiler warnings about Py_FatalError() + goto init_done; + + fatal_error: + // This case should never happen + Py_FatalError("Py_GetConstant() failed to get constants"); + } + +init_done: + if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { + return Py_NewRef(constants[constant_id]); + } + else { + PyErr_BadInternalCall(); + return NULL; + } +} + +static inline PyObject *Py_GetConstantBorrowed(unsigned int constant_id) +{ + PyObject *obj = Py_GetConstant(constant_id); + Py_XDECREF(obj); + return obj; +} +#endif + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline PyObject *PyList_GetItemRef(PyObject *op, Py_ssize_t index) +{ + PyObject *item = PyList_GetItem(op, index); + Py_XINCREF(item); + return item; +} +#endif + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline int PyDict_SetDefaultRef(PyObject *d, PyObject *key, + PyObject *default_value, + PyObject **result) +{ + PyObject *value; + if (PyDict_GetItemRef(d, key, &value) < 0) { + // get error + if (result) { + *result = NULL; + } + return -1; + } + if (value != NULL) { + // present + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; + } + + // missing: set the item + if (PyDict_SetItem(d, key, default_value) < 0) { + // set error + if (result) { + *result = NULL; + } + return -1; + } + if (result) { + *result = Py_NewRef(default_value); + } + return 0; +} +#endif + +#if PY_VERSION_HEX < 0x030D00B3 + #define Py_BEGIN_CRITICAL_SECTION(op) { + #define Py_END_CRITICAL_SECTION() } + #define Py_BEGIN_CRITICAL_SECTION2(a, b) { + #define Py_END_CRITICAL_SECTION2() } +#endif + +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && \ + !defined(PYPY_VERSION) +typedef struct PyUnicodeWriter PyUnicodeWriter; + +static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter *)writer); + PyMem_Free(writer); +} + +static inline PyUnicodeWriter *PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == _Py_NULL) { + PyErr_NoMemory(); + return _Py_NULL; + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + return pub_writer; +} + +static inline PyObject *PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter *)writer); + assert(((_PyUnicodeWriter *)writer)->buffer == NULL); + PyMem_Free(writer); + return str; +} + +static inline int PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter *)writer, ch); +} + +static inline int PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, + PyObject *obj) +{ + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str); + Py_DECREF(str); + return res; +} + +static inline int PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, + PyObject *obj) +{ + PyObject *str = PyObject_Repr(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str); + Py_DECREF(str); + return res; +} + +static inline int PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, + const wchar_t *str, + Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)wcslen(str); + } + + PyObject *str_obj = PyUnicode_FromWideChar(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, + PyObject *str, + Py_ssize_t start, + Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %T", str); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter *)writer, str, + start, end); +} + +static inline int PyUnicodeWriter_Format(PyUnicodeWriter *writer, + const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyUnicode_FromFormatV(format, vargs); + va_end(vargs); + if (str == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030E0000 + +// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyLong_GetSign(PyObject *obj, int *sign) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expect int, got %s", + Py_TYPE(obj)->tp_name); + return -1; + } + + *sign = _PyLong_Sign(obj); + return 0; +} +#endif + +// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) +{ + if (!PyUnicode_Check(str1)) { + PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", + Py_TYPE(str1)->tp_name); + return -1; + } + if (!PyUnicode_Check(str2)) { + PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", + Py_TYPE(str2)->tp_name); + return -1; + } + + #if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) + PyAPI_FUNC(int) _PyUnicode_Equal(PyObject * str1, PyObject * str2); + + return _PyUnicode_Equal(str1, str2); + #elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); + #elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); + #else + return (PyUnicode_Compare(str1, str2) == 0); + #endif +} +#endif + +// gh-121645 added PyBytes_Join() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject *PyBytes_Join(PyObject *sep, PyObject *iterable) +{ + return _PyBytes_Join(sep, iterable); +} +#endif + +#if PY_VERSION_HEX < 0x030E00A0 +static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) +{ + #if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) + PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); + + return _Py_HashBytes(ptr, len); + #else + Py_hash_t hash; + PyObject *bytes = PyBytes_FromStringAndSize((const char *)ptr, len); + if (bytes == NULL) { + return -1; + } + hash = PyObject_Hash(bytes); + Py_DECREF(bytes); + return hash; + #endif +} +#endif + +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyIter_NextItem(PyObject *iter, PyObject **item) +{ + iternextfunc tp_iternext; + + assert(iter != NULL); + assert(item != NULL); + + tp_iternext = Py_TYPE(iter)->tp_iternext; + if (tp_iternext == NULL) { + *item = NULL; + PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", + Py_TYPE(iter)->tp_name); + return -1; + } + + if ((*item = tp_iternext(iter))) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + return 0; + } + return -1; +} +#endif + +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject *PyLong_FromInt32(int32_t value) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + return PyLong_FromLong(value); +} + +static inline PyObject *PyLong_FromInt64(int64_t value) +{ + Py_BUILD_ASSERT(sizeof(long long) >= 8); + return PyLong_FromLongLong(value); +} + +static inline PyObject *PyLong_FromUInt32(uint32_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); + return PyLong_FromUnsignedLong(value); +} + +static inline PyObject *PyLong_FromUInt64(uint64_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); + return PyLong_FromUnsignedLongLong(value); +} + +static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(int) == 4); + int value = PyLong_AsInt(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int32_t)value; + return 0; +} + +static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + long long value = PyLong_AsLongLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int64_t)value; + return 0; +} + +static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + unsigned long value = PyLong_AsUnsignedLong(obj); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return -1; + } + #if SIZEOF_LONG > 4 + if ((unsigned long)UINT32_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint32_t"); + return -1; + } + #endif + *pvalue = (uint32_t)value; + return 0; +} + +static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + unsigned long long value = PyLong_AsUnsignedLongLong(obj); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (uint64_t)value; + return 0; +} +#endif + +#ifdef __cplusplus +} +#endif +#endif // PYTHONCAPI_COMPAT diff --git a/src/main/client/admin.c b/src/main/client/admin.c index 9f2332d563..9a0c6ca843 100644 --- a/src/main/client/admin.c +++ b/src/main/client/admin.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -610,7 +612,7 @@ PyObject *AerospikeClient_Admin_Revoke_Roles(AerospikeClient *self, goto CLEANUP; } - if (py_policy == Py_None) { + if (Py_IsNone(py_policy)) { py_policy = PyDict_New(); } @@ -1258,7 +1260,7 @@ PyObject *AerospikeClient_Admin_Set_Whitelist(AerospikeClient *self, goto CLEANUP; } } - else if (py_whitelist == Py_None) { + else if (Py_IsNone(py_whitelist)) { whitelist_size = 0; } else { diff --git a/src/main/client/batch_operate.c b/src/main/client/batch_operate.c index bad88fea04..40294bf24b 100644 --- a/src/main/client/batch_operate.c +++ b/src/main/client/batch_operate.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2022 Aerospike, Inc. * @@ -224,7 +226,7 @@ static PyObject *AerospikeClient_Batch_Operate_Invoke( } } - if (py_ttl == NULL || py_ttl == Py_None) { + if (py_ttl == NULL || Py_IsNone(py_ttl)) { // If ttl in this transaction's batch write policy isn't set, use the client config's default batch write // policy ttl ops.ttl = AS_RECORD_CLIENT_DEFAULT_TTL; @@ -373,7 +375,7 @@ PyObject *AerospikeClient_Batch_Operate(AerospikeClient *self, PyObject *args, goto error; } - if (py_ttl && py_ttl != Py_None && !PyLong_Check(py_ttl)) { + if (py_ttl && !Py_IsNone(py_ttl) && !PyLong_Check(py_ttl)) { as_error_update(&err, AEROSPIKE_ERR_PARAM, "ttl should be an integer"); goto error; } diff --git a/src/main/client/batch_write.c b/src/main/client/batch_write.c index 5408be4ea5..3c7f4f9705 100644 --- a/src/main/client/batch_write.c +++ b/src/main/client/batch_write.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2020 Aerospike, Inc. * @@ -39,7 +41,7 @@ { \ PyObject *py___policy = \ PyObject_GetAttrString(py_batch_record, FIELD_NAME_BATCH_POLICY); \ - if (py___policy != Py_None) { \ + if (!Py_IsNone(py___policy)) { \ as_exp *expr = NULL; \ as_exp *expr_p = expr; \ if (py___policy != NULL) { \ @@ -239,7 +241,7 @@ static PyObject *AerospikeClient_BatchWriteInvoke(AerospikeClient *self, !PyList_Size(py_ops_list)) { // batch Read can have None ops if it is using read_all_bins - if ((batch_type == BATCH_TYPE_READ && py_ops_list != Py_None) || + if ((batch_type == BATCH_TYPE_READ && !Py_IsNone(py_ops_list)) || batch_type == BATCH_TYPE_WRITE) { as_error_update(err, AEROSPIKE_ERR_PARAM, "py_ops_list is NULL or not a list, %s must be " @@ -263,7 +265,7 @@ static PyObject *AerospikeClient_BatchWriteInvoke(AerospikeClient *self, } Py_ssize_t py_ops_size = 0; - if (py_ops_list != NULL && py_ops_list != Py_None) { + if (py_ops_list != NULL && !Py_IsNone(py_ops_list)) { py_ops_size = PyList_Size(py_ops_list); } @@ -272,7 +274,7 @@ static PyObject *AerospikeClient_BatchWriteInvoke(AerospikeClient *self, as_operations *ops = NULL; if ((batch_type == AS_BATCH_READ || batch_type == AS_BATCH_WRITE) && - (py_ops_size || (py_meta != NULL && py_meta != Py_None))) { + (py_ops_size || (py_meta != NULL && !Py_IsNone(py_meta)))) { ops = as_operations_new(py_ops_size); garb->ops_to_free = ops; diff --git a/src/main/client/cdt_operation_utils.c b/src/main/client/cdt_operation_utils.c index d08bed4ab5..bc09f77eb4 100644 --- a/src/main/client/cdt_operation_utils.c +++ b/src/main/client/cdt_operation_utils.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + #include #include "cdt_operation_utils.h" @@ -83,7 +85,7 @@ as_status get_asval(AerospikeClient *self, as_error *err, char *key, } /* If the value isn't required, None indicates that it isn't provided */ - if (py_val == Py_None && !required) { + if (Py_IsNone(py_val) && !required) { *val = NULL; return AEROSPIKE_OK; } diff --git a/src/main/client/operate.c b/src/main/client/operate.c index c08a1c0d80..b54a17eab0 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -174,7 +174,7 @@ PyObject *create_pylist(PyObject *py_list, long operation, PyObject *py_bin, int check_type(AerospikeClient *self, PyObject *py_value, int op, as_error *err) { if ((!PyLong_Check(py_value) && - strcmp(py_value->ob_type->tp_name, "aerospike.null")) && + strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) && (op == AS_OPERATOR_TOUCH)) { as_error_update( err, AEROSPIKE_ERR_PARAM, @@ -182,7 +182,7 @@ int check_type(AerospikeClient *self, PyObject *py_value, int op, as_error *err) return 1; } else if ((!PyLong_Check(py_value) && (!PyFloat_Check(py_value)) && - strcmp(py_value->ob_type->tp_name, "aerospike.null")) && + strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) && op == AS_OPERATOR_INCR) { as_error_update( err, AEROSPIKE_ERR_PARAM, @@ -191,7 +191,7 @@ int check_type(AerospikeClient *self, PyObject *py_value, int op, as_error *err) } else if ((!PyUnicode_Check(py_value) && !PyByteArray_Check(py_value) && !PyBytes_Check(py_value) && - strcmp(py_value->ob_type->tp_name, "aerospike.null")) && + strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) && (op == AS_OPERATOR_APPEND || op == AS_OPERATOR_PREPEND)) { as_error_update(err, AEROSPIKE_ERR_PARAM, "Cannot concatenate 'str' and 'non-str' objects"); @@ -572,7 +572,7 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, } else { if (!self->strict_types || - !strcmp(py_value->ob_type->tp_name, "aerospike.null")) { + !strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) { as_operations *pointer_ops = ops; as_binop *binop = &pointer_ops->binops.entries[pointer_ops->binops.size++]; @@ -605,7 +605,7 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, } else { if (!self->strict_types || - !strcmp(py_value->ob_type->tp_name, "aerospike.null")) { + !strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) { as_operations *pointer_ops = ops; as_binop *binop = &pointer_ops->binops.entries[pointer_ops->binops.size++]; @@ -632,7 +632,7 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, } else { if (!self->strict_types || - !strcmp(py_value->ob_type->tp_name, "aerospike.null")) { + !strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) { as_operations *pointer_ops = ops; as_binop *binop = &pointer_ops->binops.entries[pointer_ops->binops.size++]; diff --git a/src/main/client/query.c b/src/main/client/query.c index c5243c8816..94b88c2161 100644 --- a/src/main/client/query.c +++ b/src/main/client/query.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -171,7 +173,7 @@ static int query_where_add(as_query **query, as_predicate_type predicate, return 1; } - if (py_val1 == Py_None || py_val2 == Py_None) { + if (Py_IsNone(py_val1) || Py_IsNone(py_val2)) { Py_XDECREF(py_ubin); as_error_update( err, AEROSPIKE_ERR_PARAM, diff --git a/src/main/client/sec_index.c b/src/main/client/sec_index.c index 0276148b6d..b4f6c43052 100644 --- a/src/main/client/sec_index.c +++ b/src/main/client/sec_index.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -580,7 +582,7 @@ static PyObject *createIndexWithDataAndCollectionType( py_ustr_set = PyUnicode_AsUTF8String(py_set); set_ptr = PyBytes_AsString(py_ustr_set); } - else if (py_set != Py_None) { + else if (!Py_IsNone(py_set)) { as_error_update(&err, AEROSPIKE_ERR_PARAM, "Set should be string, unicode or None"); goto CLEANUP; diff --git a/src/main/client/set_xdr_filter.c b/src/main/client/set_xdr_filter.c index 9c5723889b..d0bb06ba1d 100644 --- a/src/main/client/set_xdr_filter.c +++ b/src/main/client/set_xdr_filter.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -91,7 +93,7 @@ PyObject *AerospikeClient_SetXDRFilter(AerospikeClient *self, PyObject *args, } //convert filter to base64 - if (py_expression_filter == Py_None) { + if (Py_IsNone(py_expression_filter)) { base64_filter = (char *)DELETE_CURRENT_XDR_FILTER; } else { diff --git a/src/main/client/truncate.c b/src/main/client/truncate.c index 8af0d7149c..8dcd81cc83 100644 --- a/src/main/client/truncate.c +++ b/src/main/client/truncate.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -108,7 +110,7 @@ PyObject *AerospikeClient_Truncate(AerospikeClient *self, PyObject *args, goto CLEANUP; } } - else if (py_set != Py_None) { + else if (!Py_IsNone(py_set)) { // If the set is none, this is fine as_error_update(&err, AEROSPIKE_ERR_PARAM, "Set must be None, or unicode or string type"); diff --git a/src/main/client/type.c b/src/main/client/type.c index ccd174b77f..1d7a2b3452 100644 --- a/src/main/client/type.c +++ b/src/main/client/type.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -733,7 +735,7 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyDict_GetItemString(py_config, "serialization"); if (py_serializer_option && PyTuple_Check(py_serializer_option)) { PyObject *py_serializer = PyTuple_GetItem(py_serializer_option, 0); - if (py_serializer && py_serializer != Py_None) { + if (py_serializer && !Py_IsNone(py_serializer)) { if (!PyCallable_Check(py_serializer)) { error_code = INIT_SERIALIZE_ERR; goto CONSTRUCTOR_ERROR; @@ -743,7 +745,7 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, self->user_serializer_call_info.callback = py_serializer; } PyObject *py_deserializer = PyTuple_GetItem(py_serializer_option, 1); - if (py_deserializer && py_deserializer != Py_None) { + if (py_deserializer && !Py_IsNone(py_deserializer)) { if (!PyCallable_Check(py_deserializer)) { error_code = INIT_DESERIALIZE_ERR; goto CONSTRUCTOR_ERROR; @@ -1282,7 +1284,7 @@ static void AerospikeClient_Type_Dealloc(PyObject *self) } } } - self->ob_type->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } /******************************************************************************* diff --git a/src/main/conversions.c b/src/main/conversions.c index 3cfb750581..46295ad21d 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -1065,13 +1067,13 @@ bool is_pyobj_correct_as_helpers_type(PyObject *obj, const char *expected_submodule_name, const char *expected_type_name) { - if (strcmp(obj->ob_type->tp_name, expected_type_name)) { + if (strcmp(Py_TYPE(obj)->tp_name, expected_type_name)) { // Expected class name does not match object's class name return false; } PyObject *py_module_name = - PyDict_GetItemString(obj->ob_type->tp_dict, "__module__"); + PyDict_GetItemString(Py_TYPE(obj)->tp_dict, "__module__"); if (!py_module_name) { // Class does not belong to any module return false; @@ -1195,7 +1197,7 @@ as_status pyobject_to_val(AerospikeClient *self, as_error *err, bytes->type = AS_BYTES_HLL; } } - else if (!strcmp(py_obj->ob_type->tp_name, "aerospike.Geospatial")) { + else if (!strcmp(Py_TYPE(py_obj)->tp_name, "aerospike.Geospatial")) { PyObject *py_parameter = PyUnicode_FromString("geo_data"); PyObject *py_data = PyObject_GenericGetAttr(py_obj, py_parameter); Py_DECREF(py_parameter); @@ -1236,7 +1238,7 @@ as_status pyobject_to_val(AerospikeClient *self, as_error *err, else if (Py_None == py_obj) { *val = as_val_reserve(&as_nil); } - else if (!strcmp(py_obj->ob_type->tp_name, "aerospike.null")) { + else if (!strcmp(Py_TYPE(py_obj)->tp_name, "aerospike.null")) { *val = (as_val *)as_val_reserve(&as_nil); } else if (AS_Matches_Classname(py_obj, AS_CDT_WILDCARD_NAME)) { @@ -1325,7 +1327,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, value)) { //TODO Change to true bool support post jump version. switch (self->send_bool_as) { case SEND_BOOL_AS_AS_BOOL:; - bool converted_value = (value == Py_True); + bool converted_value = (Py_IsTrue(value)); ret_val = as_record_set_bool(rec, name, converted_value); break; case SEND_BOOL_AS_INTEGER:; @@ -1353,7 +1355,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, } ret_val = as_record_set_int64(rec, name, val); } - else if (!strcmp(value->ob_type->tp_name, "aerospike.Geospatial")) { + else if (!strcmp(Py_TYPE(value)->tp_name, "aerospike.Geospatial")) { PyObject *py_geo_string = PyUnicode_FromString("geo_data"); PyObject *py_data = PyObject_GenericGetAttr(value, py_geo_string); @@ -1436,7 +1438,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, } ret_val = as_record_set_map(rec, name, map); } - else if (!strcmp(value->ob_type->tp_name, "aerospike.null")) { + else if (!strcmp(Py_TYPE(value)->tp_name, "aerospike.null")) { ret_val = as_record_set_nil(rec, name); } else { @@ -1467,7 +1469,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, } Py_END_CRITICAL_SECTION(); - if (py_meta && py_meta != Py_None) { + if (py_meta && !Py_IsNone(py_meta)) { if (!PyDict_Check(py_meta)) { as_error_update(err, AEROSPIKE_ERR_PARAM, "meta must be a dictionary"); @@ -1586,7 +1588,7 @@ as_status pyobject_to_key(as_error *err, PyObject *py_keytuple, as_key *key) ns = (char *)PyUnicode_AsUTF8(py_ns); } - if (py_set && py_set != Py_None) { + if (py_set && !Py_IsNone(py_set)) { if (PyUnicode_Check(py_set)) { set = (char *)PyUnicode_AsUTF8(py_set); } @@ -1598,7 +1600,7 @@ as_status pyobject_to_key(as_error *err, PyObject *py_keytuple, as_key *key) as_key *returnResult = key; - if (py_key && py_key != Py_None) { + if (py_key && !Py_IsNone(py_key)) { if (PyUnicode_Check(py_key)) { PyObject *py_ustr = PyUnicode_AsUTF8String(py_key); char *k = PyBytes_AsString(py_ustr); @@ -1638,7 +1640,7 @@ as_status pyobject_to_key(as_error *err, PyObject *py_keytuple, as_key *key) as_error_update(err, AEROSPIKE_ERR_PARAM, "key is invalid"); } } - else if (py_digest && py_digest != Py_None) { + else if (py_digest && !Py_IsNone(py_digest)) { if (PyByteArray_Check(py_digest)) { uint32_t sz = (uint32_t)PyByteArray_Size(py_digest); @@ -2462,7 +2464,7 @@ void initialize_bin_for_strictypes(AerospikeClient *self, as_error *err, ((as_val *)&binop_bin->value)->type = AS_UNKNOWN; binop_bin->valuep = (as_bin_value *)map; } - else if (!strcmp(py_value->ob_type->tp_name, "aerospike.Geospatial")) { + else if (!strcmp(Py_TYPE(py_value)->tp_name, "aerospike.Geospatial")) { PyObject *geo_data = PyObject_GetAttrString(py_value, "geo_data"); PyObject *geo_data_py_str = AerospikeGeospatial_DoDumps(geo_data, err); const char *geo_data_str = PyUnicode_AsUTF8(geo_data_py_str); @@ -2479,7 +2481,7 @@ void initialize_bin_for_strictypes(AerospikeClient *self, as_error *err, Py_XDECREF(geo_data_py_str); Py_XDECREF(geo_data); } - else if (!strcmp(py_value->ob_type->tp_name, "aerospike.null")) { + else if (!strcmp(Py_TYPE(py_value)->tp_name, "aerospike.null")) { ((as_val *)&binop_bin->value)->type = AS_UNKNOWN; binop_bin->valuep = (as_bin_value *)&as_nil; } @@ -2597,7 +2599,7 @@ as_status check_and_set_meta(PyObject *py_meta, as_operations *ops, ops->gen = gen; } } - else if (py_meta && (py_meta != Py_None)) { + else if (py_meta && (!Py_IsNone(py_meta))) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Metadata should be of type dictionary"); } diff --git a/src/main/convert_expressions.c b/src/main/convert_expressions.c index 820165d98b..896e23639c 100644 --- a/src/main/convert_expressions.c +++ b/src/main/convert_expressions.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2020 Aerospike, Inc. * @@ -500,7 +502,7 @@ get_exp_val_from_pyval(AerospikeClient *self, as_static_pool *static_pool, as_exp_entry tmp_entry = as_exp_bytes(b, b_len); *new_entry = tmp_entry; } - else if (!strcmp(py_obj->ob_type->tp_name, "aerospike.Geospatial")) { + else if (!strcmp(Py_TYPE(py_obj)->tp_name, "aerospike.Geospatial")) { PyObject *py_parameter = PyUnicode_FromString("geo_data"); PyObject *py_data = PyObject_GenericGetAttr(py_obj, py_parameter); Py_DECREF(py_parameter); @@ -550,7 +552,7 @@ get_exp_val_from_pyval(AerospikeClient *self, as_static_pool *static_pool, as_exp_entry tmp_entry = as_exp_nil(); *new_entry = tmp_entry; } - else if (!strcmp(py_obj->ob_type->tp_name, "aerospike.null")) { + else if (!strcmp(Py_TYPE(py_obj)->tp_name, "aerospike.null")) { as_exp_entry tmp_entry = as_exp_nil(); *new_entry = tmp_entry; } @@ -1692,7 +1694,7 @@ as_status convert_exp_list(AerospikeClient *self, PyObject *py_exp_list, } PyObject *rt_tmp = PyTuple_GetItem(py_expr_tuple, 1); - if (rt_tmp != Py_None) { + if (!Py_IsNone(rt_tmp)) { temp_expr.result_type = PyLong_AsLong(rt_tmp); if (temp_expr.result_type == -1 && PyErr_Occurred()) { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -1703,7 +1705,7 @@ as_status convert_exp_list(AerospikeClient *self, PyObject *py_exp_list, } temp_expr.pydict = PyTuple_GetItem(py_expr_tuple, 2); - if (temp_expr.pydict != Py_None) { + if (!Py_IsNone(temp_expr.pydict)) { if (!PyDict_Check(temp_expr.pydict)) { as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to get fixed dictionary from " diff --git a/src/main/exception.c b/src/main/exception.c index 634ec91369..9ef0c2f64b 100644 --- a/src/main/exception.c +++ b/src/main/exception.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -382,7 +384,7 @@ void raise_exception(as_error *err) while (PyDict_Next(py_module_dict, &pos, &py_key, &py_value)) { if (PyObject_HasAttrString(py_value, "code")) { PyObject *py_code = PyObject_GetAttrString(py_value, "code"); - if (py_code == Py_None) { + if (Py_IsNone(py_code)) { continue; } if (err->code == PyLong_AsLong(py_code)) { @@ -456,7 +458,7 @@ PyObject *raise_exception_old(as_error *err) while (PyDict_Next(py_module_dict, &pos, &py_key, &py_value)) { if (PyObject_HasAttrString(py_value, "code")) { PyObject *py_code = PyObject_GetAttrString(py_value, "code"); - if (py_code == Py_None) { + if (Py_IsNone(py_code)) { continue; } if (err->code == PyLong_AsLong(py_code)) { diff --git a/src/main/global_hosts/type.c b/src/main/global_hosts/type.c index 63594d9c50..44e4e90511 100644 --- a/src/main/global_hosts/type.c +++ b/src/main/global_hosts/type.c @@ -42,7 +42,7 @@ static PyObject *AerospikeGlobalHosts_Type_New(PyTypeObject *type, static void AerospikeGlobalHosts_Type_Dealloc(PyObject *self) { - PyObject_Del(self); + PyObject_Free(self); } /******************************************************************************* diff --git a/src/main/nullobject/type.c b/src/main/nullobject/type.c index 9a290ed65d..c16966c0aa 100644 --- a/src/main/nullobject/type.c +++ b/src/main/nullobject/type.c @@ -25,7 +25,7 @@ static PyObject *AerospikeNullObject_Type_New(PyTypeObject *parent, static void AerospikeNullObject_Type_Dealloc(AerospikeNullObject *self) { - PyObject_Del(self); + PyObject_Free(self); } /******************************************************************************* diff --git a/src/main/policy.c b/src/main/policy.c index e49d5a355c..be1cb99a6d 100644 --- a/src/main/policy.c +++ b/src/main/policy.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -41,7 +43,7 @@ #define POLICY_INIT(__policy) \ as_error_reset(err); \ - if (!py_policy || py_policy == Py_None) { \ + if (!py_policy || Py_IsNone(py_policy)) { \ return err->code; \ } \ if (!PyDict_Check(py_policy)) { \ @@ -215,7 +217,7 @@ as_status set_query_options(as_error *err, PyObject *query_options, as_query *query) { PyObject *no_bins_val = NULL; - if (!query_options || query_options == Py_None) { + if (!query_options || Py_IsNone(query_options)) { return AEROSPIKE_OK; } @@ -247,14 +249,14 @@ as_status pyobject_to_policy_admin(AerospikeClient *self, as_error *err, as_policy_admin *config_admin_policy) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_admin); } //Initialize policy with global defaults as_policy_admin_copy(config_admin_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields POLICY_SET_FIELD(timeout, uint32_t); } @@ -291,14 +293,14 @@ as_status pyobject_to_policy_apply(AerospikeClient *self, as_error *err, as_policy_apply *config_apply_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_apply); } //Initialize policy with global defaults as_policy_apply_copy(config_apply_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -331,14 +333,14 @@ as_status pyobject_to_policy_info(as_error *err, PyObject *py_policy, as_policy_info **policy_p, as_policy_info *config_info_policy) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_info); } //Initialize policy with global defaults as_policy_info_copy(config_info_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields POLICY_SET_FIELD(timeout, uint32_t); POLICY_SET_FIELD(send_as_is, bool); @@ -363,14 +365,14 @@ as_status pyobject_to_policy_query(AerospikeClient *self, as_error *err, as_policy_query *config_query_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_query); } //Initialize policy with global defaults as_policy_query_copy(config_query_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); if (retval != AEROSPIKE_OK) { @@ -403,7 +405,7 @@ as_status pyobject_to_policy_read(AerospikeClient *self, as_error *err, as_policy_read *config_read_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_read); } @@ -411,7 +413,7 @@ as_status pyobject_to_policy_read(AerospikeClient *self, as_error *err, //Initialize policy with global defaults as_policy_read_copy(config_read_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -448,14 +450,14 @@ as_status pyobject_to_policy_remove(AerospikeClient *self, as_error *err, as_policy_remove *config_remove_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_remove); } //Initialize policy with global defaults as_policy_remove_copy(config_remove_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -490,14 +492,14 @@ as_status pyobject_to_policy_scan(AerospikeClient *self, as_error *err, as_policy_scan *config_scan_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_scan); } //Initialize policy with global defaults as_policy_scan_copy(config_scan_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -529,14 +531,14 @@ as_status pyobject_to_policy_write(AerospikeClient *self, as_error *err, as_policy_write *config_write_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_write); } //Initialize policy with global defaults as_policy_write_copy(config_write_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -572,14 +574,14 @@ as_status pyobject_to_policy_operate(AerospikeClient *self, as_error *err, as_policy_operate *config_operate_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_operate); } //Initialize policy with global defaults as_policy_operate_copy(config_operate_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -619,14 +621,14 @@ as_status pyobject_to_policy_batch(AerospikeClient *self, as_error *err, as_policy_batch *config_batch_policy, as_exp *exp_list, as_exp **exp_list_p) { - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Initialize Policy POLICY_INIT(as_policy_batch); } //Initialize policy with global defaults as_policy_batch_copy(config_batch_policy, policy); - if (py_policy && py_policy != Py_None) { + if (py_policy && !Py_IsNone(py_policy)) { // Set policy fields as_status retval = pyobject_to_policy_base( self, err, py_policy, &policy->base, exp_list, exp_list_p); @@ -816,7 +818,7 @@ as_status pyobject_to_list_policy(as_error *err, PyObject *py_policy, long list_order = AS_LIST_UNORDERED; long flags = AS_LIST_WRITE_DEFAULT; - if (!py_policy || py_policy == Py_None) { + if (!py_policy || Py_IsNone(py_policy)) { return AEROSPIKE_OK; } @@ -826,7 +828,7 @@ as_status pyobject_to_list_policy(as_error *err, PyObject *py_policy, } py_val = PyDict_GetItemString(py_policy, "list_order"); - if (py_val && py_val != Py_None) { + if (py_val && !Py_IsNone(py_val)) { if (PyLong_Check(py_val)) { list_order = (int64_t)PyLong_AsLong(py_val); if (PyErr_Occurred()) { @@ -841,7 +843,7 @@ as_status pyobject_to_list_policy(as_error *err, PyObject *py_policy, } py_val = PyDict_GetItemString(py_policy, "write_flags"); - if (py_val && py_val != Py_None) { + if (py_val && !Py_IsNone(py_val)) { if (PyLong_Check(py_val)) { flags = (int64_t)PyLong_AsLong(py_val); if (PyErr_Occurred()) { @@ -868,7 +870,7 @@ as_status pyobject_to_hll_policy(as_error *err, PyObject *py_policy, as_hll_policy_init(hll_policy); PyObject *py_val = NULL; - if (!py_policy || py_policy == Py_None) { + if (!py_policy || Py_IsNone(py_policy)) { return AEROSPIKE_OK; } @@ -878,7 +880,7 @@ as_status pyobject_to_hll_policy(as_error *err, PyObject *py_policy, } py_val = PyDict_GetItemString(py_policy, "flags"); - if (py_val && py_val != Py_None) { + if (py_val && !Py_IsNone(py_val)) { if (PyLong_Check(py_val)) { flags = (int64_t)PyLong_AsLong(py_val); if (PyErr_Occurred()) { @@ -1085,7 +1087,7 @@ set_as_metrics_listeners_using_pyobject(as_error *err, PyObject *py_metricslisteners, as_metrics_listeners *listeners) { - if (!py_metricslisteners || py_metricslisteners == Py_None) { + if (!py_metricslisteners || Py_IsNone(py_metricslisteners)) { // Use default metrics writer callbacks that were set when initializing metrics policy return AEROSPIKE_OK; } @@ -1165,7 +1167,7 @@ init_and_set_as_metrics_policy_using_pyobject(as_error *err, { as_metrics_policy_init(metrics_policy); - if (!py_metrics_policy || py_metrics_policy == Py_None) { + if (!py_metrics_policy || Py_IsNone(py_metrics_policy)) { // Use default metrics policy return AEROSPIKE_OK; } @@ -1292,7 +1294,7 @@ init_and_set_as_metrics_policy_using_pyobject(as_error *err, error: // udata would've been allocated if MetricsListener was successfully converted to C code - if (py_metrics_listeners && py_metrics_listeners != Py_None) { + if (py_metrics_listeners && !Py_IsNone(py_metrics_listeners)) { free_py_listener_data( (PyListenerData *)metrics_policy->metrics_listeners.udata); } diff --git a/src/main/policy_config.c b/src/main/policy_config.c index bb7c7acf77..6b4b235a45 100644 --- a/src/main/policy_config.c +++ b/src/main/policy_config.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2017-2021 Aerospike, Inc. * @@ -911,7 +913,7 @@ as_status set_optional_key(as_policy_key *target_ptr, PyObject *py_policy, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } @@ -933,7 +935,7 @@ as_status set_optional_replica(as_policy_replica *target_ptr, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } @@ -955,7 +957,7 @@ as_status set_optional_commit_level(as_policy_commit_level *target_ptr, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } @@ -977,7 +979,7 @@ as_status set_optional_ap_read_mode(as_policy_read_mode_ap *target_ptr, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } @@ -999,7 +1001,7 @@ as_status set_optional_sc_read_mode(as_policy_read_mode_sc *target_ptr, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } @@ -1021,7 +1023,7 @@ as_status set_optional_gen(as_policy_gen *target_ptr, PyObject *py_policy, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } @@ -1043,7 +1045,7 @@ as_status set_optional_exists(as_policy_exists *target_ptr, PyObject *py_policy, } py_policy_val = PyDict_GetItemString(py_policy, name); - if (!py_policy_val || py_policy_val == Py_None) { + if (!py_policy_val || Py_IsNone(py_policy_val)) { return AEROSPIKE_OK; } diff --git a/src/main/query/apply.c b/src/main/query/apply.c index 9218927ed5..ff068ed36e 100644 --- a/src/main/query/apply.c +++ b/src/main/query/apply.c @@ -190,9 +190,9 @@ bool Illegal_UDF_Args_Check(PyObject *py_args) } else if (!(PyLong_Check(py_val) || PyFloat_Check(py_val) || PyBool_Check(py_val) || PyUnicode_Check(py_val) || - !strcmp(py_val->ob_type->tp_name, "aerospike.Geospatial") || + !strcmp(Py_TYPE(py_val)->tp_name, "aerospike.Geospatial") || PyByteArray_Check(py_val) || (Py_None == py_val) || - (!strcmp(py_val->ob_type->tp_name, "aerospike.null")) || + (!strcmp(Py_TYPE(py_val)->tp_name, "aerospike.null")) || AS_Matches_Classname(py_val, AS_CDT_WILDCARD_NAME) || AS_Matches_Classname(py_val, AS_CDT_INFINITE_NAME) || PyBytes_Check(py_val))) { diff --git a/src/main/query/type.c b/src/main/query/type.c index 6c4758ddca..95c64eedf9 100644 --- a/src/main/query/type.c +++ b/src/main/query/type.c @@ -1,3 +1,5 @@ +#include "pythoncapi_compat.h" + /******************************************************************************* * Copyright 2013-2021 Aerospike, Inc. * @@ -200,7 +202,7 @@ static int AerospikeQuery_Type_Init(AerospikeQuery *self, PyObject *args, if (PyUnicode_Check(py_set)) { set = (char *)PyUnicode_AsUTF8(py_set); } - else if (py_set != Py_None) { + else if (!Py_IsNone(py_set)) { as_error_update(&err, AEROSPIKE_ERR_PARAM, "Set should be string, unicode or None"); goto CLEANUP; diff --git a/src/main/scan/apply.c b/src/main/scan/apply.c index 13e4e0ce4a..db3d314b39 100644 --- a/src/main/scan/apply.c +++ b/src/main/scan/apply.c @@ -188,9 +188,9 @@ bool Scan_Illegal_UDF_Args_Check(PyObject *py_args) } else if (!(PyLong_Check(py_val) || PyFloat_Check(py_val) || PyUnicode_Check(py_val) || PyBool_Check(py_val) || - !strcmp(py_val->ob_type->tp_name, "aerospike.Geospatial") || + !strcmp(Py_TYPE(py_val)->tp_name, "aerospike.Geospatial") || PyByteArray_Check(py_val) || (Py_None == py_val) || - (!strcmp(py_val->ob_type->tp_name, "aerospike.null")) || + (!strcmp(Py_TYPE(py_val)->tp_name, "aerospike.null")) || AS_Matches_Classname(py_val, AS_CDT_WILDCARD_NAME) || AS_Matches_Classname(py_val, AS_CDT_INFINITE_NAME) || PyBytes_Check(py_val))) { From ff4614c8a8127e50d35d133842ec7dfaccb015ef Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:30:43 -0700 Subject: [PATCH 29/66] fix --- src/main/client/connect.c | 1 + src/main/client/operate.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/client/connect.c b/src/main/client/connect.c index 5003227311..71bbb8702d 100644 --- a/src/main/client/connect.c +++ b/src/main/client/connect.c @@ -19,6 +19,7 @@ #include #include +#include "pythoncapi_compat.h" #include "client.h" #include "conversions.h" #include "global_hosts.h" diff --git a/src/main/client/operate.c b/src/main/client/operate.c index b54a17eab0..f36cccb186 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -36,6 +36,7 @@ #include "bit_operations.h" #include "hll_operations.h" #include "expression_operations.h" +#include "pythoncapi_compat.h" #include #include From 26ac36de8a88e7f28fa66ecc35327b276fa722fd Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:24:30 -0700 Subject: [PATCH 30/66] Make sure to exit CS to let other threads run --- src/main/conversions.c | 48 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 46295ad21d..a80ccf2d76 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -1297,30 +1297,32 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, while (PyDict_Next(py_rec, &pos, &key, &value)) { if (!PyUnicode_Check(key)) { - return as_error_update( + as_error_update( err, AEROSPIKE_ERR_CLIENT, "A bin name must be a string or unicode string."); + goto EXIT_CS; } name = PyUnicode_AsUTF8(key); if (!name) { - return as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unable to convert unicode object to C string"); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to convert unicode object to C string"); + goto EXIT_CS; } if (self->strict_types) { if (strlen(name) > AS_BIN_NAME_MAX_LEN) { - return as_error_update( + as_error_update( err, AEROSPIKE_ERR_BIN_NAME, "A bin name should not exceed 15 characters limit"); + goto EXIT_CS; } } if (!value) { // this should never happen, but if it did... - return as_error_update(err, AEROSPIKE_ERR_CLIENT, - "record is null"); + as_error_update(err, AEROSPIKE_ERR_CLIENT, "record is null"); + goto EXIT_CS; } else if ( PyBool_Check( @@ -1340,17 +1342,18 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, as_record_set_integer(rec, name, converted_integer); break; default: - return as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Unknown value for send_bool_as."); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unknown value for send_bool_as."); + goto EXIT_CS; } } else if (PyLong_Check(value)) { int64_t val = (int64_t)PyLong_AsLong(value); if (val == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - return as_error_update( - err, AEROSPIKE_ERR_PARAM, - "integer value exceeds sys.maxsize"); + as_error_update(err, AEROSPIKE_ERR_PARAM, + "integer value exceeds sys.maxsize"); + goto EXIT_CS; } } ret_val = as_record_set_int64(rec, name, val); @@ -1367,9 +1370,9 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (PyUnicode_Check(py_dumps)) { py_ustr = PyUnicode_AsUTF8String(py_dumps); if (!py_ustr) { - return as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unicode value not encoded in utf-8."); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unicode value not encoded in utf-8."); + goto EXIT_CS; } geo_value = PyBytes_AsString(py_ustr); } @@ -1388,9 +1391,9 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, else if (PyUnicode_Check(value)) { PyObject *py_ustr = PyUnicode_AsUTF8String(value); if (!py_ustr) { - return as_error_update( - err, AEROSPIKE_ERR_CLIENT, - "Unicode value not encoded in utf-8."); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unicode value not encoded in utf-8."); + goto EXIT_CS; } char *val = PyBytes_AsString(py_ustr); ret_val = as_record_set_strp(rec, name, strdup(val), true); @@ -1462,12 +1465,17 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (self->strict_types) { if (!ret_val) { - return as_error_update(err, AEROSPIKE_ERR_BIN_NAME, - "Unable to set key-value pair"); + as_error_update(err, AEROSPIKE_ERR_BIN_NAME, + "Unable to set key-value pair"); + goto EXIT_CS; } } } + EXIT_CS: Py_END_CRITICAL_SECTION(); + if (err->code != AEROSPIKE_OK) { + return err->code; + } if (py_meta && !Py_IsNone(py_meta)) { if (!PyDict_Check(py_meta)) { From 05dae2d72f4cefc027aa667c9c942db9b0451491 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:40:08 -0700 Subject: [PATCH 31/66] fix for operate --- src/main/client/operate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/client/operate.c b/src/main/client/operate.c index f36cccb186..94fd893c45 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -119,7 +119,7 @@ static inline bool isExprOp(int op); #define CONVERT_PY_CTX_TO_AS_CTX() \ if (get_cdt_ctx(self, err, &ctx, py_val, &ctx_in_use, static_pool, \ SERIALIZER_PYTHON) != AEROSPIKE_OK) { \ - return err->code; \ + goto EXIT_CS; \ } #define CONVERT_RANGE_TO_AS_VAL() \ @@ -390,8 +390,9 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, Py_BEGIN_CRITICAL_SECTION(py_val); while (PyDict_Next(py_val, &pos, &key_op, &value)) { if (!PyUnicode_Check(key_op)) { - return as_error_update(err, AEROSPIKE_ERR_CLIENT, - "An operation key must be a string."); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "An operation key must be a string."); + goto EXIT_CS; } else { char *name = (char *)PyUnicode_AsUTF8(key_op); @@ -437,11 +438,15 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, err, AEROSPIKE_ERR_PARAM, "Operation can contain only op, bin, index, key, val, " "return_type and map_policy keys"); - goto CLEANUP; + goto EXIT_CS; } } } +EXIT_CS: Py_END_CRITICAL_SECTION(); + if (err->code != AEROSPIKE_OK) { + goto CLEANUP; + } *op = operation; From b0e1892351718cf7f792cf0c86cbf74967d28892 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 08:26:02 -0700 Subject: [PATCH 32/66] fix --- src/main/conversions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index a80ccf2d76..21ade9e004 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -1336,7 +1336,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, as_integer *converted_integer = NULL; if (py_bool_to_as_integer(err, value, &converted_integer) != AEROSPIKE_OK) { - return err->code; + goto EXIT_CS; } ret_val = as_record_set_integer(rec, name, converted_integer); @@ -1456,7 +1456,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (serialize_based_on_serializer_policy( self, serializer_type, &bytes, value, err) != AEROSPIKE_OK) { - return err->code; + goto EXIT_CS; } ret_val = as_record_set_bytes(rec, name, bytes); } From a92256654ecc65151856f6d52c303511bac5be4e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 09:22:36 -0700 Subject: [PATCH 33/66] Replace one call --- src/main/conversions.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/conversions.c b/src/main/conversions.c index 21ade9e004..c253ac9c27 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -252,7 +252,12 @@ as_status pyobject_to_as_privileges(as_error *err, PyObject *py_privileges, { as_error_reset(err); for (int i = 0; i < privileges_size; i++) { - PyObject *py_val = PyList_GetItem(py_privileges, i); + PyObject *py_val = PyList_GetItemRef(py_privileges, i); + if (py_val == NULL) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get privilege at index %d", i); + } if (PyDict_Check(py_val)) { PyObject *py_dict_key = PyUnicode_FromString("code"); if (PyDict_Contains(py_val, py_dict_key)) { @@ -286,6 +291,7 @@ as_status pyobject_to_as_privileges(as_error *err, PyObject *py_privileges, } Py_DECREF(py_dict_key); } + Py_DECREF(py_val); } return err->code; } From 8be436d4bb81d1fa3bae1afc44b1944607ee15a0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 09:59:27 -0700 Subject: [PATCH 34/66] wip --- test/new_tests/test_free_threading.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/new_tests/test_free_threading.py diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py new file mode 100644 index 0000000000..75d8628cf5 --- /dev/null +++ b/test/new_tests/test_free_threading.py @@ -0,0 +1,8 @@ +import threading +from concurrent.futures import ThreadPoolExecutor + +import aerospike + +def test_unsafe(): + n_threads = 10 + barrier = threading.Barrier(n_threads) From a41d711e0d15080abb6b4e1a33bdb21eadaafc10 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:00:46 -0700 Subject: [PATCH 35/66] see what happens --- test/new_tests/test_free_threading.py | 35 ++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index 75d8628cf5..6957a02fe3 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -1,8 +1,35 @@ import threading -from concurrent.futures import ThreadPoolExecutor import aerospike -def test_unsafe(): - n_threads = 10 - barrier = threading.Barrier(n_threads) + +def test_unsafe(as_connection): + key = ("test", "demo", 1) + BIN_VALUE = 1 + as_connection.put(key, policy={"a": BIN_VALUE}) + + bin_value_sum = 0 + + THREAD_COUNT = 10 + barrier = threading.Barrier(parties=THREAD_COUNT) + config = {"hosts": [("127.0.0.1", 3000)]} + + def read_bin(): + barrier.wait() + client = aerospike.client(config) + _, _, bins = client.get(key) + nonlocal bin_value_sum + bin_value_sum += bins["a"] + + workers = [] + for _ in range(THREAD_COUNT): + workers.append(threading.Thread(target=read_bin)) + + for worker in workers: + worker.start() + + for worker in workers: + worker.join() + + # Do something about the results + assert bin_value_sum == THREAD_COUNT * BIN_VALUE From 3234a03403ee02a742bc77bcb84db2b458b593ce Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:15:09 -0700 Subject: [PATCH 36/66] make sure this works right --- test/new_tests/test_free_threading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index 6957a02fe3..b974d2a9ef 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -15,6 +15,7 @@ def test_unsafe(as_connection): config = {"hosts": [("127.0.0.1", 3000)]} def read_bin(): + nonlocal barrier barrier.wait() client = aerospike.client(config) _, _, bins = client.get(key) @@ -31,5 +32,4 @@ def read_bin(): for worker in workers: worker.join() - # Do something about the results assert bin_value_sum == THREAD_COUNT * BIN_VALUE From 622b67a250303549c601c4b9988126682de5c91c Mon Sep 17 00:00:00 2001 From: juliannguyen4 <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 22:11:19 +0000 Subject: [PATCH 37/66] fix --- test/new_tests/test_free_threading.py | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index b974d2a9ef..bc479ef30e 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -1,35 +1,37 @@ import threading - +import pytest import aerospike -def test_unsafe(as_connection): - key = ("test", "demo", 1) - BIN_VALUE = 1 - as_connection.put(key, policy={"a": BIN_VALUE}) +@pytest.mark.usefixtures("as_connection") +class TestFreeThreading: + def test_unsafe(self): + key = ("test", "demo", 1) + BIN_VALUE = 1 + self.as_connection.put(key, bins={"a": BIN_VALUE}) - bin_value_sum = 0 + bin_value_sum = 0 - THREAD_COUNT = 10 - barrier = threading.Barrier(parties=THREAD_COUNT) - config = {"hosts": [("127.0.0.1", 3000)]} + THREAD_COUNT = 10 + barrier = threading.Barrier(parties=THREAD_COUNT) + config = {"hosts": [("127.0.0.1", 3000)]} - def read_bin(): - nonlocal barrier - barrier.wait() - client = aerospike.client(config) - _, _, bins = client.get(key) - nonlocal bin_value_sum - bin_value_sum += bins["a"] + def read_bin(): + nonlocal barrier + barrier.wait() + client = aerospike.client(config) + _, _, bins = client.get(key) + nonlocal bin_value_sum + bin_value_sum += bins["a"] - workers = [] - for _ in range(THREAD_COUNT): - workers.append(threading.Thread(target=read_bin)) + workers = [] + for _ in range(THREAD_COUNT): + workers.append(threading.Thread(target=read_bin)) - for worker in workers: - worker.start() + for worker in workers: + worker.start() - for worker in workers: - worker.join() + for worker in workers: + worker.join() - assert bin_value_sum == THREAD_COUNT * BIN_VALUE + assert bin_value_sum == THREAD_COUNT * BIN_VALUE From 9d7fe5100c0c40c409dada016f4e4eb4ea282ac5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:24:29 -0700 Subject: [PATCH 38/66] fix --- test/new_tests/test_free_threading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index bc479ef30e..293f80acc7 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -17,7 +17,7 @@ def test_unsafe(self): config = {"hosts": [("127.0.0.1", 3000)]} def read_bin(): - nonlocal barrier + nonlocal barrier, config barrier.wait() client = aerospike.client(config) _, _, bins = client.get(key) From ff830d5ff43abdb26ec81630fdc1ec992c22b683 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 23 Oct 2024 19:45:44 -0700 Subject: [PATCH 39/66] try locking as CS --- test/new_tests/test_free_threading.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index 293f80acc7..d5fd6e6056 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -15,14 +15,17 @@ def test_unsafe(self): THREAD_COUNT = 10 barrier = threading.Barrier(parties=THREAD_COUNT) config = {"hosts": [("127.0.0.1", 3000)]} + lock = threading.Lock() def read_bin(): - nonlocal barrier, config + nonlocal barrier, config, lock barrier.wait() client = aerospike.client(config) _, _, bins = client.get(key) nonlocal bin_value_sum + lock.acquire() bin_value_sum += bins["a"] + lock.release() workers = [] for _ in range(THREAD_COUNT): From 1fdb0d8eb0422d8357c4cbd36f56678b0572fc0d Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:24:28 -0700 Subject: [PATCH 40/66] rewrite --- test/new_tests/test_free_threading.py | 32 +++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index d5fd6e6056..7e7bce820f 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -1,35 +1,38 @@ import threading import pytest + import aerospike +from aerospike_helpers.operations import operations @pytest.mark.usefixtures("as_connection") class TestFreeThreading: def test_unsafe(self): key = ("test", "demo", 1) - BIN_VALUE = 1 - self.as_connection.put(key, bins={"a": BIN_VALUE}) - - bin_value_sum = 0 + INIT_BIN_VALUE = 0 + BIN_NAME = "a" + BIN_VALUE_AMOUNT_TO_ADD_IN_EACH_THREAD = 1 + self.as_connection.put(key, bins={BIN_NAME: INIT_BIN_VALUE}) THREAD_COUNT = 10 barrier = threading.Barrier(parties=THREAD_COUNT) config = {"hosts": [("127.0.0.1", 3000)]} - lock = threading.Lock() - def read_bin(): - nonlocal barrier, config, lock + ops = [ + operations.increment(bin_name=BIN_NAME, amount=BIN_VALUE_AMOUNT_TO_ADD_IN_EACH_THREAD) + ] + + def increment_bin(): + nonlocal barrier barrier.wait() + nonlocal config client = aerospike.client(config) - _, _, bins = client.get(key) - nonlocal bin_value_sum - lock.acquire() - bin_value_sum += bins["a"] - lock.release() + nonlocal key, ops + client.operate(key, ops) workers = [] for _ in range(THREAD_COUNT): - workers.append(threading.Thread(target=read_bin)) + workers.append(threading.Thread(target=increment_bin)) for worker in workers: worker.start() @@ -37,4 +40,5 @@ def read_bin(): for worker in workers: worker.join() - assert bin_value_sum == THREAD_COUNT * BIN_VALUE + _, _, bins = self.as_connection.get(key) + assert bins[BIN_NAME] == INIT_BIN_VALUE + BIN_VALUE_AMOUNT_TO_ADD_IN_EACH_THREAD * THREAD_COUNT From 40179c8bcb84961737bd82b221253d8f81ca4223 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:42:32 -0700 Subject: [PATCH 41/66] measure time --- test/new_tests/test_free_threading.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index 7e7bce820f..a51daaeaf9 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -3,6 +3,7 @@ import aerospike from aerospike_helpers.operations import operations +import time @pytest.mark.usefixtures("as_connection") @@ -34,11 +35,16 @@ def increment_bin(): for _ in range(THREAD_COUNT): workers.append(threading.Thread(target=increment_bin)) + start = time.time_ns() + for worker in workers: worker.start() for worker in workers: worker.join() + end = time.time_ns() + _, _, bins = self.as_connection.get(key) assert bins[BIN_NAME] == INIT_BIN_VALUE + BIN_VALUE_AMOUNT_TO_ADD_IN_EACH_THREAD * THREAD_COUNT + print(f"Threads took {end - start} ns to run.") From c7b57494619eb709a8976d2efa661e3eb59fc473 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:43:28 -0700 Subject: [PATCH 42/66] run free threading test with both gil and no-gil --- .github/workflows/tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1294c4a596..3d007f91ae 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - py-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + py-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.13t"] fail-fast: false steps: @@ -254,7 +254,9 @@ jobs: "3.9", "3.10", "3.11", - "3.12" + "3.12", + "3.13", + "3.13t" ] fail-fast: false From 12dfa714ae23672aad49528fa8bb48a3e7c47629 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:45:11 -0700 Subject: [PATCH 43/66] update --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3d007f91ae..d67b5070d1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -53,7 +53,7 @@ jobs: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.py-version }} architecture: 'x64' @@ -265,7 +265,7 @@ jobs: with: submodules: recursive - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.py-version }} architecture: 'x64' From fd95ed61f20145c1b7d4fa05e84e1cc47bcfd2ae Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:14:12 -0700 Subject: [PATCH 44/66] fix --- test/new_tests/test_free_threading.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/new_tests/test_free_threading.py b/test/new_tests/test_free_threading.py index a51daaeaf9..96fe1d813b 100644 --- a/test/new_tests/test_free_threading.py +++ b/test/new_tests/test_free_threading.py @@ -1,9 +1,10 @@ import threading import pytest +import time import aerospike from aerospike_helpers.operations import operations -import time +from .test_base_class import TestBaseClass @pytest.mark.usefixtures("as_connection") @@ -17,11 +18,11 @@ def test_unsafe(self): THREAD_COUNT = 10 barrier = threading.Barrier(parties=THREAD_COUNT) - config = {"hosts": [("127.0.0.1", 3000)]} ops = [ operations.increment(bin_name=BIN_NAME, amount=BIN_VALUE_AMOUNT_TO_ADD_IN_EACH_THREAD) ] + config = TestBaseClass.get_connection_config() def increment_bin(): nonlocal barrier From 7d505ad6fe88f4a86435ec75f739dadd4bf762a2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:15:50 -0700 Subject: [PATCH 45/66] show output --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 959e3b1918..36ceb6a2eb 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -365,7 +365,7 @@ jobs: - run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'uv run --module' || 'python3 -m' }} >> $GITHUB_ENV shell: bash - - run: ${{ env.PYTHON_M_COMMAND }} pytest new_tests/ + - run: ${{ env.PYTHON_M_COMMAND }} pytest -s new_tests/ working-directory: test shell: bash From dd383702e0cdfd82607c77df953baa03dc616401 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:34:24 -0700 Subject: [PATCH 46/66] disable shared conn on free threading mode --- src/main/client/type.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/client/type.c b/src/main/client/type.c index 1d7a2b3452..6846f27924 100644 --- a/src/main/client/type.c +++ b/src/main/client/type.c @@ -978,7 +978,9 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_share_connect = PyDict_GetItemString(py_config, "use_shared_connection"); if (py_share_connect) { +#ifndef Py_GIL_DISABLED self->use_shared_connection = PyObject_IsTrue(py_share_connect); +#endif } PyObject *py_send_bool_as = PyDict_GetItemString(py_config, "send_bool_as"); From f509e067793f6524f336e1a9f98e369278b688ce Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:29:53 -0700 Subject: [PATCH 47/66] add noop to end of macro to allow labels before this macro --- src/include/pythoncapi_compat.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/include/pythoncapi_compat.h b/src/include/pythoncapi_compat.h index 12aac27f11..b29b929d3c 100644 --- a/src/include/pythoncapi_compat.h +++ b/src/include/pythoncapi_compat.h @@ -1316,7 +1316,9 @@ static inline int PyDict_SetDefaultRef(PyObject *d, PyObject *key, #if PY_VERSION_HEX < 0x030D00B3 #define Py_BEGIN_CRITICAL_SECTION(op) { - #define Py_END_CRITICAL_SECTION() } + #define Py_END_CRITICAL_SECTION() \ + ; \ + } #define Py_BEGIN_CRITICAL_SECTION2(a, b) { #define Py_END_CRITICAL_SECTION2() } #endif From da2ca4d3a62fdea970bafb237aaa64b7697d9f7e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 1 Nov 2024 09:49:35 -0700 Subject: [PATCH 48/66] remove labels to exit cs --- src/include/pythoncapi_compat.h | 4 +--- src/main/client/operate.c | 7 +++---- src/main/conversions.c | 29 +++++++++++++++++------------ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/include/pythoncapi_compat.h b/src/include/pythoncapi_compat.h index b29b929d3c..12aac27f11 100644 --- a/src/include/pythoncapi_compat.h +++ b/src/include/pythoncapi_compat.h @@ -1316,9 +1316,7 @@ static inline int PyDict_SetDefaultRef(PyObject *d, PyObject *key, #if PY_VERSION_HEX < 0x030D00B3 #define Py_BEGIN_CRITICAL_SECTION(op) { - #define Py_END_CRITICAL_SECTION() \ - ; \ - } + #define Py_END_CRITICAL_SECTION() } #define Py_BEGIN_CRITICAL_SECTION2(a, b) { #define Py_END_CRITICAL_SECTION2() } #endif diff --git a/src/main/client/operate.c b/src/main/client/operate.c index 94fd893c45..af671c2abb 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -119,7 +119,7 @@ static inline bool isExprOp(int op); #define CONVERT_PY_CTX_TO_AS_CTX() \ if (get_cdt_ctx(self, err, &ctx, py_val, &ctx_in_use, static_pool, \ SERIALIZER_PYTHON) != AEROSPIKE_OK) { \ - goto EXIT_CS; \ + break; \ } #define CONVERT_RANGE_TO_AS_VAL() \ @@ -392,7 +392,7 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, if (!PyUnicode_Check(key_op)) { as_error_update(err, AEROSPIKE_ERR_CLIENT, "An operation key must be a string."); - goto EXIT_CS; + break; } else { char *name = (char *)PyUnicode_AsUTF8(key_op); @@ -438,11 +438,10 @@ as_status add_op(AerospikeClient *self, as_error *err, PyObject *py_val, err, AEROSPIKE_ERR_PARAM, "Operation can contain only op, bin, index, key, val, " "return_type and map_policy keys"); - goto EXIT_CS; + break; } } } -EXIT_CS: Py_END_CRITICAL_SECTION(); if (err->code != AEROSPIKE_OK) { goto CLEANUP; diff --git a/src/main/conversions.c b/src/main/conversions.c index c253ac9c27..7e4d92826f 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -1306,14 +1306,14 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, as_error_update( err, AEROSPIKE_ERR_CLIENT, "A bin name must be a string or unicode string."); - goto EXIT_CS; + break; } name = PyUnicode_AsUTF8(key); if (!name) { as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unable to convert unicode object to C string"); - goto EXIT_CS; + break; } if (self->strict_types) { @@ -1321,18 +1321,19 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, as_error_update( err, AEROSPIKE_ERR_BIN_NAME, "A bin name should not exceed 15 characters limit"); - goto EXIT_CS; + break; } } if (!value) { // this should never happen, but if it did... as_error_update(err, AEROSPIKE_ERR_CLIENT, "record is null"); - goto EXIT_CS; + break; } else if ( PyBool_Check( value)) { //TODO Change to true bool support post jump version. + bool error_raised = false; switch (self->send_bool_as) { case SEND_BOOL_AS_AS_BOOL:; bool converted_value = (Py_IsTrue(value)); @@ -1342,7 +1343,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, as_integer *converted_integer = NULL; if (py_bool_to_as_integer(err, value, &converted_integer) != AEROSPIKE_OK) { - goto EXIT_CS; + error_raised = true; } ret_val = as_record_set_integer(rec, name, converted_integer); @@ -1350,7 +1351,12 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, default: as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unknown value for send_bool_as."); - goto EXIT_CS; + error_raised = true; + break; + } + + if (error_raised == true) { + break; } } else if (PyLong_Check(value)) { @@ -1359,7 +1365,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (PyErr_ExceptionMatches(PyExc_OverflowError)) { as_error_update(err, AEROSPIKE_ERR_PARAM, "integer value exceeds sys.maxsize"); - goto EXIT_CS; + break; } } ret_val = as_record_set_int64(rec, name, val); @@ -1378,7 +1384,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (!py_ustr) { as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unicode value not encoded in utf-8."); - goto EXIT_CS; + break; } geo_value = PyBytes_AsString(py_ustr); } @@ -1399,7 +1405,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (!py_ustr) { as_error_update(err, AEROSPIKE_ERR_CLIENT, "Unicode value not encoded in utf-8."); - goto EXIT_CS; + break; } char *val = PyBytes_AsString(py_ustr); ret_val = as_record_set_strp(rec, name, strdup(val), true); @@ -1462,7 +1468,7 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (serialize_based_on_serializer_policy( self, serializer_type, &bytes, value, err) != AEROSPIKE_OK) { - goto EXIT_CS; + break; } ret_val = as_record_set_bytes(rec, name, bytes); } @@ -1473,11 +1479,10 @@ as_status pyobject_to_record(AerospikeClient *self, as_error *err, if (!ret_val) { as_error_update(err, AEROSPIKE_ERR_BIN_NAME, "Unable to set key-value pair"); - goto EXIT_CS; + break; } } } - EXIT_CS: Py_END_CRITICAL_SECTION(); if (err->code != AEROSPIKE_OK) { return err->code; From 94c1c9012f3b039dd9400cd4b2cbe587304b31a2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:55:23 -0800 Subject: [PATCH 49/66] Revert "disable shared conn on free threading mode" This reverts commit dd383702e0cdfd82607c77df953baa03dc616401. --- src/main/client/type.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/client/type.c b/src/main/client/type.c index 6846f27924..1d7a2b3452 100644 --- a/src/main/client/type.c +++ b/src/main/client/type.c @@ -978,9 +978,7 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_share_connect = PyDict_GetItemString(py_config, "use_shared_connection"); if (py_share_connect) { -#ifndef Py_GIL_DISABLED self->use_shared_connection = PyObject_IsTrue(py_share_connect); -#endif } PyObject *py_send_bool_as = PyDict_GetItemString(py_config, "send_bool_as"); From ca1c7666dbbce7f4356319388a79d3df81756ff9 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:56:06 -0800 Subject: [PATCH 50/66] see what happens --- test/new_tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/new_tests/conftest.py b/test/new_tests/conftest.py index 6bd4c466db..591e2a0a3f 100644 --- a/test/new_tests/conftest.py +++ b/test/new_tests/conftest.py @@ -77,6 +77,7 @@ def as_connection(request): lua_user_path = os.path.join(sys.exec_prefix, "aerospike", "usr-lua") lua_info = {"user_path": lua_user_path} config["lua"] = lua_info + config["use_shared_connection"] = True # print(config) as_client = None if len(config["hosts"]) == 2: From bd2432488dc11b9806890a7f2caa5ebfc30964dd Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 13 Nov 2024 08:44:33 -0800 Subject: [PATCH 51/66] WIP --- src/main/client/batch_apply.c | 8 ++++++- src/main/client/batch_operate.c | 16 ++++++++++++-- src/main/client/batch_read.c | 18 +++++++++++++--- src/main/conversions.c | 38 +++++++++++++++++++++++++++------ src/main/convert_expressions.c | 8 ++++++- 5 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/main/client/batch_apply.c b/src/main/client/batch_apply.c index 5d9982430a..d90d630f40 100644 --- a/src/main/client/batch_apply.c +++ b/src/main/client/batch_apply.c @@ -155,7 +155,13 @@ static PyObject *AerospikeClient_Batch_Apply_Invoke( } for (int i = 0; i < keys_size; i++) { - PyObject *py_key = PyList_GetItem(py_keys, i); + PyObject *py_key = PyList_GetItemRef(py_keys, i); + if (!py_key) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get key at index %d", i); + goto CLEANUP; + } as_key *tmp_key = (as_key *)as_vector_get(&tmp_keys, i); if (!PyTuple_Check(py_key)) { diff --git a/src/main/client/batch_operate.c b/src/main/client/batch_operate.c index 40294bf24b..1b05a7c93d 100644 --- a/src/main/client/batch_operate.c +++ b/src/main/client/batch_operate.c @@ -163,7 +163,13 @@ static PyObject *AerospikeClient_Batch_Operate_Invoke( } for (int i = 0; i < ops_size; i++) { - PyObject *py_val = PyList_GetItem(py_ops, i); + PyObject *py_val = PyList_GetItemRef(py_ops, i); + if (!py_val) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get operation at index %d", i); + goto CLEANUP; + } if (!PyDict_Check(py_val)) { as_error_update(err, AEROSPIKE_ERR_PARAM, @@ -185,7 +191,13 @@ static PyObject *AerospikeClient_Batch_Operate_Invoke( uint64_t processed_key_count = 0; for (int i = 0; i < keys_size; i++) { - PyObject *py_key = PyList_GetItem(py_keys, i); + PyObject *py_key = PyList_GetItemRef(py_keys, i); + if (!py_key) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get key at index %d", i); + goto CLEANUP; + } as_key *tmp_key = (as_key *)as_vector_get(&tmp_keys, i); if (!PyTuple_Check(py_key)) { diff --git a/src/main/client/batch_read.c b/src/main/client/batch_read.c index cf6c601a15..907b20ef30 100644 --- a/src/main/client/batch_read.c +++ b/src/main/client/batch_read.c @@ -121,10 +121,15 @@ PyObject *AerospikeClient_BatchRead(AerospikeClient *self, PyObject *args, uint64_t processed_key_count = 0; for (int i = 0; i < keys_size; i++) { - PyObject *py_key = PyList_GetItem(py_keys, i); + PyObject *py_key = PyList_GetItemRef(py_keys, i); + if (!py_key) { + PyErr_Clear(); + as_error_update(&err, AEROSPIKE_ERR_CLIENT, + "Unable to get key at index %d", i); + goto CLEANUP2; + } as_key *tmp_key = (as_key *)as_vector_get(&tmp_keys, i); - Py_INCREF(py_key); if (!PyTuple_Check(py_key)) { as_error_update(&err, AEROSPIKE_ERR_PARAM, "key should be an aerospike key tuple"); @@ -230,7 +235,14 @@ PyObject *AerospikeClient_BatchRead(AerospikeClient *self, PyObject *args, filter_bins = (const char **)malloc(sizeof(char *) * bin_count); for (Py_ssize_t i = 0; i < bin_count; i++) { - PyObject *py_bin = PyList_GetItem(py_bins, i); + PyObject *py_bin = PyList_GetItemRef(py_bins, i); + if (!py_bin) { + PyErr_Clear(); + as_error_update(&err, AEROSPIKE_ERR_CLIENT, + "Unable to get python object at index %d.", + i); + goto CLEANUP5; + } if (PyUnicode_Check(py_bin)) { filter_bins[i] = PyUnicode_AsUTF8(py_bin); } diff --git a/src/main/conversions.c b/src/main/conversions.c index 064cabd117..f1844822f3 100644 --- a/src/main/conversions.c +++ b/src/main/conversions.c @@ -256,9 +256,10 @@ as_status pyobject_to_as_privileges(as_error *err, PyObject *py_privileges, if (py_val == NULL) { PyErr_Clear(); as_error_update(err, AEROSPIKE_ERR_CLIENT, - "Unable to get privilege at index %d", i); + "Unable to get privilege dictionary at index %d", + i); } - if (PyDict_Check(py_val)) { + else if (PyDict_Check(py_val)) { PyObject *py_dict_key = PyUnicode_FromString("code"); if (PyDict_Contains(py_val, py_dict_key)) { PyObject *py_code = NULL; @@ -291,7 +292,7 @@ as_status pyobject_to_as_privileges(as_error *err, PyObject *py_privileges, } Py_DECREF(py_dict_key); } - Py_DECREF(py_val); + Py_XDECREF(py_val); } return err->code; } @@ -674,7 +675,12 @@ as_status pyobject_to_strArray(as_error *err, PyObject *py_list, char **arr, char *s; for (int i = 0; i < size; i++) { - PyObject *py_val = PyList_GetItem(py_list, i); + PyObject *py_val = PyList_GetItemRef(py_list, i); + if (!py_val) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get python object at index %d", i); + } if (PyUnicode_Check(py_val)) { s = (char *)PyUnicode_AsUTF8(py_val); @@ -692,6 +698,7 @@ as_status pyobject_to_strArray(as_error *err, PyObject *py_list, char **arr, as_error_update(err, AEROSPIKE_ERR_CLIENT, "Item is not a string"); return err->code; } + Py_XDECREF(py_val); } return err->code; @@ -710,9 +717,16 @@ as_status pyobject_to_list(AerospikeClient *self, as_error *err, } for (int i = 0; i < size; i++) { - PyObject *py_val = PyList_GetItem(py_list, i); + PyObject *py_val = PyList_GetItemRef(py_list, i); + if (!py_val) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get python object at index %d", i); + break; + } as_val *val = NULL; pyobject_to_val(self, err, py_val, &val, static_pool, serializer_type); + Py_DECREF(py_val); if (err->code != AEROSPIKE_OK) { break; } @@ -2802,11 +2816,19 @@ as_status get_cdt_ctx(AerospikeClient *self, as_error *err, as_cdt_ctx *cdt_ctx, as_cdt_ctx_init(cdt_ctx, (int)py_list_size); for (int i = 0; i < py_list_size; i++) { - PyObject *py_val = PyList_GetItem(py_ctx, (Py_ssize_t)i); + PyObject *py_val = PyList_GetItemRef(py_ctx, (Py_ssize_t)i); + if (!py_val) { + PyErr_Clear(); + as_cdt_ctx_destroy(cdt_ctx); + return as_error_update( + err, AEROSPIKE_ERR_CLIENT, + "Unable to get python object at index %d", i); + } PyObject *id_temp = PyObject_GetAttrString(py_val, "id"); if (PyErr_Occurred()) { as_cdt_ctx_destroy(cdt_ctx); + Py_DECREF(py_val); return as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to convert %s, id", CTX_KEY); } @@ -2814,6 +2836,7 @@ as_status get_cdt_ctx(AerospikeClient *self, as_error *err, as_cdt_ctx *cdt_ctx, PyObject *value_temp = PyObject_GetAttrString(py_val, "value"); if (PyErr_Occurred()) { as_cdt_ctx_destroy(cdt_ctx); + Py_DECREF(py_val); return as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to convert %s, value", CTX_KEY); } @@ -2822,10 +2845,13 @@ as_status get_cdt_ctx(AerospikeClient *self, as_error *err, as_cdt_ctx *cdt_ctx, PyObject_GetAttrString(py_val, "extra_args"); if (PyErr_Occurred()) { as_cdt_ctx_destroy(cdt_ctx); + Py_DECREF(py_val); return as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to convert %s", CTX_KEY); } + Py_DECREF(py_val); + uint64_t item_type = PyLong_AsUnsignedLong(id_temp); if (PyErr_Occurred()) { as_cdt_ctx_destroy(cdt_ctx); diff --git a/src/main/convert_expressions.c b/src/main/convert_expressions.c index 896e23639c..80abc4e0ae 100644 --- a/src/main/convert_expressions.c +++ b/src/main/convert_expressions.c @@ -1675,7 +1675,13 @@ as_status convert_exp_list(AerospikeClient *self, PyObject *py_exp_list, // Reset flag for next temp expr being built is_ctx_initialized = false; - py_expr_tuple = PyList_GetItem(py_exp_list, (Py_ssize_t)i); + py_expr_tuple = PyList_GetItemRef(py_exp_list, (Py_ssize_t)i); + if (!py_expr_tuple) { + PyErr_Clear(); + as_error_update(err, AEROSPIKE_ERR_CLIENT, + "Unable to get expression at index %d", i); + goto CLEANUP; + } if (!PyTuple_Check(py_expr_tuple) || PyTuple_Size(py_expr_tuple) != 4) { as_error_update( err, AEROSPIKE_ERR_PARAM, From cda61d8a9bd3fe7e95fb33e6a62080ac9f34ff8f Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:08:43 -0800 Subject: [PATCH 52/66] wip --- src/main/policy.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/main/policy.c b/src/main/policy.c index 6e4ecaa3ef..b9ed0be2b3 100644 --- a/src/main/policy.c +++ b/src/main/policy.c @@ -69,9 +69,9 @@ return as_error_update(err, AEROSPIKE_ERR_CLIENT, \ "Unable to create Python unicode object"); \ } \ - PyObject *py_field = \ - PyDict_GetItemWithError(py_policy, py_field_name); \ - if (py_field == NULL && PyErr_Occurred()) { \ + PyObject *py_field = NULL; \ + int retval = PyDict_GetItemRef(py_policy, py_field_name, &py_field); \ + if (retval == -1) { \ PyErr_Clear(); \ Py_DECREF(py_field_name); \ return as_error_update( \ @@ -96,6 +96,7 @@ "%s is invalid", #__field); \ } \ } \ + Py_XDECREF(py_field); \ } #define POLICY_SET_EXPRESSIONS_FIELD() \ @@ -108,9 +109,10 @@ err, AEROSPIKE_ERR_CLIENT, \ "Unable to create Python unicode object"); \ } \ - PyObject *py_exp_list = \ - PyDict_GetItemWithError(py_policy, py_field_name); \ - if (py_exp_list == NULL && PyErr_Occurred()) { \ + PyObject *py_exp_list = NULL; \ + int retval = \ + PyDict_GetItemRef(py_policy, py_field_name, &py_exp_list); \ + if (retval == -1) { \ PyErr_Clear(); \ Py_DECREF(py_field_name); \ return as_error_update(err, AEROSPIKE_ERR_CLIENT, \ @@ -118,16 +120,16 @@ "from policy dictionary"); \ } \ Py_DECREF(py_field_name); \ - if (py_exp_list) { \ - if (convert_exp_list(self, py_exp_list, &exp_list, err) == \ - AEROSPIKE_OK) { \ - policy->filter_exp = exp_list; \ - *exp_list_p = exp_list; \ - } \ - else { \ - return err->code; \ - } \ + if (convert_exp_list(self, py_exp_list, &exp_list, err) == \ + AEROSPIKE_OK) { \ + policy->filter_exp = exp_list; \ + *exp_list_p = exp_list; \ + } \ + else { \ + Py_DECREF(py_exp_list); \ + return err->code; \ } \ + Py_DECREF(py_exp_list); \ } \ } @@ -276,11 +278,11 @@ static inline void check_and_set_txn_field(as_error *err, "Unable to create Python string \"txn\""); return; } - PyObject *py_obj_txn = - PyDict_GetItemWithError(py_policy, py_txn_field_name); + PyObject *py_obj_txn = NULL; + int retval = PyDict_GetItemRef(py_policy, py_txn_field_name, &py_obj_txn); Py_DECREF(py_txn_field_name); if (py_obj_txn == NULL) { - if (PyErr_Occurred()) { + if (retval == -1) { PyErr_Clear(); as_error_update(err, AEROSPIKE_ERR_CLIENT, "Getting the transaction field from Python policy " From 336cca089438997e9e968df8acd775f4a29f3a15 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 Nov 2024 08:50:05 -0800 Subject: [PATCH 53/66] fix --- src/main/client/batch_apply.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/batch_apply.c b/src/main/client/batch_apply.c index d90d630f40..951d1ff446 100644 --- a/src/main/client/batch_apply.c +++ b/src/main/client/batch_apply.c @@ -15,6 +15,7 @@ ******************************************************************************/ #include +#include "pythoncapi_compat.h" #include #include From 7ae8e737d11ef60be4ef9d7ea23d96bfacf24b11 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 Nov 2024 08:56:57 -0800 Subject: [PATCH 54/66] fix --- src/main/client/batch_apply.c | 2 +- src/main/client/batch_read.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/client/batch_apply.c b/src/main/client/batch_apply.c index 951d1ff446..45a48b4c31 100644 --- a/src/main/client/batch_apply.c +++ b/src/main/client/batch_apply.c @@ -14,8 +14,8 @@ * limitations under the License. ******************************************************************************/ -#include #include "pythoncapi_compat.h" +#include #include #include diff --git a/src/main/client/batch_read.c b/src/main/client/batch_read.c index 907b20ef30..1655ef0fb5 100644 --- a/src/main/client/batch_read.c +++ b/src/main/client/batch_read.c @@ -1,3 +1,4 @@ +#include "pythoncapi_compat.h" #include #include #include From e177301b56624b39ba26dbe248da7a9ba880915e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:14:35 -0800 Subject: [PATCH 55/66] fix --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 9ed44314e2..3bc1d4b4f9 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -374,7 +374,7 @@ jobs: - run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'uv run --module' || 'python3 -m' }} >> $GITHUB_ENV shell: bash - - run: python3 -m pytest -vv new_tests/${{ inputs.test-file }} + - run: ${{ env.PYTHON_M_COMMAND }} pytest -vv new_tests/${{ inputs.test-file }} working-directory: test shell: bash From 604fd9592838e2087d58a5bf52d39f9759071e58 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:54:15 -0800 Subject: [PATCH 56/66] fix --- src/main/policy.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/policy.c b/src/main/policy.c index b9ed0be2b3..974f417b86 100644 --- a/src/main/policy.c +++ b/src/main/policy.c @@ -120,16 +120,18 @@ "from policy dictionary"); \ } \ Py_DECREF(py_field_name); \ - if (convert_exp_list(self, py_exp_list, &exp_list, err) == \ - AEROSPIKE_OK) { \ - policy->filter_exp = exp_list; \ - *exp_list_p = exp_list; \ - } \ - else { \ + if (retval == 1) { \ + if (convert_exp_list(self, py_exp_list, &exp_list, err) == \ + AEROSPIKE_OK) { \ + policy->filter_exp = exp_list; \ + *exp_list_p = exp_list; \ + } \ + else { \ + Py_DECREF(py_exp_list); \ + return err->code; \ + } \ Py_DECREF(py_exp_list); \ - return err->code; \ } \ - Py_DECREF(py_exp_list); \ } \ } From da2c9e8deb03bbb136f5c8c9d13380d7f2094e89 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 21 Nov 2024 08:38:45 -0800 Subject: [PATCH 57/66] never do negative testing on a feature branch --- test/new_tests/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/new_tests/conftest.py b/test/new_tests/conftest.py index 886d3fbaa7..f0a13b3a3e 100644 --- a/test/new_tests/conftest.py +++ b/test/new_tests/conftest.py @@ -77,7 +77,6 @@ def as_connection(request): lua_user_path = os.path.join(sys.exec_prefix, "aerospike", "usr-lua") lua_info = {"user_path": lua_user_path} config["lua"] = lua_info - config["use_shared_connection"] = True # print(config) as_client = None if len(config["hosts"]) == 2: From bfd495976e10c587703e04c46f07139f42ca7254 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:26:42 -0800 Subject: [PATCH 58/66] raise error if shared mem is enabled with nogil --- src/main/client/type.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/client/type.c b/src/main/client/type.c index 8f51ff8a6a..d759774b83 100644 --- a/src/main/client/type.c +++ b/src/main/client/type.c @@ -51,7 +51,8 @@ enum { INIT_DESERIALIZE_ERR, INIT_COMPRESSION_ERR, INIT_POLICY_PARAM_ERR, - INIT_INVALID_AUTHMODE_ERR + INIT_INVALID_AUTHMODE_ERR, + INIT_USING_SHARED_MEMORY_WITH_NOGIL_ERR }; /******************************************************************************* @@ -983,7 +984,13 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, PyObject *py_share_connect = PyDict_GetItemString(py_config, "use_shared_connection"); if (py_share_connect) { +#ifdef Py_GIL_DISABLED + /* code that only runs in the free-threaded build */ + error_code = INIT_USING_SHARED_MEMORY_WITH_NOGIL_ERR; + goto CONSTRUCTOR_ERROR; +#else self->use_shared_connection = PyObject_IsTrue(py_share_connect); +#endif } PyObject *py_send_bool_as = PyDict_GetItemString(py_config, "send_bool_as"); @@ -1139,6 +1146,12 @@ static int AerospikeClient_Type_Init(AerospikeClient *self, PyObject *args, "Specify valid auth_mode"); break; } + case INIT_USING_SHARED_MEMORY_WITH_NOGIL_ERR: { + as_error_update( + &constructor_err, AEROSPIKE_ERR_PARAM, + "Shared connection cannot be enabled with free threading mode"); + break; + } default: // If a generic error was caught during init, use this message as_error_update(&constructor_err, AEROSPIKE_ERR_PARAM, From 581cf14da39c3f686dc0c133e0af5b2016965830 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:31:55 -0800 Subject: [PATCH 59/66] Prevent undefined behavior, don't use global vars for GIL-free --- src/main/aerospike.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/aerospike.c b/src/main/aerospike.c index e430dcc8c9..cb942438bf 100644 --- a/src/main/aerospike.c +++ b/src/main/aerospike.c @@ -42,7 +42,7 @@ #include #include -PyObject *py_global_hosts; +PyObject *py_global_hosts = NULL; int counter = 0xA8000000; bool user_shm_key = false; @@ -579,10 +579,12 @@ PyMODINIT_FUNC PyInit_aerospike(void) Aerospike_Enable_Default_Logging(); +#ifndef Py_GIL_DISABLED py_global_hosts = PyDict_New(); if (py_global_hosts == NULL) { goto MODULE_CLEANUP_ON_ERROR; } +#endif unsigned long i = 0; int retval; From ad5dbcc84c49363167aaa94de551715774608b03 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:49:17 -0800 Subject: [PATCH 60/66] wip. NOT DONE --- src/include/log.h | 7 ------- src/main/aerospike.c | 2 ++ src/main/client/admin.c | 5 +++++ src/main/client/close.c | 1 + src/main/client/connect.c | 2 -- src/main/log.c | 10 ++++------ 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/include/log.h b/src/include/log.h index fa87b13cd7..dd303e9be2 100644 --- a/src/include/log.h +++ b/src/include/log.h @@ -19,13 +19,6 @@ #include #include -/* - * Structure to hold user's log_callback object - */ -typedef struct Aerospike_log_callback { - PyObject *callback; -} AerospikeLogCallback; - /** * Set log level for C-SDK * aerospike.set_log_level( aerospike.LOG_LEVEL_WARN ) diff --git a/src/main/aerospike.c b/src/main/aerospike.c index cb942438bf..b079cdf6e6 100644 --- a/src/main/aerospike.c +++ b/src/main/aerospike.c @@ -43,6 +43,8 @@ #include PyObject *py_global_hosts = NULL; + +// The following are only used when config["use_shm"] is True int counter = 0xA8000000; bool user_shm_key = false; diff --git a/src/main/client/admin.c b/src/main/client/admin.c index 9a0c6ca843..01b4f81510 100644 --- a/src/main/client/admin.c +++ b/src/main/client/admin.c @@ -222,6 +222,8 @@ PyObject *AerospikeClient_Admin_Drop_User(AerospikeClient *self, PyObject *args, aerospike_drop_user(self->as, &err, admin_policy_p, user); Py_END_ALLOW_THREADS + // Assuming this is only used for deleting item in global hosts +#ifndef Py_GIL_DISABLED char *alias_to_search = NULL; alias_to_search = return_search_string(self->as); PyObject *py_persistent_item = NULL; @@ -233,6 +235,7 @@ PyObject *AerospikeClient_Admin_Drop_User(AerospikeClient *self, PyObject *args, } PyMem_Free(alias_to_search); alias_to_search = NULL; +#endif CLEANUP: @@ -416,6 +419,7 @@ PyObject *AerospikeClient_Admin_Change_Password(AerospikeClient *self, aerospike_change_password(self->as, &err, admin_policy_p, user, password); Py_END_ALLOW_THREADS +#ifndef Py_GIL_DISABLED char *alias_to_search = NULL; alias_to_search = return_search_string(self->as); PyObject *py_persistent_item = NULL; @@ -427,6 +431,7 @@ PyObject *AerospikeClient_Admin_Change_Password(AerospikeClient *self, } PyMem_Free(alias_to_search); alias_to_search = NULL; +#endif CLEANUP: diff --git a/src/main/client/close.c b/src/main/client/close.c index 2d1daadd16..7cb8bcf7b8 100644 --- a/src/main/client/close.c +++ b/src/main/client/close.c @@ -60,6 +60,7 @@ PyObject *AerospikeClient_Close(AerospikeClient *self, PyObject *args, goto CLEANUP; } + // This should always be disabled in no-GIL mode if (self->use_shared_connection) { alias_to_search = return_search_string(self->as); py_persistent_item = diff --git a/src/main/client/connect.c b/src/main/client/connect.c index 71bbb8702d..e683412d1f 100644 --- a/src/main/client/connect.c +++ b/src/main/client/connect.c @@ -101,7 +101,6 @@ int AerospikeClientConnect(AerospikeClient *self) } while (1) { flag = 0; - Py_BEGIN_CRITICAL_SECTION(py_global_hosts); while (PyDict_Next(py_global_hosts, &pos, &py_key, &py_value)) { if (((AerospikeGlobalHosts *)py_value)->as->config.use_shm) { if (((AerospikeGlobalHosts *)py_value)->shm_key == @@ -111,7 +110,6 @@ int AerospikeClientConnect(AerospikeClient *self) } } } - Py_END_CRITICAL_SECTION(); if (!flag) { self->as->config.shm_key = shm_key; break; diff --git a/src/main/log.c b/src/main/log.c index fc2090c6db..ad0695da9f 100644 --- a/src/main/log.c +++ b/src/main/log.c @@ -29,8 +29,6 @@ #define __sync_fetch_and_add InterlockedExchangeAdd64 #endif -static AerospikeLogCallback user_callback; - PyObject *Aerospike_Set_Log_Level(PyObject *parent, PyObject *args, PyObject *kwds) { @@ -113,7 +111,8 @@ static bool log_cb(as_log_level level, const char *func, const char *file, va_end(ap); // Extract pyhton user callback - PyObject *py_callback = user_callback.callback; + // TODO: need aerospike module to access callback + PyObject *py_callback = user_callback; // User callback's argument list PyObject *py_arglist = NULL; @@ -164,9 +163,8 @@ PyObject *Aerospike_Set_Log_Handler(PyObject *parent, PyObject *args, &py_callback); if (py_callback && PyCallable_Check(py_callback)) { - // Store user callback - Py_INCREF(py_callback); - user_callback.callback = py_callback; + // Store user callback in aerospike module + PyObject_SetAttrString(parent, "__callback", py_callback); // Register callback to C-SDK as_log_set_callback((as_log_callback)log_cb); From ff768b452354fbfb3883a1bb24b3e27a6db9990a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:39:57 -0800 Subject: [PATCH 61/66] not sure why this is formatted this way --- src/main/geospatial/type.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/geospatial/type.c b/src/main/geospatial/type.c index e2ebb0616c..be0fb2fe80 100644 --- a/src/main/geospatial/type.c +++ b/src/main/geospatial/type.c @@ -133,8 +133,7 @@ static int AerospikeGeospatial_Type_Init(AerospikeGeospatial *self, return 0; } -PyObject *AerospikeGeospatial_Type_Repr(self) -AerospikeGeospatial *self; +PyObject *AerospikeGeospatial_Type_Repr(AerospikeGeospatial *self) { PyObject *initresult = NULL, *py_return = NULL; char *new_repr_str = NULL; @@ -177,8 +176,7 @@ AerospikeGeospatial *self; return py_return; } -PyObject *AerospikeGeospatial_Type_Str(self) -AerospikeGeospatial *self; +PyObject *AerospikeGeospatial_Type_Str(AerospikeGeospatial *self) { PyObject *initresult = NULL; // Aerospike error object From 413638e5642c69971cec32a69b80c517300a90ab Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:27:00 -0800 Subject: [PATCH 62/66] update cibw --- .github/workflows/build-wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index b0c334ff6c..72fb0f3bb0 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -242,7 +242,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build wheel - uses: pypa/cibuildwheel@v2.21.3 + uses: pypa/cibuildwheel@v2.22.0 env: CIBW_ENVIRONMENT_PASS_LINUX: ${{ inputs.unoptimized && 'UNOPTIMIZED' || '' }} CIBW_ENVIRONMENT_MACOS: SSL_LIB_PATH="$(brew --prefix openssl@${{ env.MACOS_OPENSSL_VERSION }})/lib/" CPATH="$(brew --prefix openssl@${{ env.MACOS_OPENSSL_VERSION }})/include/" STATIC_SSL=1 @@ -252,7 +252,7 @@ jobs: yum install python-setuptools -y # delvewheel is not enabled by default but we do need to repair the wheel CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel==1.*" - CIBW_FREE_THREADED_SUPPORT: ${{ matrix.python-tag == 'cp313t' && '1' || '0' }} + CIBW_ENABLE: ${{ matrix.python-tag == 'cp313t' && '1' || '0' }} # We want to check that our wheel links to the new openssl 3 install, not the system default # This assumes that ldd prints out the "soname" for the libraries # We can also manually verify the repair worked by checking the repaired wheel's compatibility tag From b44d0af1318d124b100a71b448c409e0f4073d99 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:34:21 -0800 Subject: [PATCH 63/66] fix --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 72fb0f3bb0..09f33e66c0 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -252,7 +252,7 @@ jobs: yum install python-setuptools -y # delvewheel is not enabled by default but we do need to repair the wheel CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel==1.*" - CIBW_ENABLE: ${{ matrix.python-tag == 'cp313t' && '1' || '0' }} + CIBW_ENABLE: ${{ matrix.python-tag == 'cp313t' && 'cpython-freethreading' || '' }} # We want to check that our wheel links to the new openssl 3 install, not the system default # This assumes that ldd prints out the "soname" for the libraries # We can also manually verify the repair worked by checking the repaired wheel's compatibility tag From a05cc210e3d716b2eb7aabee3319a6b2b1875938 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 19 May 2025 15:02:47 -0700 Subject: [PATCH 64/66] we can use setup-python to download 3.13t --- .github/workflows/build-wheels.yml | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 3a5174bc16..35921e8277 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -362,11 +362,6 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ env.COMMIT_SHA_TO_BUILD_AND_TEST }} - sparse-checkout-cone-mode: false - sparse-checkout: | - !pyproject.toml - test/** - .github/** # Need to be able to save Docker Hub credentials to keychain # Unable to do this via the ansible script for self hosted mac m1 runners @@ -387,7 +382,6 @@ jobs: name: ${{ env.BUILD_IDENTIFIER }}.build - name: Convert Python tag to Python version - if: ${{ matrix.python-tag != 'cp313t' }} # Don't use sed because we want this command to work on both mac and windows # The command used in GNU sed is different than in macOS sed run: | @@ -397,36 +391,18 @@ jobs: shell: bash - uses: actions/setup-python@v5 - if: ${{ matrix.python-tag != 'cp313t' }} with: python-version: ${{ env.PYTHON_VERSION }} - - if: ${{ matrix.python-tag == 'cp313t' }} - uses: astral-sh/setup-uv@v3 - with: - version: "0.4.x" - - - if: ${{ matrix.python-tag == 'cp313t' }} - # Will automatically install Python 3.13t if not present - run: uv venv --python 3.13t - shell: bash - - # TODO: check if uv is installed, instead - - run: echo PIP_COMMAND="${{ matrix.python-tag == 'cp313t' && 'uv' || '' }} pip" >> $GITHUB_ENV - shell: bash - - name: Install wheel - run: ${{ env.PIP_COMMAND }} install aerospike --force-reinstall --no-index --find-links=./ + run: python3 -m pip install aerospike --force-reinstall --no-index --find-links=./ shell: bash - - run: ${{ env.PIP_COMMAND }} install pytest -c requirements.txt + - run: python3 -m pip install pytest -c requirements.txt working-directory: test shell: bash - - run: echo PYTHON_M_COMMAND=${{ matrix.python-tag == 'cp313t' && 'uv run --module' || 'python3 -m' }} >> $GITHUB_ENV - shell: bash - - - run: ${{ env.PYTHON_M_COMMAND }} pytest -vv new_tests/${{ inputs.test-file }} + - run: python3 -m pytest -vv new_tests/${{ inputs.test-file }} working-directory: test shell: bash From d8d26ae3906cc565be288fd350380bb99fe441d0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 19 May 2025 15:18:20 -0700 Subject: [PATCH 65/66] forgot --- src/main/convert_expressions.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/convert_expressions.c b/src/main/convert_expressions.c index 5d592f74b4..6486f485f5 100644 --- a/src/main/convert_expressions.c +++ b/src/main/convert_expressions.c @@ -1700,13 +1700,8 @@ as_status convert_exp_list(AerospikeClient *self, PyObject *py_exp_list, } PyObject *rt_tmp = PyTuple_GetItem(py_expr_tuple, 1); -<<<<<<< HEAD if (!Py_IsNone(rt_tmp)) { - temp_expr.result_type = PyLong_AsLong(rt_tmp); -======= - if (rt_tmp != Py_None) { temp_expr.result_type = PyLong_AsLongLong(rt_tmp); ->>>>>>> origin/dev if (temp_expr.result_type == -1 && PyErr_Occurred()) { as_error_update(err, AEROSPIKE_ERR_PARAM, "Failed to get result_type from expression " From fbacadb5ce6569c8f954578d76cd5ddabf799ad3 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 22 May 2025 13:57:48 -0700 Subject: [PATCH 66/66] fix --- src/main/geospatial/type.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/geospatial/type.c b/src/main/geospatial/type.c index be0fb2fe80..4be0948204 100644 --- a/src/main/geospatial/type.c +++ b/src/main/geospatial/type.c @@ -133,7 +133,7 @@ static int AerospikeGeospatial_Type_Init(AerospikeGeospatial *self, return 0; } -PyObject *AerospikeGeospatial_Type_Repr(AerospikeGeospatial *self) +PyObject *AerospikeGeospatial_Type_Repr(PyObject *self) { PyObject *initresult = NULL, *py_return = NULL; char *new_repr_str = NULL; @@ -147,7 +147,8 @@ PyObject *AerospikeGeospatial_Type_Repr(AerospikeGeospatial *self) goto CLEANUP; } - initresult = AerospikeGeospatial_DoDumps(self->geo_data, &err); + initresult = AerospikeGeospatial_DoDumps( + ((AerospikeGeospatial *)self)->geo_data, &err); if (!initresult) { as_error_update(&err, AEROSPIKE_ERR_CLIENT, "Unable to call get data in str format"); @@ -176,7 +177,7 @@ PyObject *AerospikeGeospatial_Type_Repr(AerospikeGeospatial *self) return py_return; } -PyObject *AerospikeGeospatial_Type_Str(AerospikeGeospatial *self) +PyObject *AerospikeGeospatial_Type_Str(PyObject *self) { PyObject *initresult = NULL; // Aerospike error object @@ -189,7 +190,8 @@ PyObject *AerospikeGeospatial_Type_Str(AerospikeGeospatial *self) goto CLEANUP; } - initresult = AerospikeGeospatial_DoDumps(self->geo_data, &err); + initresult = AerospikeGeospatial_DoDumps( + ((AerospikeGeospatial *)self)->geo_data, &err); if (!initresult) { as_error_update(&err, AEROSPIKE_ERR_CLIENT, "Unable to call get data in str format");