From 156ee69612034b06009066f927e95cbe9ca572e9 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 09:59:10 -0700 Subject: [PATCH 001/127] Replace a few constants with cross refs. Leave TODO's for later --- aerospike_helpers/operations/bitwise_operations.py | 2 ++ aerospike_helpers/operations/map_operations.py | 9 +++++---- doc/aerospike.rst | 4 +++- doc/client.rst | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index f0677bbe28..32d283a273 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -167,6 +167,8 @@ def bit_resize(bin_name: str, byte_size, policy=None, resize_flags: int = 0): bin_name (str): The name of the bin containing the map. byte_size (int): The new size of the bytes. policy (dict): The :ref:`bit_policy ` dictionary. default: None. + # TODO: this is the only method referencing these flags, but should document explicitly that + # the flags can be bitwise OR'd together. resize_flags (int): :ref:`aerospike_bitwise_resize_flag` modifying the resize behavior (default ``aerospike.BIT_RESIZE_DEFAULT``), such as ``aerospike.BIT_RESIZE_GROW_ONLY | aerospike.BIT_RESIZE_FROM_FRONT``. diff --git a/aerospike_helpers/operations/map_operations.py b/aerospike_helpers/operations/map_operations.py index 32f62df5d5..c17a35c896 100755 --- a/aerospike_helpers/operations/map_operations.py +++ b/aerospike_helpers/operations/map_operations.py @@ -968,7 +968,8 @@ def map_remove_by_value_rank_range_relative( Note: This operation requires server version 4.3.0 or greater. - Examples for a key ordered map ``{0: 6, 6: 12, 10: 18, 15: 24}`` and return type of ``aerospike.MAP_RETURN_KEY``: + Examples for a key ordered map ``{0: 6, 6: 12, 10: 18, 15: 24}`` and return type of + :py:data:`aerospike.MAP_RETURN_KEY`: :: @@ -1041,7 +1042,7 @@ def map_get_by_value_rank_range_relative( Note: This operation requires server version 4.3.0 or greater. - Examples for map ``{0: 6, 10: 18, 6: 12, 15: 24}`` and return type of ``aerospike.MAP_RETURN_KEY``. + Examples for map ``{0: 6, 10: 18, 6: 12, 15: 24}`` and return type of :py:data:`aerospike.MAP_RETURN_KEY`. See :meth:`map_remove_by_value_rank_range_relative` for in-depth explanation. :: @@ -1101,7 +1102,7 @@ def map_remove_by_key_index_range_relative( should be considered an internal detail, and subject to change. Examples for a key ordered map ``{0: 6, 6: 12, 10: 18, 15: 24}`` - and return type of ``aerospike.MAP_RETURN_KEY`` + and return type of :py:data:`aerospike.MAP_RETURN_KEY` :: @@ -1175,7 +1176,7 @@ def map_get_by_key_index_range_relative( This operation requires server version 4.3.0 or greater. Examples for a key ordered map ``{0: 6, 6: 12, 10: 18, 15: 24}`` - and return type of ``aerospike.MAP_RETURN_KEY``. + and return type of :py:data:`aerospike.MAP_RETURN_KEY`. See :meth:`map_remove_by_key_index_range_relative` for in-depth explanation. :: diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 3e4571a7c4..111d1a3083 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -11,7 +11,7 @@ Overview :platform: 64-bit Linux and OS X :synopsis: Aerospike client for Python. -``aerospike`` is a package which provides a Python client for Aerospike database clusters. +:py:mod:`aerospike` is a package which provides a Python client for Aerospike database clusters. The Aerospike client enables you to build an application in Python with an Aerospike cluster as its database. The client manages the connections to the @@ -140,6 +140,8 @@ Types A type for distinguishing a server-side null from a Python :py:obj:`None`. Replaces the constant ``aerospike.null``. + .. Needs cross reference to server docs + :return: a type representing the server-side type ``as_null``. .. versionadded:: 2.0.1 diff --git a/doc/client.rst b/doc/client.rst index 329ab109b7..7ca4a3009d 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -483,7 +483,9 @@ Numeric Operations :param int offset: the value by which to increment the value in *bin*. :type offset: :py:class:`int` or :py:class:`float` :param dict meta: record metadata to be set. See :ref:`metadata_dict`. - :param dict policy: optional :ref:`aerospike_operate_policies`. Note: the ``exists`` policy option may not be: ``aerospike.POLICY_EXISTS_CREATE_OR_REPLACE`` nor ``aerospike.POLICY_EXISTS_REPLACE`` + .. Does this still apply? + + :param dict policy: optional :ref:`aerospike_operate_policies`. Note: the ``exists`` policy option may not be: :py:data:`aerospike.POLICY_EXISTS_CREATE_OR_REPLACE` nor :py:data:`aerospike.POLICY_EXISTS_REPLACE` :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. .. code-block:: python From 005bd265f9f62dfee0d474ae2e4b935bd0ddb59f Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:11:49 -0700 Subject: [PATCH 002/127] Follow quick start guide --- .github/workflows/doc-tests.yml | 17 +++++++++++++++++ aerospike_helpers/__init__.py | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/.github/workflows/doc-tests.yml b/.github/workflows/doc-tests.yml index e440389374..1bad3defd1 100644 --- a/.github/workflows/doc-tests.yml +++ b/.github/workflows/doc-tests.yml @@ -54,3 +54,20 @@ jobs: - name: Run builder run: sphinx-build -b ${{ matrix.builder-command }} working-directory: doc + + doctest: + runs-on: ubuntu-22.04 + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + with: + egress-policy: audit + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: 3.12 + + - name: Run doctest + run: python3 aerospike_helpers/__init__.py diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index bad706b072..7f89244297 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -35,3 +35,7 @@ def __repr__(self) -> str: def __str__(self) -> str: return self.__repr__() + +if __name__ == "__main__": + import doctest + doctest.testmod() From a7ac1177614fd7dad9705fa2a24e197c1f184e65 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:13:52 -0700 Subject: [PATCH 003/127] Prevent silent failing --- aerospike_helpers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 7f89244297..042b8437c6 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -38,4 +38,4 @@ def __str__(self) -> str: if __name__ == "__main__": import doctest - doctest.testmod() + doctest.testmod(raise_on_error=True) From b9676ba3ddfeda2f9de47ba118aad5066c8d27d6 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:17:18 -0700 Subject: [PATCH 004/127] Add doctest to test code examples in aerospike module rst docs. TODO - this may make cicd check redundant in doc tests. --- doc/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/conf.py b/doc/conf.py index c015fe4626..020a6c9426 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -37,6 +37,7 @@ def __getattr__(cls, name): extensions = [ "sphinx.ext.todo", "sphinx.ext.autodoc", + "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", "sphinxcontrib.spelling" From 9410da77a825eca4894b80e41e12cb0944ed9ef1 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:38:25 -0700 Subject: [PATCH 005/127] Try running with doc/*.rst to see what files get inspected --- .github/workflows/doc-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc-tests.yml b/.github/workflows/doc-tests.yml index 1bad3defd1..9d69561e50 100644 --- a/.github/workflows/doc-tests.yml +++ b/.github/workflows/doc-tests.yml @@ -70,4 +70,4 @@ jobs: python-version: 3.12 - name: Run doctest - run: python3 aerospike_helpers/__init__.py + run: python3 -m doctest doc/*.rst From 054b8a1ba1d224cd6185c0fdda6fbf9c5aa0d1b0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:40:41 -0700 Subject: [PATCH 006/127] *.rst doesn't show any output. Try checking a specific file --- .github/workflows/doc-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc-tests.yml b/.github/workflows/doc-tests.yml index 9d69561e50..2e6d9ec526 100644 --- a/.github/workflows/doc-tests.yml +++ b/.github/workflows/doc-tests.yml @@ -70,4 +70,4 @@ jobs: python-version: 3.12 - name: Run doctest - run: python3 -m doctest doc/*.rst + run: python3 -m doctest doc/aerospike.rst From 7e1e1e6cf1a4dd1c5017bf7b9c235ab3546230f8 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:44:44 -0700 Subject: [PATCH 007/127] need verbose mode to see number of tests run and whether they passed --- .github/workflows/doc-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc-tests.yml b/.github/workflows/doc-tests.yml index 2e6d9ec526..fe47896a7e 100644 --- a/.github/workflows/doc-tests.yml +++ b/.github/workflows/doc-tests.yml @@ -70,4 +70,4 @@ jobs: python-version: 3.12 - name: Run doctest - run: python3 -m doctest doc/aerospike.rst + run: python3 -m doctest -v doc/aerospike.rst From 2b0446c7ff9c3a1c8a40ae061032114a28024d57 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:52:07 -0700 Subject: [PATCH 008/127] Update code example to be doctest compatible. TODO - see how it appears in sphinx preview --- doc/aerospike.rst | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 3e4571a7c4..373a5b00f3 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -45,18 +45,12 @@ Client Simple example: - .. code-block:: python - - import aerospike - - # Configure the client to first connect to a cluster node at 127.0.0.1 - # The client will learn about the other nodes in the cluster from the seed node. - # Also sets a top level policy for read commands - config = { - 'hosts': [ ('127.0.0.1', 3000) ], - 'policies': {'read': {'total_timeout': 1000}}, - } - client = aerospike.client(config) + >>> import aerospike + >>> config = { + ... 'hosts': [ ('127.0.0.1', 3000) ], + ... 'policies': {'read': {'total_timeout': 1000}} + ... } + >>> client = aerospike.client(config) Connecting using TLS example: From aeee4d69e8d8dc2e4ce17c1f630c7ad2a25c3b87 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:59:53 -0700 Subject: [PATCH 009/127] Move doctest to smoke tests to use python client build in order to import it in the code examples --- .github/workflows/doc-tests.yml | 17 ----------------- .github/workflows/smoke-tests.yml | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/.github/workflows/doc-tests.yml b/.github/workflows/doc-tests.yml index fe47896a7e..e440389374 100644 --- a/.github/workflows/doc-tests.yml +++ b/.github/workflows/doc-tests.yml @@ -54,20 +54,3 @@ jobs: - name: Run builder run: sphinx-build -b ${{ matrix.builder-command }} working-directory: doc - - doctest: - runs-on: ubuntu-22.04 - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 - with: - egress-policy: audit - - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: 3.12 - - - name: Run doctest - run: python3 -m doctest -v doc/aerospike.rst diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index e12244c9c0..b149837349 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -95,6 +95,31 @@ jobs: name: ${{ env.WHEEL_GH_ARTIFACT_NAME }} path: ./dist/*.whl + doctest: + needs: build + runs-on: ubuntu-22.04 + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 + with: + egress-policy: audit + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ env.LOWEST_SUPPORTED_PY_VERSION }} + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: wheel-${{ env.LOWEST_SUPPORTED_PY_VERSION }} + + - name: Install client + run: pip install *.whl + + - name: Run doctest + run: python3 -m doctest -v doc/aerospike.rst + build-and-run-tests-with-debug-interpreter: runs-on: ubuntu-22.04 steps: From cb8f30fdcd10b46ad0b896c3d96c264e32df5742 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:06:56 -0700 Subject: [PATCH 010/127] Reuse job that deploys server to run doctest examples --- .github/workflows/smoke-tests.yml | 32 +++++++------------------------ 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index b149837349..99ec20d011 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -95,31 +95,6 @@ jobs: name: ${{ env.WHEEL_GH_ARTIFACT_NAME }} path: ./dist/*.whl - doctest: - needs: build - runs-on: ubuntu-22.04 - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0 - with: - egress-policy: audit - - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: ${{ env.LOWEST_SUPPORTED_PY_VERSION }} - - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: wheel-${{ env.LOWEST_SUPPORTED_PY_VERSION }} - - - name: Install client - run: pip install *.whl - - - name: Run doctest - run: python3 -m doctest -v doc/aerospike.rst - build-and-run-tests-with-debug-interpreter: runs-on: ubuntu-22.04 steps: @@ -313,6 +288,7 @@ jobs: - sanitizer - dont_validate_keys - lowest_supported_server_version + - doctest fail-fast: false runs-on: ubuntu-22.04 needs: build @@ -345,6 +321,7 @@ jobs: run: pip install *.whl - name: Install test dependencies + if: ${{ matrix.type != 'doctest' }} run: pip install -r test/requirements.txt # We can already detect leaks using valgrind @@ -365,7 +342,12 @@ jobs: run: crudini --existing=param --set config.conf input-validation validate_keys false working-directory: test + - if: ${{ matrix.type == 'doctest' }} + name: Run doctest + run: python3 -m doctest -v doc/aerospike.rst + - name: Run tests + if: ${{ matrix.type != 'doctest' }} # We need to disable capturing output or else we cannot see memory errors reported by # libasan during the test run # Here we also treat DeprecationWarnings as errors to make sure we're showing warnings as expected From ee3036685175bd60f17233718650d1fd70d37ebb Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:11:56 -0700 Subject: [PATCH 011/127] Move doctest to CE test to avoid needing to login as user --- .github/workflows/smoke-tests.yml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 99ec20d011..4386dfc341 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -288,7 +288,6 @@ jobs: - sanitizer - dont_validate_keys - lowest_supported_server_version - - doctest fail-fast: false runs-on: ubuntu-22.04 needs: build @@ -321,7 +320,6 @@ jobs: run: pip install *.whl - name: Install test dependencies - if: ${{ matrix.type != 'doctest' }} run: pip install -r test/requirements.txt # We can already detect leaks using valgrind @@ -342,12 +340,7 @@ jobs: run: crudini --existing=param --set config.conf input-validation validate_keys false working-directory: test - - if: ${{ matrix.type == 'doctest' }} - name: Run doctest - run: python3 -m doctest -v doc/aerospike.rst - - name: Run tests - if: ${{ matrix.type != 'doctest' }} # We need to disable capturing output or else we cannot see memory errors reported by # libasan during the test run # Here we also treat DeprecationWarnings as errors to make sure we're showing warnings as expected @@ -367,6 +360,14 @@ jobs: "3.13", "3.14" ] + type: [ + regular, + doc-test + ] + include: + - type: regular + - type: doc-test + py-version: '3.10' fail-fast: false steps: @@ -395,6 +396,7 @@ jobs: run: pip install *.whl - name: Install test dependencies + if: ${{ matrix.type != 'doctest' }} run: pip install -r test/requirements.txt - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 @@ -407,6 +409,7 @@ jobs: run: docker run -d --name aerospike -p 3000-3002:3000-3002 -e DEFAULT_TTL=2592000 ${{ env.REGISTRY_NAME }}/aerospike/aerospike-server:${{ env.SERVER_TAG }} - name: Create config.conf + if: ${{ matrix.type != 'doctest' }} run: cp config.conf.template config.conf working-directory: test @@ -415,9 +418,14 @@ jobs: container-name: aerospike - name: Run tests + if: ${{ matrix.type != 'doctest' }} run: python -m pytest ./new_tests -vv -W error::pytest.PytestUnraisableExceptionWarning working-directory: test + - if: ${{ matrix.type == 'doctest' }} + name: Run doctest + run: python3 -m doctest -v doc/aerospike.rst + test-ee: runs-on: ubuntu-22.04 needs: build From 5bede578c76094e35e638610be92ecf528ebcdf5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:19:07 -0700 Subject: [PATCH 012/127] Do this to avoid running doc test on every python version --- .github/workflows/smoke-tests.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 4386dfc341..476d49fbe1 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -361,11 +361,9 @@ jobs: "3.14" ] type: [ - regular, - doc-test + regular ] include: - - type: regular - type: doc-test py-version: '3.10' fail-fast: false From 2fd18297b5ad371e2bf30cf12a0c8781f1b741a5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:22:33 -0700 Subject: [PATCH 013/127] fix workflow --- .github/workflows/smoke-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 476d49fbe1..03b8abb867 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -364,7 +364,7 @@ jobs: regular ] include: - - type: doc-test + - type: doctest py-version: '3.10' fail-fast: false From c8fe0075d6990ac1721304ad8a11955e901942ac Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:48:56 -0700 Subject: [PATCH 014/127] Add sphinx_copybutton extension to make copying doctest-formatted code examples easier --- doc/conf.py | 3 ++- doc/requirements.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 020a6c9426..8c45e93892 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -40,7 +40,8 @@ def __getattr__(cls, name): "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", - "sphinxcontrib.spelling" + "sphinxcontrib.spelling", + "sphinx_copybutton" ] napoleon_google_docstring = True intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} diff --git a/doc/requirements.txt b/doc/requirements.txt index 4662a72484..da5efab381 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -2,6 +2,7 @@ sphinx==9.1.0 sphinx-rtd-theme==3.1.0 sphinxcontrib-spelling==8.0.2 +sphinx-copybutton==0.5.2 certifi>=2023.7.22 # not directly required, pinned by Snyk to avoid a vulnerability jinja2>=3.1.3 # not directly required, pinned by Snyk to avoid a vulnerability pygments>=2.15.0 # not directly required, pinned by Snyk to avoid a vulnerability From 865ceae2a55bcc5a608508d4bacd48dd76bb623b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:54:55 -0700 Subject: [PATCH 015/127] Follow 'use and customize' guide for sphinx-copybutton to skip copying prompt chars --- doc/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/conf.py b/doc/conf.py index 8c45e93892..31a7c201e6 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -46,6 +46,8 @@ def __getattr__(cls, name): napoleon_google_docstring = True intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} +copybutton_exclude = '.linenos, .gp' + # Add any paths that contain templates here, relative to this directory. templates_path = [] From 17aa74cf21bdc6eeeed8b61483bcff14dca65686 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 16:00:53 -0700 Subject: [PATCH 016/127] Add back comments --- doc/aerospike.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 373a5b00f3..8b644c35bb 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -45,7 +45,12 @@ Client Simple example: +.. TODO - Multiline comments maybe should be added to API descriptions + >>> import aerospike + >>> # Configure the client to first connect to a cluster node at 127.0.0.1 + >>> # The client will learn about the other nodes in the cluster from the seed node. + >>> # Also sets a top level policy for read commands >>> config = { ... 'hosts': [ ('127.0.0.1', 3000) ], ... 'policies': {'read': {'total_timeout': 1000}} From a15032a9fa13c91c94033a407994e02fa0175bb3 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 16:09:05 -0700 Subject: [PATCH 017/127] Convert all code examples to be doctest compatible TODO - except for the TLS test which needs to be run with a different job --- doc/aerospike.rst | 153 ++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 86 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 8b644c35bb..9c509b4c32 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -64,8 +64,6 @@ Client import aerospike import sys - # NOTE: Use of TLS requires Aerospike Enterprise version >= 3.11 - # and client version 2.1.0 or greater tls_name = "some-server-tls-name" tls_ip = "127.0.0.1" tls_port = 4333 @@ -102,15 +100,12 @@ Geospatial :param dict geo_data: a :class:`dict` representing the geospatial data. :return: an instance of the :py:class:`aerospike.GeoJSON` class. - .. code-block:: python - - import aerospike + >> import aerospike - # Create GeoJSON point using WGS84 coordinates. - latitude = 45.920278 - longitude = 63.342222 - loc = aerospike.geodata({'type': 'Point', - 'coordinates': [longitude, latitude]}) + >> # Create GeoJSON point using WGS84 coordinates. + >> latitude = 45.920278 + >> longitude = 63.342222 + >> loc = aerospike.geodata({'type': 'Point', 'coordinates': [longitude, latitude]}) .. versionadded:: 1.0.54 @@ -122,12 +117,10 @@ Geospatial :param dict geojson_str: a :class:`str` of raw GeoJSON. :return: an instance of the :py:class:`aerospike.GeoJSON` class. - .. code-block:: python - - import aerospike + >> import aerospike - # Create GeoJSON point using WGS84 coordinates. - loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') + >> # Create GeoJSON point using WGS84 coordinates. + >> loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') .. versionadded:: 1.0.54 @@ -151,19 +144,17 @@ Types :return: a type representing a wildcard value. - .. code-block:: python + >> import aerospike + >> from aerospike_helpers.operations import list_operations as list_ops - import aerospike - from aerospike_helpers.operations import list_operations as list_ops + >> client = aerospike.client({'hosts': [('localhost', 3000)]}) + >> key = 'test', 'demo', 1 - client = aerospike.client({'hosts': [('localhost', 3000)]}) - key = 'test', 'demo', 1 - - # get all values of the form [1, ...] from a list of lists. - # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - # [1, 2, 3] and [1, 'a'] - operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] - _, _, bins = client.operate(key, operations) + >> # get all values of the form [1, ...] from a list of lists. + >> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match + >> # [1, 2, 3] and [1, 'a'] + >> operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] + >> _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -175,19 +166,17 @@ Types :return: a type representing an infinite value. - .. code-block:: python + >> import aerospike + >> from aerospike_helpers.operations import list_operations as list_ops - import aerospike - from aerospike_helpers.operations import list_operations as list_ops - - client = aerospike.client({'hosts': [('localhost', 3000)]}) - key = 'test', 'demo', 1 + >> client = aerospike.client({'hosts': [('localhost', 3000)]}) + >> key = 'test', 'demo', 1 - # get all values of the form [1, ...] from a list of lists. - # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - # [1, 2, 3] and [1, 'a'] - operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] - _, _, bins = client.operate(key, operations) + >> # get all values of the form [1, ...] from a list of lists. + >> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match + >> # [1, 2, 3] and [1, 'a'] + >> operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] + >> _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -213,12 +202,10 @@ Serialization .. seealso:: To use this function with :meth:`Client.put`, \ the argument to the serializer parameter should be :const:`aerospike.SERIALIZER_USER`. - .. code-block:: python + >> def my_serializer(val): + ... return json.dumps(val) - def my_serializer(val): - return json.dumps(val) - - aerospike.set_serializer(my_serializer) + >> aerospike.set_serializer(my_serializer) .. versionadded:: 1.0.39 @@ -335,13 +322,11 @@ Other :return: a RIPEMD-160 digest of the input tuple. :rtype: :class:`bytearray` - .. code-block:: python + >> import aerospike + >> import pprint - import aerospike - import pprint - - digest = aerospike.calc_digest("test", "demo", 1 ) - pp.pprint(digest) + >> digest = aerospike.calc_digest("test", "demo", 1 ) + >> pp.pprint(digest) .. _client_config: @@ -373,47 +358,43 @@ Only the `hosts` key is required; the rest of the keys are optional. Invalid client config example: - .. code-block:: python - - import aerospike - - config = { - "validate_keys": True, - "hosts": [ - ("127.0.0.1", 3000) - ], - # The correct key is "user", but "username" may be used by accident - "username": "user", - "password": "password" - } - # This call will raise a ParamError from aerospike.exception - # Exception message should be: - # "username" is an invalid client config dictionary key - client = aerospike.client(config) + >> import aerospike + + >> config = { + ... "validate_keys": True, + ... "hosts": [ + ... ("127.0.0.1", 3000) + ... ], + ... # The correct key is "user", but "username" may be used by accident + ... "username": "user", + ... "password": "password" + ... } + >> # This call will raise a ParamError from aerospike.exception + >> # Exception message should be: + >> # "username" is an invalid client config dictionary key + >> client = aerospike.client(config) Invalid policy example: - .. code-block:: python - - import aerospike - - config = { - "validate_keys": True, - "hosts": [ - ("127.0.0.1", 3000) - ], - } - client = aerospike.client(config) - - key = ("test", "demo", 1) - # "key_policy" is used instead of the correct key named "key" - policy = { - "key_policy": aerospike.POLICY_KEY_SEND - } - # This call will raise a ParamError from aerospike.exception - # Exception message should be: - # "key_policy" is an invalid policy dictionary key - client.get(key, policy=policy) + >> import aerospike + + >> config = { + ... "validate_keys": True, + ... "hosts": [ + ... ("127.0.0.1", 3000) + ... ], + ... } + >> client = aerospike.client(config) + + >> key = ("test", "demo", 1) + >> # "key_policy" is used instead of the correct key named "key" + >> policy = { + ... "key_policy": aerospike.POLICY_KEY_SEND + ... } + >> # This call will raise a ParamError from aerospike.exception + >> # Exception message should be: + >> # "key_policy" is an invalid policy dictionary key + >> client.get(key, policy=policy) * **hosts** (:class:`list`) A list of tuples identifying a node (or multiple nodes) in the cluster. From 50664bcaebb49fd8c084d3d096bf3284caa14a14 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 16:12:14 -0700 Subject: [PATCH 018/127] Fix doctest formatting. Not sure why sphinx didn't fail bc of this --- doc/aerospike.rst | 96 +++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 9c509b4c32..bc4ef18f63 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -100,12 +100,12 @@ Geospatial :param dict geo_data: a :class:`dict` representing the geospatial data. :return: an instance of the :py:class:`aerospike.GeoJSON` class. - >> import aerospike + >>> import aerospike - >> # Create GeoJSON point using WGS84 coordinates. - >> latitude = 45.920278 - >> longitude = 63.342222 - >> loc = aerospike.geodata({'type': 'Point', 'coordinates': [longitude, latitude]}) + >>> # Create GeoJSON point using WGS84 coordinates. + >>> latitude = 45.920278 + >>> longitude = 63.342222 + >>> loc = aerospike.geodata({'type': 'Point', 'coordinates': [longitude, latitude]}) .. versionadded:: 1.0.54 @@ -117,10 +117,10 @@ Geospatial :param dict geojson_str: a :class:`str` of raw GeoJSON. :return: an instance of the :py:class:`aerospike.GeoJSON` class. - >> import aerospike + >>> import aerospike - >> # Create GeoJSON point using WGS84 coordinates. - >> loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') + >>> # Create GeoJSON point using WGS84 coordinates. + >>> loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') .. versionadded:: 1.0.54 @@ -144,17 +144,17 @@ Types :return: a type representing a wildcard value. - >> import aerospike - >> from aerospike_helpers.operations import list_operations as list_ops + >>> import aerospike + >>> from aerospike_helpers.operations import list_operations as list_ops - >> client = aerospike.client({'hosts': [('localhost', 3000)]}) - >> key = 'test', 'demo', 1 + >>> client = aerospike.client({'hosts': [('localhost', 3000)]}) + >>> key = 'test', 'demo', 1 - >> # get all values of the form [1, ...] from a list of lists. - >> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - >> # [1, 2, 3] and [1, 'a'] - >> operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] - >> _, _, bins = client.operate(key, operations) + >>> # get all values of the form [1, ...] from a list of lists. + >>> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match + >>> # [1, 2, 3] and [1, 'a'] + >>> operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] + >>> _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -166,17 +166,17 @@ Types :return: a type representing an infinite value. - >> import aerospike - >> from aerospike_helpers.operations import list_operations as list_ops + >>> import aerospike + >>> from aerospike_helpers.operations import list_operations as list_ops - >> client = aerospike.client({'hosts': [('localhost', 3000)]}) - >> key = 'test', 'demo', 1 + >>> client = aerospike.client({'hosts': [('localhost', 3000)]}) + >>> key = 'test', 'demo', 1 - >> # get all values of the form [1, ...] from a list of lists. - >> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - >> # [1, 2, 3] and [1, 'a'] - >> operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] - >> _, _, bins = client.operate(key, operations) + >>> # get all values of the form [1, ...] from a list of lists. + >>> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match + >>> # [1, 2, 3] and [1, 'a'] + >>> operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] + >>> _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -202,10 +202,10 @@ Serialization .. seealso:: To use this function with :meth:`Client.put`, \ the argument to the serializer parameter should be :const:`aerospike.SERIALIZER_USER`. - >> def my_serializer(val): + >>> def my_serializer(val): ... return json.dumps(val) - >> aerospike.set_serializer(my_serializer) + >>> aerospike.set_serializer(my_serializer) .. versionadded:: 1.0.39 @@ -322,11 +322,11 @@ Other :return: a RIPEMD-160 digest of the input tuple. :rtype: :class:`bytearray` - >> import aerospike - >> import pprint + >>> import aerospike + >>> import pprint - >> digest = aerospike.calc_digest("test", "demo", 1 ) - >> pp.pprint(digest) + >>> digest = aerospike.calc_digest("test", "demo", 1 ) + >>> pp.pprint(digest) .. _client_config: @@ -358,9 +358,9 @@ Only the `hosts` key is required; the rest of the keys are optional. Invalid client config example: - >> import aerospike + >>> import aerospike - >> config = { + >>> config = { ... "validate_keys": True, ... "hosts": [ ... ("127.0.0.1", 3000) @@ -369,32 +369,32 @@ Only the `hosts` key is required; the rest of the keys are optional. ... "username": "user", ... "password": "password" ... } - >> # This call will raise a ParamError from aerospike.exception - >> # Exception message should be: - >> # "username" is an invalid client config dictionary key - >> client = aerospike.client(config) + >>> # This call will raise a ParamError from aerospike.exception + >>> # Exception message should be: + >>> # "username" is an invalid client config dictionary key + >>> client = aerospike.client(config) Invalid policy example: - >> import aerospike + >>> import aerospike - >> config = { + >>> config = { ... "validate_keys": True, ... "hosts": [ ... ("127.0.0.1", 3000) ... ], ... } - >> client = aerospike.client(config) + >>> client = aerospike.client(config) - >> key = ("test", "demo", 1) - >> # "key_policy" is used instead of the correct key named "key" - >> policy = { + >>> key = ("test", "demo", 1) + >>> # "key_policy" is used instead of the correct key named "key" + >>> policy = { ... "key_policy": aerospike.POLICY_KEY_SEND ... } - >> # This call will raise a ParamError from aerospike.exception - >> # Exception message should be: - >> # "key_policy" is an invalid policy dictionary key - >> client.get(key, policy=policy) + >>> # This call will raise a ParamError from aerospike.exception + >>> # Exception message should be: + >>> # "key_policy" is an invalid policy dictionary key + >>> client.get(key, policy=policy) * **hosts** (:class:`list`) A list of tuples identifying a node (or multiple nodes) in the cluster. From 9ba17f01dc173c4e11949cdaeb3b834bde1ba204 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:31:02 -0700 Subject: [PATCH 019/127] Fix reported failures. Exception detail doesn't matter since it's a tuple that contains the line number where it happened which isn't important --- doc/aerospike.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index bc4ef18f63..9d546d9e23 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -206,6 +206,7 @@ Serialization ... return json.dumps(val) >>> aerospike.set_serializer(my_serializer) + 0 .. versionadded:: 1.0.39 @@ -325,8 +326,9 @@ Other >>> import aerospike >>> import pprint - >>> digest = aerospike.calc_digest("test", "demo", 1 ) - >>> pp.pprint(digest) + >>> digest = aerospike.calc_digest("test", "demo", 1) + >>> pprint.pprint(digest) + bytearray(b'\xb7\xf4\xb88\x89\xe2\xdag\xdeh>\x1d\xf6\x91\x9a\x1e\xac\xc4F\xc8') .. _client_config: @@ -369,10 +371,9 @@ Only the `hosts` key is required; the rest of the keys are optional. ... "username": "user", ... "password": "password" ... } - >>> # This call will raise a ParamError from aerospike.exception - >>> # Exception message should be: - >>> # "username" is an invalid client config dictionary key >>> client = aerospike.client(config) + Traceback (most recent call last): + aerospike.exception.ParamError: "username" is an invalid client config dictionary key Invalid policy example: @@ -391,10 +392,9 @@ Only the `hosts` key is required; the rest of the keys are optional. >>> policy = { ... "key_policy": aerospike.POLICY_KEY_SEND ... } - >>> # This call will raise a ParamError from aerospike.exception - >>> # Exception message should be: - >>> # "key_policy" is an invalid policy dictionary key >>> client.get(key, policy=policy) + Traceback (most recent call last): + aerospike.exception.ParamError: "key_policy" is an invalid policy dictionary key * **hosts** (:class:`list`) A list of tuples identifying a node (or multiple nodes) in the cluster. From 14d5820b6b7b4b301ff3d16e6e03adc7770e06a8 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:49:17 -0700 Subject: [PATCH 020/127] Add custom script to run all doctest code examples. This allows us to run boilerplate code before each example in client.rst --- .github/workflows/smoke-tests.yml | 3 ++- doc/client.rst | 10 ++++------ doc/doctest.py | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 doc/doctest.py diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 03b8abb867..43445e92b0 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -422,7 +422,8 @@ jobs: - if: ${{ matrix.type == 'doctest' }} name: Run doctest - run: python3 -m doctest -v doc/aerospike.rst + run: python3 -m doctest -v doctest.py + working-directory: doc test-ee: runs-on: ubuntu-22.04 diff --git a/doc/client.rst b/doc/client.rst index 329ab109b7..4d33f0dc11 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -434,12 +434,10 @@ String Operations :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python - - client.put(keyTuple, {'bin1': 'Martin Luther King'}) - client.append(keyTuple, 'bin1', ' jr.') - (_, _, bins) = client.get(keyTuple) - print(bins) # Martin Luther King jr. + >>> client.put(keyTuple, {'bin1': 'Martin Luther King'}) + >>> client.append(keyTuple, 'bin1', ' jr.') + >>> (_, _, bins) = client.get(keyTuple) + >>> print(bins) # Martin Luther King jr. .. method:: prepend(key, bin, val[, meta: dict[, policy: dict]]) diff --git a/doc/doctest.py b/doc/doctest.py new file mode 100644 index 0000000000..328427bf9b --- /dev/null +++ b/doc/doctest.py @@ -0,0 +1,20 @@ +import doctest +import unittest +import runpy + + +def custom_setup(test): + # This code runs before every doctest in the suite + # test.globs['shared_data'] = [1, 2, 3] + # TODO: should use file location and not cwd + runpy.run_path('./examples/boilerplate.py') + +def load_tests(loader, tests, ignore): + # Add setup and teardown logic here + # TODO: should use file location and not cwd + tests.addTests(doctest.DocFileSuite(["./aerospike.rst"])) + tests.addTests(doctest.DocFileSuite(["./client.rst"], setUp=custom_setup)) + return tests + +if __name__ == "__main__": + unittest.main() From 667460f445be76f8aa0d137e2441e7d16c5480fc Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:53:34 -0700 Subject: [PATCH 021/127] Mv script name to prevent shadowing doctest package --- .github/workflows/smoke-tests.yml | 2 +- doc/{doctest.py => check-code-examples-in-docs.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename doc/{doctest.py => check-code-examples-in-docs.py} (100%) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 43445e92b0..d26d3b863e 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -422,7 +422,7 @@ jobs: - if: ${{ matrix.type == 'doctest' }} name: Run doctest - run: python3 -m doctest -v doctest.py + run: python3 -m doctest -v check-code-examples-in-docs.py working-directory: doc test-ee: diff --git a/doc/doctest.py b/doc/check-code-examples-in-docs.py similarity index 100% rename from doc/doctest.py rename to doc/check-code-examples-in-docs.py From 86b1f35a67e33f69f6af8a907be30d04c10c2504 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:58:02 -0700 Subject: [PATCH 022/127] Fix workflow --- .github/workflows/smoke-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index d26d3b863e..fd6f56a2a4 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -422,7 +422,7 @@ jobs: - if: ${{ matrix.type == 'doctest' }} name: Run doctest - run: python3 -m doctest -v check-code-examples-in-docs.py + run: python3 check-code-examples-in-docs.py working-directory: doc test-ee: From 1758e561af02c9f40b289037cac6ef1ed324f66f Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:38:29 -0700 Subject: [PATCH 023/127] Try using this method instead to run the boilerplate code before every code example. It doesn't seem possible with doctest + unittest --- doc/client.rst | 36 ++++++++++++++++++++++++++++++------ doc/examples/boilerplate.py | 20 -------------------- 2 files changed, 30 insertions(+), 26 deletions(-) delete mode 100644 doc/examples/boilerplate.py diff --git a/doc/client.rst b/doc/client.rst index 4d33f0dc11..eca06ca85a 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -31,8 +31,29 @@ Assume every in-line example runs this code beforehand: .. warning:: Only run example code on a brand new Aerospike server. This code deletes all records in the ``demo`` set! -.. include:: examples/boilerplate.py - :code: python + +.. testsetup:: + + # Imports + import aerospike + from aerospike import exception as ex + import sys + + # Configure the client + config = { + 'hosts': [ ('127.0.0.1', 3000)] + } + + # Create a client and connect it to the cluster + try: + client = aerospike.client(config) + client.truncate('test', "demo", 0) + except ex.ClientError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) + + # Record key tuple: (namespace, set, key) + keyTuple = ('test', 'demo', 'key') Basic example: @@ -434,10 +455,13 @@ String Operations :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - >>> client.put(keyTuple, {'bin1': 'Martin Luther King'}) - >>> client.append(keyTuple, 'bin1', ' jr.') - >>> (_, _, bins) = client.get(keyTuple) - >>> print(bins) # Martin Luther King jr. + + .. doctest:: + + >>> client.put(keyTuple, {'bin1': 'Martin Luther King'}) + >>> client.append(keyTuple, 'bin1', ' jr.') + >>> (_, _, bins) = client.get(keyTuple) + >>> print(bins) # Martin Luther King jr. .. method:: prepend(key, bin, val[, meta: dict[, policy: dict]]) diff --git a/doc/examples/boilerplate.py b/doc/examples/boilerplate.py deleted file mode 100644 index 74a7adff80..0000000000 --- a/doc/examples/boilerplate.py +++ /dev/null @@ -1,20 +0,0 @@ -# Imports -import aerospike -from aerospike import exception as ex -import sys - -# Configure the client -config = { - 'hosts': [ ('127.0.0.1', 3000)] -} - -# Create a client and connect it to the cluster -try: - client = aerospike.client(config) - client.truncate('test', "demo", 0) -except ex.ClientError as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - sys.exit(1) - -# Record key tuple: (namespace, set, key) -keyTuple = ('test', 'demo', 'key') From f7664a2eca86640661b49b886514fcc01d2bd53a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:47:29 -0700 Subject: [PATCH 024/127] Use doctest builder to run doctest in client.rst --- .github/workflows/smoke-tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index fd6f56a2a4..7ae90ddc90 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -420,9 +420,13 @@ jobs: run: python -m pytest ./new_tests -vv -W error::pytest.PytestUnraisableExceptionWarning working-directory: test + - name: Install test dependencies + if: ${{ matrix.type == 'doctest' }} + run: pip install -r doc/requirements.txt + - if: ${{ matrix.type == 'doctest' }} name: Run doctest - run: python3 check-code-examples-in-docs.py + run: sphinx-build -b doctest working-directory: doc test-ee: From 0b3af6d5fcbe80ce682942618d69da628469c6f5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:50:42 -0700 Subject: [PATCH 025/127] Forgot that pinned sphinx version requires 3.12 or higher --- .github/workflows/smoke-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 7ae90ddc90..1314714006 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -365,7 +365,7 @@ jobs: ] include: - type: doctest - py-version: '3.10' + py-version: '3.12' fail-fast: false steps: From bebf00093f4858efd781b2cfb90d7b6c2d62caa0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 12:55:08 -0700 Subject: [PATCH 026/127] Fix syntax --- .github/workflows/smoke-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 1314714006..e8760c9901 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -426,7 +426,7 @@ jobs: - if: ${{ matrix.type == 'doctest' }} name: Run doctest - run: sphinx-build -b doctest + run: sphinx-build -b doctest . doctest working-directory: doc test-ee: From a59305f11f6954d5c50d41f22a28cf8710c3ea73 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:32:48 -0700 Subject: [PATCH 027/127] Noticed some aerospike.* methods in aerospike.rst are returning MagicMock() --- doc/conf.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 31a7c201e6..c393fc0327 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -2,14 +2,14 @@ import sys, os -try: - from unittest.mock import MagicMock -except ImportError: - try: - from mock import Mock as MagicMock - except ImportError as e: - print("mock is missing: pip install mock") - raise e +# try: +# from unittest.mock import MagicMock +# except ImportError: +# try: +# from mock import Mock as MagicMock +# except ImportError as e: +# print("mock is missing: pip install mock") +# raise e # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -20,13 +20,13 @@ # see https://docs.readthedocs.io/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules -class Mock(MagicMock): - @classmethod - def __getattr__(cls, name): - return MagicMock() +# class Mock(MagicMock): +# @classmethod +# def __getattr__(cls, name): +# return MagicMock() -sys.modules.update({"aerospike": Mock()}) +# sys.modules.update({"aerospike": Mock()}) # sys.path.append(os.path.abspath('/usr/local/lib/python2.7/site-packages/aerospike-1.0.44-py2.7-macosx-10.9-x86_64.egg/')) From 52418becae4267f8974d03c2541a1d9c6461af04 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:49:10 -0700 Subject: [PATCH 028/127] Fix HyperLogLog code example --- aerospike_helpers/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 042b8437c6..74ff998035 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -20,7 +20,8 @@ class HyperLogLog(bytes): The constructor takes in any argument that the :class:`bytes` constructor takes in. - >>> h = HyperLogLog([1, 2, 3]) + >>> import aerospike + >>> h = aerospike.HyperLogLog([1, 2, 3]) >>> client.put(key, {"hyperloglog": h}) """ def __new__(cls, o) -> "HyperLogLog": From d04848c317e22c36ad3aa1235d113f980ed626b6 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:51:33 -0700 Subject: [PATCH 029/127] Fix formatting. Output here is correct --- doc/client.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index eca06ca85a..5fd1cd9227 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -459,9 +459,12 @@ String Operations .. doctest:: >>> client.put(keyTuple, {'bin1': 'Martin Luther King'}) + 0 >>> client.append(keyTuple, 'bin1', ' jr.') + 0 >>> (_, _, bins) = client.get(keyTuple) - >>> print(bins) # Martin Luther King jr. + >>> print(bins) + {'bin1': 'Martin Luther King jr.'} .. method:: prepend(key, bin, val[, meta: dict[, policy: dict]]) From 2d047be5ecb1d5ccc282d1844ca15a5659b3bbe3 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:03:48 -0700 Subject: [PATCH 030/127] Try testcode block, if it works then just move the other code examples to this format (takes less work) --- doc/client.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/client.rst b/doc/client.rst index 5fd1cd9227..9b6978c0cc 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -480,12 +480,16 @@ String Operations :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: client.put(keyTuple, {'bin1': 'Freeman'}) client.prepend(keyTuple, 'bin1', ' Gordon ') (_, _, bins) = client.get(keyTuple) - print(bins) # Gordon Freeman + print(bins) + + .. testoutput:: + + {"bin1": "Gordon Freeman"} .. index:: single: Numeric Operations From efb9f786f27134538763d9c699ed391c37ee7893 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:07:53 -0700 Subject: [PATCH 031/127] Fix hyperloglog.. --- aerospike_helpers/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 74ff998035..f99911b83b 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -20,8 +20,8 @@ class HyperLogLog(bytes): The constructor takes in any argument that the :class:`bytes` constructor takes in. - >>> import aerospike - >>> h = aerospike.HyperLogLog([1, 2, 3]) + >>> from aerospike_helpers import HyperLogLog + >>> h = HyperLogLog([1, 2, 3]) >>> client.put(key, {"hyperloglog": h}) """ def __new__(cls, o) -> "HyperLogLog": From 153cfa775cabe61274f6f0d68b9520461c67a23a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:09:27 -0700 Subject: [PATCH 032/127] Think it's safe to remove code to run doctest if we are already using sphinx doctest plugin --- aerospike_helpers/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index f99911b83b..aa482023aa 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -36,7 +36,3 @@ def __repr__(self) -> str: def __str__(self) -> str: return self.__repr__() - -if __name__ == "__main__": - import doctest - doctest.testmod(raise_on_error=True) From 522334872fe0732bde7269812bfbfea867e86949 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:11:15 -0700 Subject: [PATCH 033/127] Fix prepend code example --- doc/client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index 9b6978c0cc..6c4e95abb9 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -483,7 +483,7 @@ String Operations .. testcode:: client.put(keyTuple, {'bin1': 'Freeman'}) - client.prepend(keyTuple, 'bin1', ' Gordon ') + client.prepend(keyTuple, 'bin1', 'Gordon ') (_, _, bins) = client.get(keyTuple) print(bins) From 71c6941d37cb3449595032b85dd81a285f021acd Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:30:36 -0700 Subject: [PATCH 034/127] Make most of the code examples in client.rst testable. --- doc/client.rst | 111 +++++++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 44 deletions(-) diff --git a/doc/client.rst b/doc/client.rst index 6c4e95abb9..e7b5b75d0c 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -515,7 +515,7 @@ Numeric Operations :param dict policy: optional :ref:`aerospike_operate_policies`. Note: the ``exists`` policy option may not be: ``aerospike.POLICY_EXISTS_CREATE_OR_REPLACE`` nor ``aerospike.POLICY_EXISTS_REPLACE`` :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: # Start with 100 lives client.put(keyTuple, {'lives': 100}) @@ -523,12 +523,17 @@ Numeric Operations # Gain health client.increment(keyTuple, 'lives', 10) (key, meta, bins) = client.get(keyTuple) - print(bins) # 110 + print("Lives:", bins) # Take damage client.increment(keyTuple, 'lives', -90) (key, meta, bins) = client.get(keyTuple) - print(bins) # 20 + print("Lives:", bins) + + .. testoutput:: + + Lives: 110 + Lives: 20 .. index:: single: List Operations @@ -603,8 +608,11 @@ User Defined Functions .. note:: To run this example, do not run the boilerplate code. - .. code-block:: python - :emphasize-lines: 5,9 + .. TODO - probably there is better syntax than using emphasize-lines with hardcoded numbers + + .. :emphasize-lines: 5,9 + + .. testcode:: import aerospike @@ -627,7 +635,7 @@ User Defined Functions :param dict policy: currently **timeout** in milliseconds is the available policy. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: client.udf_remove('my_module.lua') @@ -639,19 +647,22 @@ User Defined Functions :rtype: :class:`list` :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: print(client.udf_list()) - # [ - # {'content': bytearray(b''), - # 'hash': bytearray(b'195e39ceb51c110950bd'), - # 'name': 'my_udf1.lua', - # 'type': 0}, - # {'content': bytearray(b''), - # 'hash': bytearray(b'8a2528e8475271877b3b'), - # 'name': 'stream_udf.lua', - # 'type': 0} - # ] + + .. testoutput:: + + [ + {'content': bytearray(b''), + 'hash': bytearray(b'195e39ceb51c110950bd'), + 'name': 'my_udf1.lua', + 'type': 0}, + {'content': bytearray(b''), + 'hash': bytearray(b'8a2528e8475271877b3b'), + 'name': 'stream_udf.lua', + 'type': 0} + ] .. method:: udf_get(module: str[, language: int = aerospike.UDF_TYPE_LUA[, policy: dict]]) -> str @@ -747,12 +758,15 @@ Info Operations :return: a :class:`list` of node info dictionaries. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: # Assuming two nodes nodes = client.get_node_names() print(nodes) - # [{'address': '1.1.1.1', 'port': 3000, 'node_name': 'BCER199932C'}, {'address': '1.1.1.1', 'port': 3010, 'node_name': 'ADFFE7782CD'}] + + .. testoutput:: + + [{'address': '1.1.1.1', 'port': 3000, 'node_name': 'BCER199932C'}, {'address': '1.1.1.1', 'port': 3010, 'node_name': 'ADFFE7782CD'}] .. versionchanged:: 6.0.0 @@ -763,12 +777,15 @@ Info Operations :return: a :class:`list` of node address tuples. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: # Assuming two nodes nodes = client.get_nodes() print(nodes) - # [('127.0.0.1', 3000), ('127.0.0.1', 3010)] + + .. testoutput:: + + [('127.0.0.1', 3000), ('127.0.0.1', 3010)] .. versionchanged:: 3.0.0 @@ -797,11 +814,14 @@ Info Operations :rtype: :class:`dict` :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. code-block:: python + .. testcode:: response = client.info_all("namespaces") print(response) - # {'BB9020011AC4202': (None, 'test\n')} + + .. testoutput:: + + {'BB9020011AC4202': (None, 'test\n')} .. versionadded:: 3.0.0 @@ -946,7 +966,7 @@ Index Operations .. note:: Requires server version >= 3.8.0 - .. code-block:: python + .. testcode:: import aerospike @@ -1067,9 +1087,7 @@ Index Operations .. note:: Requires server version >= 3.7.0 - .. code-block:: python - - import aerospike + .. testcode:: client = aerospike.client({ 'hosts': [ ('127.0.0.1', 3000)]}) client.index_geo2dsphere_create('test', 'pads', 'loc', 'pads_loc_geo') @@ -1478,7 +1496,7 @@ Key Tuple * How to use the key tuple in a `put` operation * How to fetch the key tuple in a `get` operation - .. code-block:: python + .. testcode:: import aerospike @@ -1494,7 +1512,7 @@ Key Tuple keyTuple = (namespaceName, setName, primaryKeyName) # Insert a record - recordBins = {'bin1':0, 'bin2':1} + recordBins = {'bin1': 0, 'bin2': 1} client.put(keyTuple, recordBins) # Now fetch that record @@ -1505,13 +1523,14 @@ Key Tuple # and there is the record's digest print(key) - # Expected output: - # ('test', 'setname', None, bytearray(b'b\xc7[\xbb\xa4K\xe2\x9al\xd12!&\xbf<\xd9\xf9\x1bPo')) - # Cleanup client.remove(keyTuple) client.close() + .. testoutput:: + + ('test', 'setname', None, bytearray(b'b\xc7[\xbb\xa4K\xe2\x9al\xd12!&\xbf<\xd9\xf9\x1bPo')) + .. seealso:: `Data Model: Keys and Digests `_. .. _aerospike_record_tuple: @@ -1544,7 +1563,7 @@ Record Tuple We reuse the code example in the key-tuple section and print the ``meta`` and ``bins`` values that were returned from :meth:`~aerospike.Client.get()`: - .. code-block:: python + .. testcode:: import aerospike @@ -1559,25 +1578,27 @@ Record Tuple keyTuple = (namespaceName, setName, primaryKeyName) # Insert a record - recordBins = {'bin1':0, 'bin2':1} + recordBins = {'bin1': 0, 'bin2': 1} client.put(keyTuple, recordBins) # Now fetch that record (key, meta, bins) = client.get(keyTuple) # Generation is 1 because this is the first time we wrote the record - print(meta) - - # Expected output: - # {'ttl': 2592000, 'gen': 1} + print("Metadata:", meta) # The bin-value pairs we inserted - print(bins) - {'bin1': 0, 'bin2': 1} + print("Bins:", bins) client.remove(keyTuple) client.close() + .. testoutput:: + + Metadata: {'ttl': 2592000, 'gen': 1} + Bins: {'bin1': 0, 'bin2': 1} + + .. seealso:: `Data Model: Records `_. .. _metadata_dict: @@ -2329,7 +2350,7 @@ List Policies Example: - .. code-block:: python + .. testcode:: list_policy = { "write_flags": aerospike.LIST_WRITE_ADD_UNIQUE | aerospike.LIST_WRITE_INSERT_BOUNDED, @@ -2374,7 +2395,7 @@ Map Policies Example: - .. code-block:: python + .. testcode:: # Server >= 4.3.0 map_policy = { @@ -2404,7 +2425,7 @@ Bit Policies Example: - .. code-block:: python + .. testcode:: bit_policy = { 'bit_write_flags': aerospike.BIT_WRITE_UPDATE_ONLY @@ -2432,7 +2453,7 @@ HyperLogLog Policies Example: - .. code-block:: python + .. testcode:: HLL_policy = { 'flags': aerospike.HLL_WRITE_UPDATE_ONLY @@ -2516,6 +2537,8 @@ Partition Objects Default: ``{}`` (All partitions will be queried/scanned). + .. TODO more thorough example needed here. + .. code-block:: python # Example of a query policy using partition_filter. From 03fbd1318fcd074cfb38c2bfd12d1f8cfdde4fa4 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:44:50 -0700 Subject: [PATCH 035/127] Test if this command works in aerospike_helpers --- aerospike_helpers/cdt_ctx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/cdt_ctx.py b/aerospike_helpers/cdt_ctx.py index 7aa758fd40..ba0a04b502 100644 --- a/aerospike_helpers/cdt_ctx.py +++ b/aerospike_helpers/cdt_ctx.py @@ -17,7 +17,7 @@ Helper functions to generate complex data type context (cdt_ctx) objects for use with operations on nested CDTs (list, map, etc). -Example:: +.. testcode:: import aerospike from aerospike import exception as ex From 6e869378c5a6d65c3e5679c968709cd32721817d Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:56:45 -0700 Subject: [PATCH 036/127] Try this setting in order to build docs for aerospike_helpers. --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index c393fc0327..c603734228 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -26,7 +26,7 @@ # return MagicMock() -# sys.modules.update({"aerospike": Mock()}) +autodoc_mock_imports = ["aerospike"] # sys.path.append(os.path.abspath('/usr/local/lib/python2.7/site-packages/aerospike-1.0.44-py2.7-macosx-10.9-x86_64.egg/')) From 7cb953d5e0562e3c02581d334d7fede70964e4b4 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:07:19 -0700 Subject: [PATCH 037/127] Add back example label. --- aerospike_helpers/cdt_ctx.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aerospike_helpers/cdt_ctx.py b/aerospike_helpers/cdt_ctx.py index ba0a04b502..f8d30eb84d 100644 --- a/aerospike_helpers/cdt_ctx.py +++ b/aerospike_helpers/cdt_ctx.py @@ -17,6 +17,8 @@ Helper functions to generate complex data type context (cdt_ctx) objects for use with operations on nested CDTs (list, map, etc). +Example: + .. testcode:: import aerospike From 8ddf4989c0ab26b874c68e7ffd9896a6297cdd7a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:13:21 -0700 Subject: [PATCH 038/127] Replace 'Example::' with testcode directive for sphinx doctest to check --- aerospike_helpers/batch/records.py | 20 ++- aerospike_helpers/expressions/arithmetic.py | 57 +++++-- aerospike_helpers/expressions/base.py | 160 +++++++++++++----- aerospike_helpers/expressions/bitwise.py | 76 ++++++--- .../expressions/bitwise_operators.py | 40 +++-- aerospike_helpers/expressions/hll.py | 36 +++- aerospike_helpers/expressions/list.py | 124 ++++++++++---- aerospike_helpers/expressions/map.py | 148 ++++++++++++---- aerospike_helpers/metrics/__init__.py | 4 +- .../operations/bitwise_operations.py | 8 +- .../operations/expression_operations.py | 8 +- .../operations/hll_operations.py | 4 +- 12 files changed, 514 insertions(+), 171 deletions(-) diff --git a/aerospike_helpers/batch/records.py b/aerospike_helpers/batch/records.py index 5b0a8cfd92..92dff87be8 100644 --- a/aerospike_helpers/batch/records.py +++ b/aerospike_helpers/batch/records.py @@ -81,7 +81,9 @@ def __init__( self, key: tuple, ops: "TypeOps", meta: Optional[dict] = None, policy: "TypeBatchPolicyWrite" = None ) -> None: """ - Example:: + Example: + + .. testcode:: # Create a batch Write to increment bin "a" by 10 and read the result from the record. import aerospike @@ -140,7 +142,9 @@ def __init__( policy: "TypeBatchPolicyRead" = None, ) -> None: """ - Example:: + Example: + + .. testcode:: # Create a batch Read to read bin "a" from the record. import aerospike @@ -192,7 +196,9 @@ def __init__( self, key: tuple, module: str, function: str, args: "TypeUDFArgs", policy: "TypeBatchPolicyApply" = None ) -> None: """ - Example:: + Example: + + .. testcode:: # Create a batch Apply to apply UDF "test_func" to bin "a" from the record. # Assume that "test_func" takes a bin name string as an argument. @@ -240,7 +246,9 @@ class Remove(BatchRecord): def __init__(self, key: tuple, policy: "TypeBatchPolicyRemove" = None) -> None: """ - Example:: + Example: + + .. testcode:: # Create a batch Remove to remove the record. import aerospike_helpers.operations as op @@ -278,7 +286,9 @@ class BatchRecords: def __init__(self, batch_records: Optional[TypeBatchRecordList] = None) -> None: """ - Example:: + Example: + + .. testcode:: import aerospike import aerospike_helpers.operations.operations as op diff --git a/aerospike_helpers/expressions/arithmetic.py b/aerospike_helpers/expressions/arithmetic.py index 8ec1012e17..d231e1a47b 100644 --- a/aerospike_helpers/expressions/arithmetic.py +++ b/aerospike_helpers/expressions/arithmetic.py @@ -53,7 +53,10 @@ def __init__(self, *args: "TypeNumber"): :return: (integer or float value). - Example:: + Example: + + .. testcode:: + # Integer bin "a" + "b" == 11 expr = exp.Eq(exp.Add(exp.IntBin("a"), exp.IntBin("b")), 11).compile() @@ -86,7 +89,9 @@ def __init__(self, *args: "TypeNumber"): :return: (integer or float value) - Example:: + Example: + + .. testcode:: # Integer bin "a" - "b" == 11 expr = exp.Eq(exp.Sub(exp.IntBin("a"), exp.IntBin("b")), 11).compile() @@ -117,7 +122,9 @@ def __init__(self, *args: "TypeNumber"): :return: (integer or float value) - Example:: + Example: + + .. testcode:: # Integer bin "a" * "b" >= 11 expr = exp.GE(exp.Mul(exp.IntBin("a"), exp.IntBin("b")), 11).compile() @@ -150,7 +157,9 @@ def __init__(self, *args: "TypeNumber"): :return: (integer or float value) - Example:: + Example: + + .. testcode:: # Integer bin "a" / "b" / "c" >= 11 expr = exp.GE(exp.Div(exp.IntBin("a"), exp.IntBin("b"), exp.IntBin("c")), 11).compile() @@ -184,7 +193,9 @@ def __init__(self, base: "TypeFloat", exponent: "TypeFloat"): :return: (float value) - Example:: + Example: + + .. testcode:: # Float bin "a" ** 2.0 == 16.0 expr = exp.Eq(exp.Pow(exp.FloatBin("a"), 2.0), 16.0).compile() @@ -212,7 +223,9 @@ def __init__(self, num: "TypeFloat", base: "TypeFloat"): :return: (float value) - Example:: + Example: + + .. testcode:: # For float bin "a", log("a", 2.0) == 16.0 expr = exp.Eq(exp.Log(exp.FloatBin("a"), 2.0), 16.0).compile() @@ -240,7 +253,9 @@ def __init__(self, numerator: "TypeInteger", denominator: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # For int bin "a" % 10 == 0 expr = exp.Eq(exp.Mod(exp.IntBin("a"), 10), 0).compile() @@ -270,7 +285,9 @@ def __init__(self, value: "TypeNumber"): :return: (number value) - Example:: + Example: + + .. testcode:: # For int bin "a", abs("a") == 1 expr = exp.Eq(exp.Abs(exp.IntBin("a")), 1).compile() @@ -300,7 +317,9 @@ def __init__(self, value: "TypeFloat"): :return: (float value) - Example:: + Example: + + .. testcode:: # Floor(2.25) == 2.0 expr = exp.Eq(exp.Floor(2.25), 2.0).compile() @@ -331,7 +350,9 @@ def __init__(self, value: "TypeFloat"): :return: (float value) - Example:: + Example: + + .. testcode:: # Ceil(2.25) == 3.0 expr = exp.Eq(exp.Ceil(2.25), 3.0).compile() @@ -357,7 +378,9 @@ def __init__(self, value: "TypeFloat"): :return: (integer value) - Example:: + Example: + + .. testcode:: #For float bin "a", int(exp.FloatBin("a")) == 2 expr = exp.Eq(exp.ToInt(exp.FloatBin("a")), 2).compile() @@ -379,7 +402,9 @@ def __init__(self, value: "TypeInteger"): :return: (float value) - Example:: + Example: + + .. testcode:: #For int bin "a", float(exp.IntBin("a")) == 2 expr = exp.Eq(exp.ToFloat(exp.IntBin("a")), 2).compile() @@ -404,7 +429,9 @@ def __init__(self, *args: "TypeNumber"): :return: (integer or float value). - Example:: + Example: + + .. testcode:: # for integer bins a, b, c, min(a, b, c) > 0 expr = exp.GT(exp.Min(exp.IntBin("a"), exp.IntBin("b"), exp.IntBin("c")), 0).compile() @@ -428,7 +455,9 @@ def __init__(self, *args: "TypeNumber"): :return: (integer or float value). - Example:: + Example: + + .. testcode:: # for integer bins a, b, c, max(a, b, c) > 100 expr = exp.GT(exp.Max(exp.IntBin("a"), exp.IntBin("b"), exp.IntBin("c")), 100).compile() diff --git a/aerospike_helpers/expressions/base.py b/aerospike_helpers/expressions/base.py index e4280fb642..84f5efa17b 100644 --- a/aerospike_helpers/expressions/base.py +++ b/aerospike_helpers/expressions/base.py @@ -85,7 +85,9 @@ class Unknown(_BaseExpr): def __init__(self): """:return: (unknown value) - Example:: + Example: + + .. testcode:: from aerospike_helpers.expressions.arithmetic import Add @@ -122,7 +124,9 @@ class KeyInt(_Key): def __init__(self): """:return: (integer value): Integer value of the key if the key is an integer. - Example:: + Example: + + .. testcode:: # Integer record key >= 10000. expr = exp.GE(exp.KeyInt(), 10000).compile() @@ -140,7 +144,9 @@ class KeyStr(_Key): def __init__(self): """:return: (string value): string value of the key if the key is an string. - Example:: + Example: + + .. testcode:: # string record key == "aaa". expr = exp.Eq(exp.KeyStr(), "aaa").compile() @@ -158,7 +164,9 @@ class KeyBlob(_Key): def __init__(self): """:return: (blob value): Blob value of the key if the key is a blob. - Example:: + Example: + + .. testcode:: # blob record key <= bytearray([0x65, 0x65]). expr = exp.GE(exp.KeyBlob(), bytearray([0x65, 0x65])).compile() @@ -178,7 +186,9 @@ class KeyExists(_BaseExpr): def __init__(self): """:return: (boolean value): True if the record has a stored key, false otherwise. - Example:: + Example: + + .. testcode:: # Key exists in record meta data. expr = exp.KeyExists().compile() @@ -205,7 +215,9 @@ def __init__(self, bin: str): :return: (boolean bin) - Example:: + Example: + + .. testcode:: # Boolean bin "a" is True. expr = exp.BoolBin("a").compile() @@ -227,7 +239,9 @@ def __init__(self, bin: str): :return: (integer bin) - Example:: + Example: + + .. testcode:: # Integer bin "a" == 200. expr = exp.Eq(exp.IntBin("a"), 200).compile() @@ -249,7 +263,9 @@ def __init__(self, bin: str): :return: (string bin) - Example:: + Example: + + .. testcode:: # String bin "a" == "xyz". expr = exp.Eq(exp.StrBin("a"), "xyz").compile() @@ -271,7 +287,9 @@ def __init__(self, bin: str): :return: (float bin) - Example:: + Example: + + .. testcode:: # Float bin "a" > 2.71. expr = exp.GT(exp.FloatBin("a"), 2.71).compile() @@ -293,7 +311,9 @@ def __init__(self, bin: str): :return: (blob bin) - Example:: + Example: + + .. testcode:: #. Blob bin "a" == bytearray([0x65, 0x65]) expr = exp.Eq(exp.BlobBin("a"), bytearray([0x65, 0x65])).compile() @@ -315,7 +335,9 @@ def __init__(self, bin: str): :return: (GeoJSON bin) - Example:: + Example: + + .. testcode:: #GeoJSON bin "a" contained by GeoJSON bin "b". expr = exp.CmpGeo(GeoBin("a"), exp.GeoBin("b")).compile() @@ -337,7 +359,9 @@ def __init__(self, bin: str): :return: (list bin) - Example:: + Example: + + .. testcode:: from aerospike_helpers.expressions import list as list_exprs @@ -367,7 +391,9 @@ def __init__(self, bin: str): :return: (map bin) - Example:: + Example: + + .. testcode:: from aerospike_helpers.expressions import map as map_exprs @@ -391,7 +417,9 @@ def __init__(self, bin: str): :return: (HyperLogLog bin) - Example:: + Example: + + .. testcode:: # Does HLL bin "a" have a hll_count > 1000000. from aerospike_helpers.expressions import hll @@ -413,7 +441,9 @@ def __init__(self, bin: str): :return: (boolean value): True if bin exists, False otherwise. - Example:: + Example: + + .. testcode:: #Bin "a" exists in record. expr = exp.BinExists("a").compile() @@ -435,7 +465,9 @@ def __init__(self, bin: str): :return: (integer value): returns the bin type. - Example:: + Example: + + .. testcode:: # bin "a" == type string. expr = exp.Eq(exp.BinType("a"), aerospike.AS_BYTES_STRING).compile() @@ -460,7 +492,9 @@ class SetName(_BaseExpr): def __init__(self): """:return: (string value): Name of the set this record belongs to. - Example:: + Example: + + .. testcode:: # Record set name == "myset". expr = exp.Eq(exp.SetName(), "myset").compile() @@ -486,7 +520,9 @@ class DeviceSize(_BaseExpr): def __init__(self): """:return: (integer value): Uncompressed storage size of the record. - Example:: + Example: + + .. testcode:: # Record device size >= 100 KB. expr = exp.GE(exp.DeviceSize(), 100 * 1024).compile() @@ -543,7 +579,9 @@ class LastUpdateTime(_BaseExpr): def __init__(self): """:return: (integer value): When the record was last updated. - Example:: + Example: + + .. testcode:: # Record last update time >= 2020-01-15. expr = exp.GE(exp.LastUpdateTime(), 1577836800).compile() @@ -562,7 +600,9 @@ class SinceUpdateTime(_BaseExpr): def __init__(self): """:return: (integer value): Number of milliseconds since last updated. - Example:: + Example: + + .. testcode:: # Record last updated more than 2 hours ago. expr = exp.GT(exp.SinceUpdateTime(), 2 * 60 * 60 * 1000).compile() @@ -581,7 +621,9 @@ class VoidTime(_BaseExpr): def __init__(self): """:return: (integer value): Expiration time in nanoseconds since 1970-01-01. - Example:: + Example: + + .. testcode:: # Record expires on 2021-01-01. expr = exp.And( @@ -603,7 +645,9 @@ def __init__(self): """:return: (integer value): Number of seconds till the record will expire, returns -1 if the record never expires. - Example:: + Example: + + .. testcode:: # Record expires in less than 1 hour. expr = exp.LT(exp.TTL(), 60 * 60).compile() @@ -623,7 +667,9 @@ class IsTombstone(_BaseExpr): def __init__(self): """:return: (boolean value): True if the record is a tombstone, false otherwise. - Example:: + Example: + + .. testcode:: # Detect deleted records that are in tombstone state. expr = exp.IsTombstone().compile() @@ -643,7 +689,9 @@ def __init__(self, mod: int): :return: (integer value): Value in range 0 and mod (exclusive). - Example:: + Example: + + .. testcode:: # Records that have digest(key) % 3 == 1. expr = exp.Eq(exp.DigestMod(3), 1).compile() @@ -668,7 +716,9 @@ def __init__(self, expr0: "TypeComparisonArg", expr1: "TypeComparisonArg"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Integer bin "a" == 11 expr = exp.Eq(exp.IntBin("a"), 11).compile() @@ -688,7 +738,9 @@ def __init__(self, expr0: "TypeComparisonArg", expr1: "TypeComparisonArg"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Integer bin "a" not == 13. expr = exp.NE(exp.IntBin("a"), 13).compile() @@ -708,7 +760,9 @@ def __init__(self, expr0: "TypeComparisonArg", expr1: "TypeComparisonArg"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Integer bin "a" > 8. expr = exp.GT(exp.IntBin("a"), 8).compile() @@ -728,7 +782,9 @@ def __init__(self, expr0: "TypeComparisonArg", expr1: "TypeComparisonArg"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Integer bin "a" >= 88. expr = exp.GE(exp.IntBin("a"), 88).compile() @@ -748,7 +804,9 @@ def __init__(self, expr0: "TypeComparisonArg", expr1: "TypeComparisonArg"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Integer bin "a" < 1000. expr = exp.LT(exp.IntBin("a"), 1000).compile() @@ -768,7 +826,9 @@ def __init__(self, expr0: "TypeComparisonArg", expr1: "TypeComparisonArg"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Integer bin "a" <= 1. expr = exp.LE(exp.IntBin("a"), 1).compile() @@ -789,7 +849,9 @@ def __init__(self, options: int, regex_str: str, cmp_str: Union[_BaseExpr, str]) :return: (boolean value) - Example:: + Example: + + .. testcode:: # Select string bin "a" that starts with "prefix" and ends with "suffix". # Ignore case and do not match newline. @@ -813,7 +875,9 @@ def __init__(self, expr0: "TypeGeo", expr1: "TypeGeo"): :return: (boolean value) - Example:: + Example: + + .. testcode:: # Geo bin "point" is within geo bin "region". expr = exp.CmpGeo(exp.GeoBin("point"), exp.GeoBin("region")).compile() @@ -837,7 +901,9 @@ def __init__(self, *exprs: _BaseExpr): :return: (boolean value) - Example:: + Example: + + .. testcode:: # not (a == 0 or a == 10) expr = exp.Not(exp.Or( @@ -858,7 +924,9 @@ def __init__(self, *exprs: _BaseExpr): :return: (boolean value) - Example:: + Example: + + .. testcode:: # (a > 5 || a == 0) && b < 3 expr = exp.And( @@ -881,7 +949,9 @@ def __init__(self, *exprs: _BaseExpr): :return: (boolean value) - Example:: + Example: + + .. testcode:: # (a == 0 || b == 0) expr = exp.Or( @@ -902,7 +972,9 @@ def __init__(self, *exprs: _BaseExpr): :return: (boolean value) - Example:: + Example: + + .. testcode:: # exclusive(a == 0, b == 0) expr = exp.Exclusive( @@ -942,7 +1014,9 @@ def __init__(self, *exprs: _BaseExpr): :return: (boolean value) - Example:: + Example: + + .. testcode:: from aerospike_helpers.expressions.arithmetic import Add, Sub, Mul @@ -1010,7 +1084,9 @@ def __init__(self, *exprs: _BaseExpr): :return: (result of scoped expression) - Example:: + Example: + + .. testcode:: # for int bin "a", 5 < a < 10 expr = exp.Let(exp.Def("x", exp.IntBin("a")), @@ -1036,7 +1112,9 @@ def __init__(self, var_name: str, expr: _BaseExpr): :return: (a variable name expression pair) - Example:: + Example: + + .. testcode:: # for int bin "a", 5 < a < 10 expr = exp.Let(exp.Def("x", exp.IntBin("a")), @@ -1062,7 +1140,9 @@ def __init__(self, var_name: str): :return: (value stored in variable) - Example:: + Example: + + .. testcode:: # for int bin "a", 5 < a < 10 expr = exp.Let(exp.Def("x", exp.IntBin("a")), diff --git a/aerospike_helpers/expressions/bitwise.py b/aerospike_helpers/expressions/bitwise.py index ea9f29450d..f2e641babe 100644 --- a/aerospike_helpers/expressions/bitwise.py +++ b/aerospike_helpers/expressions/bitwise.py @@ -19,7 +19,9 @@ :mod:`Bitwise Operations API ` for binary data. -Example:: +Example: + +.. testcode:: import aerospike_helpers.expressions as exp # Let blob bin "c" == bytearray([3] * 5). @@ -60,7 +62,9 @@ def __init__(self, policy: "TypePolicy", byte_size: int, flags: int, bin: "TypeB :return: Blob value expression of resized blob bin. - Example:: + Example: + + .. testcode:: # Blob bin "c" == bytearray([1] * 5). # Resize blob bin "c" from the front so that the returned value is bytearray([0] * 5 + [1] * 5). @@ -96,7 +100,9 @@ def __init__(self, policy: "TypePolicy", byte_offset: int, value: "TypeBitValue" :return: Resulting blob containing the inserted bytes. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1] * 5). # Insert 3 so that returned value is bytearray([1, 3, 1, 1, 1, 1]). @@ -130,7 +136,9 @@ def __init__(self, policy: "TypePolicy", byte_offset: int, byte_size: int, bin: :return: Resulting blob containing the remaining bytes. - Example:: + Example: + + .. testcode:: # b = bytearray([1, 2, 3, 4, 5]) expr = exp.BitRemove(None, 1, 1, exp.BlobBin("b")).compile() @@ -169,7 +177,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, value: :return: Resulting blob expression with the bits overwritten. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([0] * 5). # Set bit at offset 7 with size 1 bits to 1 to make the returned value bytearray([1, 0, 0, 0, 0]). @@ -205,7 +215,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, value: :return: Resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1] * 5). # bitwise Or `8` with the first byte of blob bin c so that the returned value is bytearray([9, 1, 1, 1, 1]). @@ -248,7 +260,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, value: :return: Resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1] * 5). # bitwise Xor `1` with the first byte of blob bin c so that the returned value is bytearray([0, 1, 1, 1, 1]) @@ -284,7 +298,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, value: :return: Resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1] * 5). # bitwise and `0` with the first byte of blob bin c so that the returned value is bytearray([0, 1, 1, 1, 1]) @@ -327,7 +343,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, bin: "T :return: Resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([255] * 5). # bitwise, not, all of "c" to get bytearray([254] * 5). @@ -362,7 +380,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, shift: :return: Resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1] * 5). # Bit left shift the first byte of bin "c" to get bytearray([8, 1, 1, 1, 1]). @@ -398,7 +418,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, shift: :return: Resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([8] * 5). # Bit left shift the first byte of bin "c" to get bytearray([4, 8, 8, 8, 8]). @@ -446,7 +468,9 @@ def __init__( :return: resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Assume we have a blob bin of five bytes: bytearray([1, 1, 1, 1, 1]) expr = exp.BitAdd(None, 8, 8, 1, aerospike.BIT_OVERFLOW_FAIL, exp.BlobBin("b")).compile() @@ -498,7 +522,9 @@ def __init__( :return: resulting blob with the bits operated on. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1] * 5). # Bit subtract the second byte of bin "c" to get bytearray([1, 0, 1, 1, 1]) @@ -539,7 +565,9 @@ def __init__(self, policy: "TypePolicy", bit_offset: int, bit_size: int, value: :return: Resulting blob expression with the bits overwritten. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([0] * 5). # Set bit at offset 7 with size 1 bytes to 1 to make the returned value bytearray([1, 0, 0, 0, 0]). @@ -578,7 +606,9 @@ def __init__(self, bit_offset: int, bit_size: int, bin: "TypeBinName"): :return: Blob, bit_size bits rounded up to the nearest byte size. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1, 2, 3, 4, 5). # Get 2 from bin "c". @@ -600,7 +630,9 @@ def __init__(self, bit_offset: int, bit_size: int, bin: "TypeBinName"): :return: Blob, bit_size bits rounded up to the nearest byte size. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([3] * 5). # Count set bits starting at 3rd byte in bin "c" to get count of 6. @@ -627,7 +659,9 @@ def __init__(self, bit_offset: int, bit_size: int, value: bool, bin: "TypeBinNam :return: Index of the left most bit starting from bit_offset set to value. Returns -1 if not found. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([3] * 5). # Scan the first byte of bin "c" for the first bit set to 1. (should get 6) @@ -650,7 +684,9 @@ def __init__(self, bit_offset: int, bit_size: int, value: bool, bin: "TypeBinNam :return: Index of the right most bit starting from bit_offset set to value. Returns -1 if not found. - Example:: + Example: + + .. testcode:: # b = bytearray([1, 0, 0, 0, 128]) expr = exp.BitRightScan(32, 8, True, exp.BlobBin("b")).compile() @@ -677,7 +713,9 @@ def __init__(self, bit_offset: int, bit_size: int, sign: bool, bin: "TypeBinName :return: Integer expression. - Example:: + Example: + + .. testcode:: # Let blob bin "c" == bytearray([1, 2, 3, 4, 5). # Get 2 as an integer from bin "c". diff --git a/aerospike_helpers/expressions/bitwise_operators.py b/aerospike_helpers/expressions/bitwise_operators.py index 73165defc1..aeca79b00f 100644 --- a/aerospike_helpers/expressions/bitwise_operators.py +++ b/aerospike_helpers/expressions/bitwise_operators.py @@ -48,7 +48,9 @@ def __init__(self, *exprs: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", a & 0xff == 0x11 expr = exp.Eq(exp.IntAnd(exp.IntBin("a"), 0xff), 0x11).compile() @@ -71,7 +73,9 @@ def __init__(self, *exprs: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", a | 0x10 not == 0 expr = exp.NE(exp.IntOr(IntBin("a"), 0x10), 0).compile() @@ -94,7 +98,9 @@ def __init__(self, *exprs: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", "b", a ^ b == 16 expr = exp.Eq(exp.IntXOr(exp.IntBin("a"), exp.IntBin("b")), 16).compile() @@ -116,7 +122,9 @@ def __init__(self, expr: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", ~ a == 7 expr = exp.Eq(exp.IntNot(exp.IntBin("a")), 7).compile() @@ -139,7 +147,9 @@ def __init__(self, value: "TypeInteger", shift: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", a << 8 > 0xff expr = exp.GT(exp.IntLeftShift(exp.IntBin("a"), 8), 0xff).compile() @@ -162,7 +172,9 @@ def __init__(self, value: "TypeInteger", shift: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", a >>> 8 > 0xff expr = exp.GT(exp.IntRightShift(exp.IntBin("a"), 8), 0xff).compile() @@ -185,7 +197,9 @@ def __init__(self, value: "TypeInteger", shift: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", a >> 8 > 0xff expr = exp.GT(exp.IntArithmeticRightShift(exp.IntBin("a"), 8), 0xff).compile() @@ -207,7 +221,9 @@ def __init__(self, value: "TypeInteger"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", count(a) == 4 expr = exp.Eq(exp.IntCount(exp.IntBin("a")), 4).compile() @@ -234,7 +250,9 @@ def __init__(self, value: "TypeInteger", search: "TypeBool"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", lscan(a, True) == 4 expr = exp.GT(exp.IntLeftScan(exp.IntBin("a"), True), 4).compile() @@ -262,7 +280,9 @@ def __init__(self, value: "TypeInteger", search: "TypeBool"): :return: (integer value) - Example:: + Example: + + .. testcode:: # for int bin "a", rscan(a, True) == 4 expr = exp.GT(exp.IntRightScan(exp.IntBin("a"), True), 4).compile() diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 3905e5f075..bcb364573d 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -61,7 +61,9 @@ def __init__( :return: Returns the resulting hll. - Example:: + Example: + + .. testcode:: # Create an HLL with 12 index bits and 24 min hash bits. expr = exp.HLLInit(None, 12, 24, exp.HLLBin("my_hll")) @@ -96,7 +98,9 @@ def __init__( :return: Returns the resulting hll bin after adding elements from list. - Example:: + Example: + + .. testcode:: # Let HLL bin "d" have the following elements, ['key1', 'key2', 'key3'], index_bits 8, mh_bits 8. # Add ['key4', 'key5', 'key6'] so that the returned value is ['key1', 'key2', 'key3', 'key4', 'key5', @@ -128,7 +132,9 @@ def __init__(self, bin: "TypeBinName"): :return: Integer bin, the estimated number of unique elements in an HLL. - Example:: + Example: + + .. testcode:: # Get count from HLL bin "d". expr = exp.HLLGetCount(exp.HLLBin("d")).compile() @@ -148,7 +154,9 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): :return: HLL bin representing the set union. - Example:: + Example: + + .. testcode:: # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing HLL objects retrieved from the aerospike database. @@ -173,7 +181,9 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): :return: Integer bin, estimated number of elements in the set union. - Example:: + Example: + + .. testcode:: # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing one HLL object with keys ['key%s' % str(i) for i in range(5000, 15000)]. @@ -198,7 +208,9 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): :return: Integer bin, estimated number of elements in the set intersection. - Example:: + Example: + + .. testcode:: # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing one HLL object with keys ['key%s' % str(i) for i in range(5000, 15000)]. @@ -223,7 +235,9 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): :return: Float bin, estimated similarity between 0.0 and 1.0. - Example:: + Example: + + .. testcode:: # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing one HLL object with keys ['key%s' % str(i) for i in range(5000, 15000)]. @@ -248,7 +262,9 @@ def __init__(self, bin: "TypeBinName"): :return: List bin, a list containing the index_bit_count and minhash_bit_count. - Example:: + Example: + + .. testcode:: # Get description of HLL bin "d". expr = exp.HLLDescribe(exp.HLLBin("d")).compile() @@ -274,7 +290,9 @@ def __init__( :return: 1 if bin may contain any key in list, 0 otherwise. - Example:: + Example: + + .. testcode:: # Check if HLL bin "d" may contain any of the keys in `list`. expr = exp.HLLMayContain(["key1", "key2", "key3"], exp.HLLBin("d")).compile() diff --git a/aerospike_helpers/expressions/list.py b/aerospike_helpers/expressions/list.py index 5efbb4df38..d3aaaecb15 100644 --- a/aerospike_helpers/expressions/list.py +++ b/aerospike_helpers/expressions/list.py @@ -59,7 +59,9 @@ def __init__(self, ctx: "TypeCTX", policy: "TypePolicy", value: "TypeValue", bin :return: List expression. - Example:: + Example: + + .. testcode:: # Check if length of list bin "a" is > 5 after appending 1 item. listAppendedBy3 = exp.ListAppend(None, None, 3, exp.ListBin("a")) @@ -97,7 +99,9 @@ def __init__(self, ctx: "TypeCTX", policy: "TypePolicy", value: "TypeValue", bin :return: List expression. - Example:: + Example: + + .. testcode:: # Check if length of list bin "a" is > 5 after appending multiple items. listAppendedByTwoItems = exp.ListAppendItems(None, None, [3, 2], exp.ListBin("a")) @@ -140,7 +144,9 @@ def __init__( :return: List expression. - Example:: + Example: + + .. testcode:: # Check if list bin "a" has length > 5 after insert. listInsertedBy3At0 = exp.ListInsert(None, None, 0, 3, exp.ListBin("a")) @@ -182,7 +188,9 @@ def __init__( :return: List expression. - Example:: + Example: + + .. testcode:: # Check if list bin "a" has length > 5 after inserting items. listInsertedByTwoItems = exp.ListInsertItems(None, None, 0, [4, 7], exp.ListBin("a")) @@ -224,7 +232,9 @@ def __init__( :return: List expression. - Example:: + Example: + + .. testcode:: # Check if incremented value in list bin "a" is the largest in the list. # Rank of -1 == largest element @@ -273,7 +283,9 @@ def __init__( :return: List expression. - Example:: + Example: + + .. testcode:: # Get smallest element in list bin "a" after setting index 1 to 10. listSetAtIndex1 = exp.ListSet(None, None, 1, 10, exp.ListBin("a")) @@ -311,7 +323,9 @@ def __init__(self, ctx: "TypeCTX", bin: "TypeBinName"): :return: List expression. - Example:: + Example: + + .. testcode:: # Clear list value of list nested in list bin "a" index 1. from aerospike_helpers import cdt_ctx @@ -341,7 +355,9 @@ def __init__(self, ctx: "TypeCTX", order: int, bin: "TypeBinName"): :return: list expression. - Example:: + Example: + + .. testcode:: # Get value of sorted list bin "a". expr = exp.ListSort(None, aerospike.LIST_SORT_DEFAULT, "a").compile() @@ -369,7 +385,9 @@ def __init__(self, ctx: "TypeCTX", value: "TypeValue", bin: "TypeBinName", inver :return: list expression. - Example:: + Example: + + .. testcode:: # See if list bin "a", with `3` removed, is equal to list bin "b". listRemoved3 = exp.ListRemoveByValue(None, 3, exp.ListBin("a")) @@ -400,7 +418,9 @@ def __init__(self, ctx: "TypeCTX", values: "TypeListValue", bin: "TypeBinName", :return: list expression. - Example:: + Example: + + .. testcode:: # Remove elements with values [1, 2, 3] from list bin "a". expr = exp.ListRemoveByValueList(None, [1, 2, 3], exp.ListBin("a")).compile() @@ -441,7 +461,9 @@ def __init__( :return: list expression. - Example:: + Example: + + .. testcode:: # Remove list of items with values >= 3 and < 7 from list bin "a". expr = exp.ListRemoveByValueRange(None, 3, 7, exp.ListBin("a")).compile() @@ -482,7 +504,9 @@ def __init__( :return: list expression. - Example:: + Example: + + .. testcode:: # Remove elements larger than 4 by relative rank in list bin "a". # Assume list in bin a is: [6, 12, 4, 21] @@ -527,7 +551,9 @@ def __init__( :return: list expression. - Example:: + Example: + + .. testcode:: # Remove 2 elements greater than 4 # Assume list in bin a is: [6, 12, 4, 21] @@ -558,7 +584,9 @@ def __init__(self, ctx: "TypeCTX", index: "TypeIndex", bin: "TypeBinName"): :return: list expression. - Example:: + Example: + + .. testcode:: # Get size of list bin "a" after index 3 has been removed. expr = exp.ListSize(None, exp.ListRemoveByIndex(None, 3, exp.ListBin("a"))).compile() @@ -586,7 +614,9 @@ def __init__(self, ctx: "TypeCTX", index: "TypeIndex", bin: "TypeBinName", inver :return: list expression. - Example:: + Example: + + .. testcode:: # Remove all elements starting from index 3 in list bin "a". expr = exp.ListRemoveByIndexRangeToEnd(None, 3, exp.ListBin("a")).compile() @@ -624,7 +654,9 @@ def __init__( :return: list expression. - Example:: + Example: + + .. testcode:: # Get size of list bin "a" after index 3, 4, and 5 have been removed. expr = exp.ListSize(None, exp.ListRemoveByIndexRange(None, 3, 3, exp.ListBin("a"))).compile() @@ -653,7 +685,9 @@ def __init__(self, ctx: "TypeCTX", rank: "TypeRank", bin: "TypeBinName"): :return: list expression. - Example:: + Example: + + .. testcode:: # Remove smallest value in list bin "a". expr = exp.ListRemoveByRank(None, 0, exp.ListBin("a")).compile() @@ -681,7 +715,9 @@ def __init__(self, ctx: "TypeCTX", rank: "TypeRank", bin: "TypeBinName", inverte :return: list expression. - Example:: + Example: + + .. testcode:: # Remove the 2 largest elements from List bin "a". # Assume list bin contains [6, 12, 4, 21] @@ -721,7 +757,9 @@ def __init__( :return: list expression. - Example:: + Example: + + .. testcode:: # Remove the 3 smallest items from list bin "a". expr = exp.ListRemoveByRankRange(None, 0, 3, exp.ListBin("a")).compile() @@ -754,7 +792,9 @@ def __init__(self, ctx: "TypeCTX", bin: "TypeBinName"): :return: Integer expression. - Example:: + Example: + + .. testcode:: #Take the size of list bin "a". expr = exp.ListSize(None, exp.ListBin("a")).compile() @@ -793,7 +833,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the index of the element with value, 3, in list bin "a". expr = exp.ListGetByValue(None, aerospike.LIST_RETURN_INDEX, 3, exp.ListBin("a")).compile() @@ -839,7 +881,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get rank of values between 3 (inclusive) and 7 (exclusive) in list bin "a". expr = exp.ListGetByValueRange(None, aerospike.LIST_RETURN_RANK, 3, 7, exp.ListBin("a")).compile() @@ -882,7 +926,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the indexes of the the elements in list bin "a" with values [3, 6, 12]. expr = exp.ListGetByValueList(None, aerospike.LIST_RETURN_INDEX, [3, 6, 12], exp.ListBin("a")).compile() @@ -923,7 +969,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # [6, 12, 4, 21] expr = exp.ListGetByValueRelRankRangeToEnd(None, aerospike.LIST_RETURN_VALUE, 3, 1, @@ -993,7 +1041,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # [6, 12, 4, 21] expr = exp.ListGetByValueRelRankRange(None, aerospike.LIST_RETURN_VALUE, 3, 1, 2, @@ -1039,7 +1089,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the value at index 0 in list bin "a". (assume this value is an integer) expr = exp.ListGetByIndex(None, aerospike.LIST_RETURN_VALUE, exp.ResultType.INTEGER, 0, @@ -1079,7 +1131,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get element 5 to end from list bin "a". expr = exp.ListGetByIndexRangeToEnd(None, aerospike.LIST_RETURN_VALUE, 5, exp.ListBin("a")).compile() @@ -1122,7 +1176,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get elements at indexes 3, 4, 5, 6 in list bin "a". expr = exp.ListGetByIndexRange(None, aerospike.LIST_RETURN_VALUE, 3, 4, exp.ListBin("a")).compile() @@ -1163,7 +1219,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the smallest element in list bin "a". expr = exp.ListGetByRank(None, aerospike.LIST_RETURN_VALUE, exp.ResultType.INTEGER, 0, @@ -1196,7 +1254,9 @@ def __init__(self, ctx: "TypeCTX", return_type: int, rank: "TypeRank", bin: "Typ :return: Expression. - Example:: + Example: + + .. testcode:: # Get the three largest elements in list bin "a". expr = exp.ListGetByRankRangeToEnd(None, aerospike.LIST_RETURN_VALUE, -3, exp.ListBin("a")).compile() @@ -1238,7 +1298,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the 3 smallest elements in list bin "a". expr = exp.ListGetByRankRange(None, aerospike.LIST_RETURN_VALUE, 0, 3, exp.ListBin("a")).compile() diff --git a/aerospike_helpers/expressions/map.py b/aerospike_helpers/expressions/map.py index ac6623705f..4509565903 100644 --- a/aerospike_helpers/expressions/map.py +++ b/aerospike_helpers/expressions/map.py @@ -62,7 +62,9 @@ def __init__(self, ctx: "TypeCTX", policy: "TypePolicy", key: "TypeKey", value: :return: Map expression. - Example:: + Example: + + .. testcode:: # Put {"key": 27} into map bin "b". expr = exp.MapPut(None, None, "key", 27, exp.MapBin("b")).compile() @@ -100,7 +102,9 @@ def __init__(self, ctx: "TypeCTX", policy: "TypePolicy", map: map, bin: "TypeBin :return: Map expression. - Example:: + Example: + + .. testcode:: # Put {27: 'key27', 28: 'key28'} into map bin "b". expr = exp.MapPutItems(None, None, {27: 'key27', 28: 'key28'}, exp.MapBin("b")).compile() @@ -140,7 +144,9 @@ def __init__(self, ctx: "TypeCTX", policy: "TypePolicy", key: "TypeKey", value: :return: Map expression. - Example:: + Example: + + .. testcode:: # Increment element at 'vageta' in map bin "b" by 9000. expr = exp.MapIncrement(None, None, 'vageta', 9000, exp.MapBin("b")).compile() @@ -176,7 +182,9 @@ def __init__(self, ctx: "TypeCTX", bin: "TypeBinName"): :return: Map expression. - Example:: + Example: + + .. testcode:: # Clear map bin "b". expr = exp.MapClear(None, exp.MapBin("b")).compile() @@ -203,7 +211,9 @@ def __init__(self, ctx: "TypeCTX", key: "TypeKey", bin: "TypeBinName"): :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove element at key 1 in map bin "b". expr = exp.MapRemoveByKey(None, 1, exp.MapBin("b")).compile() @@ -234,7 +244,9 @@ def __init__(self, ctx: "TypeCTX", keys: List[TypeKey], bin: "TypeBinName", inve :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove elements at keys [1, 2] in map bin "b". expr = exp.MapRemoveByKeyList(None, [1, 2], exp.MapBin("b")).compile() @@ -276,7 +288,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove elements at keys between 1 and 10 in map bin "b". expr = exp.MapRemoveByKeyRange(None, 1, 10, exp.MapBin("b")).compile() @@ -312,7 +326,9 @@ def __init__(self, ctx: "TypeCTX", key: "TypeKey", index: "TypeIndex", bin: "Typ :return: Map expression. - Example:: + Example: + + .. testcode:: # {"key1": 1, "key2": 2, "key3": 3, "key4": 10} expr = exp.MapRemoveByKeyRelIndexRangeToEnd(None, "key2", 1, exp.MapBin("b")).compile() @@ -355,7 +371,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove the next two items after key1 # {"key1": 1, "key2": 2, "key3": 3, "key4": 10} @@ -390,7 +408,9 @@ def __init__(self, ctx: "TypeCTX", value: "TypeValue", bin: "TypeBinName", inver :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove {"key1": 1} from map bin "b". expr = exp.MapRemoveByValue(None, 1, exp.MapBin("b")).compile() @@ -421,7 +441,9 @@ def __init__(self, ctx: "TypeCTX", values: "TypeListValue", bin: "TypeBinName", :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove elements with values 1, 2, 3 from map bin "b". expr = exp.MapRemoveByValueList(None, [1, 2, 3], exp.MapBin("b")).compile() @@ -463,7 +485,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove list of items with values >= 3 and < 7 from map bin "b". expr = exp.MapRemoveByValueRange(None, 3, 7, exp.MapBin("b")).compile() @@ -502,7 +526,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove all elements with values larger than 3 from map bin "b". expr = exp.MapRemoveByValueRelRankRangeToEnd(None, 3, 1, exp.MapBin("b")).compile() @@ -546,7 +572,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove the key with a value just lower than 17 expr = exp.MapRemoveByValueRelRankRange(None, 17, -1, 1, exp.MapBin("b")).compile() @@ -578,7 +606,9 @@ def __init__(self, ctx: "TypeCTX", index: "TypeIndex", bin: "TypeBinName"): :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove element with smallest key from map bin "b". expr = exp.MapRemoveByIndex(None, 0, exp.MapBin("b")).compile() @@ -609,7 +639,9 @@ def __init__(self, ctx: "TypeCTX", index: "TypeIndex", bin: "TypeBinName", inver :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove all elements starting from index 3 in map bin "b". expr = exp.MapRemoveByIndexRangeToEnd(None, 3, exp.MapBin("b")).compile() @@ -648,7 +680,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Get size of map bin "b" after index 3, 4, and 5 have been removed. expr = exp.MapSize(None, exp.MapRemoveByIndexRange(None, 3, 3, exp.MapBin("b"))).compile() @@ -679,7 +713,9 @@ def __init__(self, ctx: "TypeCTX", rank: "TypeRank", bin: "TypeBinName"): :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove key with smallest value in map bin "b". expr = exp.MapRemoveByRank(None, 0, exp.MapBin("b")).compile() @@ -710,7 +746,9 @@ def __init__(self, ctx: "TypeCTX", rank: "TypeRank", bin: "TypeBinName", inverte :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove keys with 2 largest values from map bin "b". expr = exp.MapRemoveByRankRangeToEnd(None, -2, exp.MapBin("b")).compile() @@ -749,7 +787,9 @@ def __init__( :return: Map expression. - Example:: + Example: + + .. testcode:: # Remove 3 keys with the smallest values from map bin "b". expr = exp.MapRemoveByRankRange(None, 0, 3, exp.MapBin("b")).compile() @@ -784,7 +824,9 @@ def __init__(self, ctx: "TypeCTX", bin: "TypeBinName"): :return: Integer expression. - Example:: + Example: + + .. testcode:: #Take the size of map bin "b". expr = exp.MapSize(None, exp.MapBin("b")).compile() @@ -816,7 +858,9 @@ def __init__(self, ctx: "TypeCTX", return_type: int, value_type: int, key: "Type :return: Expression. - Example:: + Example: + + .. testcode:: # Get the value at key "key0" in map bin "b". (assume the value at key0 is an integer) expr = exp.MapGetByKey(None, aerospike.MAP_RETURN_VALUE, exp.ResultType.INTEGER, "key0", @@ -863,7 +907,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get elements at keys "key3", "key4", "key5", "key6" in map bin "b". expr = exp.MapGetByKeyRange(None, aerospike.MAP_RETURN_VALUE, "key3", "key7", exp.MapBin("b")).compile() @@ -908,7 +954,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get elements at keys "key3", "key4", "key5" in map bin "b". expr = exp.MapGetByKeyList(None, aerospike.MAP_RETURN_VALUE, ["key3", "key4", "key5"], @@ -955,7 +1003,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get elements with keys larger than "key2" from map bin "b". expr = exp.MapGetByKeyRelIndexRangeToEnd(None, aerospike.MAP_RETURN_VALUE, "key2", 1, @@ -1005,7 +1055,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: expr = exp.MapGetByKeyRelIndexRange(None, aerospike.MAP_RETURN_VALUE, "key2", 0, 2, exp.MapBin("b")).compile() @@ -1056,7 +1108,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the rank of the element with value, 3, in map bin "b". expr = exp.MapGetByValue(None, aerospike.MAP_RETURN_RANK, 3, exp.MapBin("b")).compile() @@ -1101,7 +1155,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get elements with values between 3 and 7 from map bin "b". expr = exp.MapGetByValueRange(None, aerospike.MAP_RETURN_VALUE, 3, 7, exp.MapBin("b")).compile() @@ -1145,7 +1201,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the indexes of the the elements in map bin "b" with values [3, 6, 12]. expr = exp.MapGetByValueList(None, aerospike.MAP_RETURN_INDEX, [3, 6, 12], exp.MapBin("b")).compile() @@ -1188,7 +1246,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the values of all elements in map bin "b" larger than 3. expr = exp.MapGetByValueRelRankRangeToEnd(None, aerospike.MAP_RETURN_VALUE, 3, 1, exp.MapBin("b")).compile() @@ -1233,7 +1293,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # {"key1": 1, "key2": 2, "key3": 3, "key4": 10} # Get next two largest values greater than a value of 1 @@ -1276,7 +1338,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the value at index 0 in map bin "b". (assume this value is an integer) expr = exp.MapGetByIndex(None, aerospike.MAP_RETURN_VALUE, @@ -1316,7 +1380,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get element at index 5 to end from map bin "b". expr = exp.MapGetByIndexRangeToEnd(None, aerospike.MAP_RETURN_VALUE, 5, exp.MapBin("b")).compile() @@ -1359,7 +1425,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get elements at indexes 3, 4, 5, 6 in map bin "b". expr = exp.MapGetByIndexRange(None, aerospike.MAP_RETURN_VALUE, 3, 4, exp.MapBin("b")).compile() @@ -1400,7 +1468,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the smallest element in map bin "b". expr = exp.MapGetByRank(None, aerospike.MAP_RETURN_VALUE, exp.ResultType.INTEGER, 0, @@ -1433,7 +1503,9 @@ def __init__(self, ctx: "TypeCTX", return_type: int, rank: "TypeRank", bin: "Typ :return: Expression. - Example:: + Example: + + .. testcode:: # Get the three largest elements in map bin "b". expr = exp.MapGetByRankRangeToEnd(None, aerospike.MAP_RETURN_VALUE, -3, MapBin("b")).compile() @@ -1476,7 +1548,9 @@ def __init__( :return: Expression. - Example:: + Example: + + .. testcode:: # Get the 3 smallest elements in map bin "b". expr = exp.MapGetByRankRange(None, aerospike.MAP_RETURN_VALUE, 0, 3, exp.MapBin("b")).compile() diff --git a/aerospike_helpers/metrics/__init__.py b/aerospike_helpers/metrics/__init__.py index 2f5ceb5c87..a64a624ccb 100644 --- a/aerospike_helpers/metrics/__init__.py +++ b/aerospike_helpers/metrics/__init__.py @@ -217,7 +217,9 @@ class MetricsPolicy: The bucket units are in milliseconds. The first 2 buckets are "<=1ms" and ">1ms". labels (dict[str, str]): List of name/value labels that is applied when exporting metrics. - Example:: + Example: + + .. testcode:: # latencyColumns=7 latencyShift=1 # <=1ms >1ms >2ms >4ms >8ms >16ms >32ms diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index f0677bbe28..67b48c1b9e 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -30,7 +30,9 @@ * -1: rightmost bit in the map * -4: 3 bits from rightmost -Example:: +Example: + +.. testcode:: import aerospike from aerospike_helpers.operations import bitwise_operations @@ -77,7 +79,9 @@ client.remove(key) client.close() -Example:: +Example: + +.. testcode:: import aerospike from aerospike import exception as e diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index da5ec131ab..ee77276afe 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -47,7 +47,9 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r Returns: A dictionary to be passed to operate or operate_ordered. - Example:: + Example: + + .. testcode:: # Read the value of int bin "balance". # Let 'client' be a connected aerospike client. @@ -89,7 +91,9 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ Returns: A dictionary to be passed to operate or operate_ordered. - Example:: + Example: + + .. testcode:: # Write the value of int bin "balance" + 50 back to "balance". # Let 'client' be a connected aerospike client. diff --git a/aerospike_helpers/operations/hll_operations.py b/aerospike_helpers/operations/hll_operations.py index 7e91fbcc8b..73ea55de5f 100644 --- a/aerospike_helpers/operations/hll_operations.py +++ b/aerospike_helpers/operations/hll_operations.py @@ -29,7 +29,9 @@ .. seealso:: `HyperLogLog (Data Type) more info. \ `_. -Example:: +Example: + +.. testcode:: import aerospike from aerospike_helpers.operations import hll_operations as hll_ops From 66dcce0c866dddd5844c2677a411f8c7db9cc460 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:36:38 -0700 Subject: [PATCH 039/127] Revert code examples back to code-output-style blocks since sphinx doctest plugin supports that style --- doc/aerospike.rst | 160 +++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 72 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 9d546d9e23..986914af41 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -45,17 +45,17 @@ Client Simple example: -.. TODO - Multiline comments maybe should be added to API descriptions - - >>> import aerospike - >>> # Configure the client to first connect to a cluster node at 127.0.0.1 - >>> # The client will learn about the other nodes in the cluster from the seed node. - >>> # Also sets a top level policy for read commands - >>> config = { - ... 'hosts': [ ('127.0.0.1', 3000) ], - ... 'policies': {'read': {'total_timeout': 1000}} - ... } - >>> client = aerospike.client(config) + .. testcode:: + + import aerospike + # Configure the client to first connect to a cluster node at 127.0.0.1 + # The client will learn about the other nodes in the cluster from the seed node. + # Also sets a top level policy for read commands + config = { + 'hosts': [ ('127.0.0.1', 3000) ], + 'policies': {'read': {'total_timeout': 1000}} + } + client = aerospike.client(config) Connecting using TLS example: @@ -100,12 +100,14 @@ Geospatial :param dict geo_data: a :class:`dict` representing the geospatial data. :return: an instance of the :py:class:`aerospike.GeoJSON` class. - >>> import aerospike + .. testcode:: + + import aerospike - >>> # Create GeoJSON point using WGS84 coordinates. - >>> latitude = 45.920278 - >>> longitude = 63.342222 - >>> loc = aerospike.geodata({'type': 'Point', 'coordinates': [longitude, latitude]}) + # Create GeoJSON point using WGS84 coordinates. + latitude = 45.920278 + longitude = 63.342222 + loc = aerospike.geodata({'type': 'Point', 'coordinates': [longitude, latitude]}) .. versionadded:: 1.0.54 @@ -117,10 +119,12 @@ Geospatial :param dict geojson_str: a :class:`str` of raw GeoJSON. :return: an instance of the :py:class:`aerospike.GeoJSON` class. - >>> import aerospike + .. testcode:: - >>> # Create GeoJSON point using WGS84 coordinates. - >>> loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') + import aerospike + + # Create GeoJSON point using WGS84 coordinates. + loc = aerospike.geojson('{"type": "Point", "coordinates": [-80.604333, 28.608389]}') .. versionadded:: 1.0.54 @@ -144,17 +148,19 @@ Types :return: a type representing a wildcard value. - >>> import aerospike - >>> from aerospike_helpers.operations import list_operations as list_ops + .. testcode:: - >>> client = aerospike.client({'hosts': [('localhost', 3000)]}) - >>> key = 'test', 'demo', 1 + import aerospike + from aerospike_helpers.operations import list_operations as list_ops - >>> # get all values of the form [1, ...] from a list of lists. - >>> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - >>> # [1, 2, 3] and [1, 'a'] - >>> operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] - >>> _, _, bins = client.operate(key, operations) + client = aerospike.client({'hosts': [('localhost', 3000)]}) + key = 'test', 'demo', 1 + + # get all values of the form [1, ] from a list of lists. + # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match + # [1, 2, 3] and [1, 'a'] + operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] + _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -166,17 +172,19 @@ Types :return: a type representing an infinite value. - >>> import aerospike - >>> from aerospike_helpers.operations import list_operations as list_ops + .. testcode:: - >>> client = aerospike.client({'hosts': [('localhost', 3000)]}) - >>> key = 'test', 'demo', 1 + import aerospike + from aerospike_helpers.operations import list_operations as list_ops - >>> # get all values of the form [1, ...] from a list of lists. - >>> # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - >>> # [1, 2, 3] and [1, 'a'] - >>> operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] - >>> _, _, bins = client.operate(key, operations) + client = aerospike.client({'hosts': [('localhost', 3000)]}) + key = 'test', 'demo', 1 + + # get all values of the form [1, ] from a list of lists. + # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match + # [1, 2, 3] and [1, 'a'] + operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] + _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -202,10 +210,12 @@ Serialization .. seealso:: To use this function with :meth:`Client.put`, \ the argument to the serializer parameter should be :const:`aerospike.SERIALIZER_USER`. - >>> def my_serializer(val): - ... return json.dumps(val) + .. testcode:: + + def my_serializer(val): + return json.dumps(val) - >>> aerospike.set_serializer(my_serializer) + aerospike.set_serializer(my_serializer) 0 .. versionadded:: 1.0.39 @@ -323,11 +333,13 @@ Other :return: a RIPEMD-160 digest of the input tuple. :rtype: :class:`bytearray` - >>> import aerospike - >>> import pprint + .. testcode:: - >>> digest = aerospike.calc_digest("test", "demo", 1) - >>> pprint.pprint(digest) + import aerospike + import pprint + + digest = aerospike.calc_digest("test", "demo", 1) + pprint.pprint(digest) bytearray(b'\xb7\xf4\xb88\x89\xe2\xdag\xdeh>\x1d\xf6\x91\x9a\x1e\xac\xc4F\xc8') .. _client_config: @@ -360,39 +372,43 @@ Only the `hosts` key is required; the rest of the keys are optional. Invalid client config example: - >>> import aerospike - - >>> config = { - ... "validate_keys": True, - ... "hosts": [ - ... ("127.0.0.1", 3000) - ... ], - ... # The correct key is "user", but "username" may be used by accident - ... "username": "user", - ... "password": "password" - ... } - >>> client = aerospike.client(config) + .. testcode:: + + import aerospike + + config = { + "validate_keys": True, + "hosts": [ + ("127.0.0.1", 3000) + ], + # The correct key is "user", but "username" may be used by accident + "username": "user", + "password": "password" + } + client = aerospike.client(config) Traceback (most recent call last): aerospike.exception.ParamError: "username" is an invalid client config dictionary key Invalid policy example: - >>> import aerospike - - >>> config = { - ... "validate_keys": True, - ... "hosts": [ - ... ("127.0.0.1", 3000) - ... ], - ... } - >>> client = aerospike.client(config) - - >>> key = ("test", "demo", 1) - >>> # "key_policy" is used instead of the correct key named "key" - >>> policy = { - ... "key_policy": aerospike.POLICY_KEY_SEND - ... } - >>> client.get(key, policy=policy) + .. testcode:: + + import aerospike + + config = { + "validate_keys": True, + "hosts": [ + ("127.0.0.1", 3000) + ], + } + client = aerospike.client(config) + + key = ("test", "demo", 1) + # "key_policy" is used instead of the correct key named "key" + policy = { + "key_policy": aerospike.POLICY_KEY_SEND + } + client.get(key, policy=policy) Traceback (most recent call last): aerospike.exception.ParamError: "key_policy" is an invalid policy dictionary key From 0620de53e7f4f8fb1b0edc7ba4d788f4f902f2e3 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:38:21 -0700 Subject: [PATCH 040/127] Rm unused script now that we use sphinx doctest --- doc/check-code-examples-in-docs.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 doc/check-code-examples-in-docs.py diff --git a/doc/check-code-examples-in-docs.py b/doc/check-code-examples-in-docs.py deleted file mode 100644 index 328427bf9b..0000000000 --- a/doc/check-code-examples-in-docs.py +++ /dev/null @@ -1,20 +0,0 @@ -import doctest -import unittest -import runpy - - -def custom_setup(test): - # This code runs before every doctest in the suite - # test.globs['shared_data'] = [1, 2, 3] - # TODO: should use file location and not cwd - runpy.run_path('./examples/boilerplate.py') - -def load_tests(loader, tests, ignore): - # Add setup and teardown logic here - # TODO: should use file location and not cwd - tests.addTests(doctest.DocFileSuite(["./aerospike.rst"])) - tests.addTests(doctest.DocFileSuite(["./client.rst"], setUp=custom_setup)) - return tests - -if __name__ == "__main__": - unittest.main() From ad7b685f3449ceda56841dc9060e83579c959c6c Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:20:27 -0700 Subject: [PATCH 041/127] Fix code example outputs causing doc build to fail --- doc/aerospike.rst | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 986914af41..ec0279228b 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -216,7 +216,6 @@ Serialization return json.dumps(val) aerospike.set_serializer(my_serializer) - 0 .. versionadded:: 1.0.39 @@ -340,7 +339,10 @@ Other digest = aerospike.calc_digest("test", "demo", 1) pprint.pprint(digest) - bytearray(b'\xb7\xf4\xb88\x89\xe2\xdag\xdeh>\x1d\xf6\x91\x9a\x1e\xac\xc4F\xc8') + + .. testoutput:: + + bytearray(b'\xb7\xf4\xb88\x89\xe2\xdag\xdeh>\x1d\xf6\x91\x9a\x1e\xac\xc4F\xc8') .. _client_config: @@ -386,8 +388,11 @@ Only the `hosts` key is required; the rest of the keys are optional. "password": "password" } client = aerospike.client(config) - Traceback (most recent call last): - aerospike.exception.ParamError: "username" is an invalid client config dictionary key + + .. testoutput:: + + Traceback (most recent call last): + aerospike.exception.ParamError: "username" is an invalid client config dictionary key Invalid policy example: @@ -409,8 +414,11 @@ Only the `hosts` key is required; the rest of the keys are optional. "key_policy": aerospike.POLICY_KEY_SEND } client.get(key, policy=policy) - Traceback (most recent call last): - aerospike.exception.ParamError: "key_policy" is an invalid policy dictionary key + + .. testoutput:: + + Traceback (most recent call last): + aerospike.exception.ParamError: "key_policy" is an invalid policy dictionary key * **hosts** (:class:`list`) A list of tuples identifying a node (or multiple nodes) in the cluster. From 6e839832248d9dd5236a8b150a1f04bab5365fee Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:26:18 -0700 Subject: [PATCH 042/127] Add testsetup block to fix many errors in aerospike_helpers.expressions docs --- doc/aerospike_helpers.expressions.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/aerospike_helpers.expressions.rst b/doc/aerospike_helpers.expressions.rst index 53e3fcfbf3..b32f11690f 100644 --- a/doc/aerospike_helpers.expressions.rst +++ b/doc/aerospike_helpers.expressions.rst @@ -157,7 +157,9 @@ The following documentation uses type aliases that map to standard Python types. .. note:: Requires server version >= 5.2.0 -Assume all in-line examples run this code beforehand:: +Assume all in-line examples run this code beforehand: + +.. testsetup:: import aerospike import aerospike_helpers.expressions as exp From 72615a1794185e72775f8c981686f49f864cfaee Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:34:18 -0700 Subject: [PATCH 043/127] Fix a few bad/inconsistent code examples --- aerospike_helpers/batch/records.py | 6 +++--- aerospike_helpers/expressions/base.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aerospike_helpers/batch/records.py b/aerospike_helpers/batch/records.py index 92dff87be8..87a583f632 100644 --- a/aerospike_helpers/batch/records.py +++ b/aerospike_helpers/batch/records.py @@ -87,7 +87,7 @@ def __init__( # Create a batch Write to increment bin "a" by 10 and read the result from the record. import aerospike - import aerospike_helpers.operations as op + from aerospike_helpers.operations import operations as op from aerospike_helpers.batch.records import Write bin_name = "a" @@ -148,7 +148,7 @@ def __init__( # Create a batch Read to read bin "a" from the record. import aerospike - import aerospike_helpers.operations as op + from aerospike_helpers.operations import operations as op from aerospike_helpers.batch.records import Read bin_name = "a" @@ -259,7 +259,7 @@ def __init__(self, key: tuple, policy: "TypeBatchPolicyRemove" = None) -> None: user_key = 1 key = (namespace, set, user_key) - br = Remove(key, ops) + br = Remove(key) """ super().__init__(key) self._type = _Types.REMOVE diff --git a/aerospike_helpers/expressions/base.py b/aerospike_helpers/expressions/base.py index 84f5efa17b..3527976451 100644 --- a/aerospike_helpers/expressions/base.py +++ b/aerospike_helpers/expressions/base.py @@ -340,7 +340,7 @@ def __init__(self, bin: str): .. testcode:: #GeoJSON bin "a" contained by GeoJSON bin "b". - expr = exp.CmpGeo(GeoBin("a"), exp.GeoBin("b")).compile() + expr = exp.CmpGeo(exp.GeoBin("a"), exp.GeoBin("b")).compile() """ self._fixed = {_Keys.BIN_KEY: bin} From f966c32a4dd4871bfd9b15b46c8e7cfb4787e5b0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:41:01 -0700 Subject: [PATCH 044/127] Fix more bad code examples.. --- aerospike_helpers/expressions/bitwise.py | 2 +- aerospike_helpers/expressions/list.py | 2 +- aerospike_helpers/expressions/map.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aerospike_helpers/expressions/bitwise.py b/aerospike_helpers/expressions/bitwise.py index f2e641babe..9e23103f2d 100644 --- a/aerospike_helpers/expressions/bitwise.py +++ b/aerospike_helpers/expressions/bitwise.py @@ -528,7 +528,7 @@ def __init__( # Let blob bin "c" == bytearray([1] * 5). # Bit subtract the second byte of bin "c" to get bytearray([1, 0, 1, 1, 1]) - expr = exp.BitSubtract(None, 8, 8, 1, aerospike.BIT_OVERFLOW_FAIL).compile() + expr = exp.BitSubtract(None, 8, 8, 1, aerospike.BIT_OVERFLOW_FAIL, "c").compile() """ self._children = ( bit_offset, diff --git a/aerospike_helpers/expressions/list.py b/aerospike_helpers/expressions/list.py index d3aaaecb15..4566716ec8 100644 --- a/aerospike_helpers/expressions/list.py +++ b/aerospike_helpers/expressions/list.py @@ -238,7 +238,7 @@ def __init__( # Check if incremented value in list bin "a" is the largest in the list. # Rank of -1 == largest element - largestListValue = exp.ListGetByRank(None, aerospike.LIST_RETURN_VALUE, exp.ResultType.INTEGER, -1) + largestListValue = exp.ListGetByRank(None, aerospike.LIST_RETURN_VALUE, exp.ResultType.INTEGER, -1, "a") listIncrementedAtIndex1 = exp.ListIncrement(None, None, 1, 5, exp.ListBin("a")) listItemAtIndex1 = exp.ListGetByIndex(None, aerospike.LIST_RETURN_VALUE, exp.ResultType.INTEGER, 1, listIncrementedAtIndex1) diff --git a/aerospike_helpers/expressions/map.py b/aerospike_helpers/expressions/map.py index 4509565903..cd9c08b1ec 100644 --- a/aerospike_helpers/expressions/map.py +++ b/aerospike_helpers/expressions/map.py @@ -1508,7 +1508,7 @@ def __init__(self, ctx: "TypeCTX", return_type: int, rank: "TypeRank", bin: "Typ .. testcode:: # Get the three largest elements in map bin "b". - expr = exp.MapGetByRankRangeToEnd(None, aerospike.MAP_RETURN_VALUE, -3, MapBin("b")).compile() + expr = exp.MapGetByRankRangeToEnd(None, aerospike.MAP_RETURN_VALUE, -3, exp.MapBin("b")).compile() """ self._children = (rank, bin if isinstance(bin, _BaseExpr) else MapBin(bin)) self._fixed = {_Keys.RETURN_TYPE_KEY: return_type} From 16d0bbbf9a1e88fff17e8c45babd9e672692ee5b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:06:42 -0700 Subject: [PATCH 045/127] Improve naming for one code example. Fix another example --- aerospike_helpers/expressions/bitwise_operators.py | 2 +- aerospike_helpers/operations/expression_operations.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aerospike_helpers/expressions/bitwise_operators.py b/aerospike_helpers/expressions/bitwise_operators.py index aeca79b00f..65089ab4ff 100644 --- a/aerospike_helpers/expressions/bitwise_operators.py +++ b/aerospike_helpers/expressions/bitwise_operators.py @@ -78,7 +78,7 @@ def __init__(self, *exprs: "TypeInteger"): .. testcode:: # for int bin "a", a | 0x10 not == 0 - expr = exp.NE(exp.IntOr(IntBin("a"), 0x10), 0).compile() + expr = exp.NE(exp.IntOr(exp.IntBin("a"), 0x10), 0).compile() """ self._children = exprs + (_GenericExpr(_ExprOp._AS_EXP_CODE_END_OF_VA_ARGS, 0, {}),) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index ee77276afe..0573006bf7 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -99,12 +99,12 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ # Let 'client' be a connected aerospike client. # Let int bin 'balance' == 50. - from aerospike_helpers.operations import expression_operations as expressions + from aerospike_helpers.operations import expression_operations as expr_ops from aerospike_helpers.expressions import * expr = Add(IntBin("balance"), 50).compile() ops = [ - expressions.expression_write("balance", expr) + expr_ops.expression_write("balance", expr) ] client.operate(self.key, ops) _, _, res = client.get(self.key) From ef4ef1fa9195a0f9376cc7a3367ec49c9130b8cc Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:12:21 -0700 Subject: [PATCH 046/127] Add missing import --- aerospike_helpers/batch/records.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aerospike_helpers/batch/records.py b/aerospike_helpers/batch/records.py index 87a583f632..c4bf02810b 100644 --- a/aerospike_helpers/batch/records.py +++ b/aerospike_helpers/batch/records.py @@ -204,6 +204,7 @@ def __init__( # Assume that "test_func" takes a bin name string as an argument. # Assume the appropriate UDF module has already been registered. import aerospike_helpers.operations as op + from aerospike_helpers.batch.records import Apply module = "my_lua" From 817450d1502b597f0f246225c9c66e40c1cdf606 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:33:30 -0700 Subject: [PATCH 047/127] Add debug print to see why operation code is not an integer --- doc/aerospike.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index ec0279228b..2185118c22 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -160,6 +160,8 @@ Types # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match # [1, 2, 3] and [1, 'a'] operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] + import sys + print(operations, file=sys.stderr) _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 From d260ec0a9b7f2ee74fa864938757f58e6697abaf Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:38:39 -0700 Subject: [PATCH 048/127] Debug further --- doc/aerospike.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 2185118c22..a1b0574e01 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -162,6 +162,8 @@ Types operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] import sys print(operations, file=sys.stderr) + print(aerospike.OP_LIST_GET_BY_VALUE, file=sys.stderr) + print(type(aerospike.OP_LIST_GET_BY_VALUE), file=sys.stderr) _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 From cf83d664e9147ceca14791a4e1d30d906710b777 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 19:00:46 -0700 Subject: [PATCH 049/127] Add debug prints in glue code to see what type the client sees. TODO - prone to memory leaks --- src/main/client/operate.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/client/operate.c b/src/main/client/operate.c index b743252b83..cce9f31ae6 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -1480,8 +1480,28 @@ static as_status get_operation(as_error *err, PyObject *op_dict, "Operation must contain an \"op\" entry"); } if (!PyLong_Check(py_operation)) { + PyObject *py_op_type = PyObject_Type(py_operation); + if (!py_op_type) { + return as_error_update(err, AEROSPIKE_ERR_PARAM, + "Operation must be an integer, but got an " + "indeterminate type instead"); + } + PyObject *py_op_type_name = PyType_GetName(py_op_type); + if (!py_op_type_name) { + return as_error_update(err, AEROSPIKE_ERR_PARAM, + "Operation must be an integer, but got an " + "indeterminate type instead"); + } + const char *op_type_name = PyUnicode_AsUTF8(py_op_type_name); + if (!op_type_name) { + return as_error_update(err, AEROSPIKE_ERR_PARAM, + "Operation must be an integer, but got an " + "indeterminate type instead"); + } return as_error_update(err, AEROSPIKE_ERR_PARAM, - "Operation must be an integer"); + "Operation must be an integer, but got a value " + "with type %s instead", + op_type_name); } *operation_ptr = PyLong_AsLong(py_operation); From a222bd88205b72e9a1915472613453e34fb4360d Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 19:03:03 -0700 Subject: [PATCH 050/127] fix compiler error --- src/main/client/operate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/operate.c b/src/main/client/operate.c index cce9f31ae6..80035936e6 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -1486,7 +1486,7 @@ static as_status get_operation(as_error *err, PyObject *op_dict, "Operation must be an integer, but got an " "indeterminate type instead"); } - PyObject *py_op_type_name = PyType_GetName(py_op_type); + PyObject *py_op_type_name = PyType_GetName((PyTypeObject *)py_op_type); if (!py_op_type_name) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Operation must be an integer, but got an " From 65eb2d51a830c9ccb3df5b88c7034afca37fa794 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 19:10:01 -0700 Subject: [PATCH 051/127] Make compatible with Python 3.10. PyType_GetName isn't available in the *compat.h header file --- src/main/client/operate.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/client/operate.c b/src/main/client/operate.c index 80035936e6..68c92dccf2 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -1486,13 +1486,7 @@ static as_status get_operation(as_error *err, PyObject *op_dict, "Operation must be an integer, but got an " "indeterminate type instead"); } - PyObject *py_op_type_name = PyType_GetName((PyTypeObject *)py_op_type); - if (!py_op_type_name) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "Operation must be an integer, but got an " - "indeterminate type instead"); - } - const char *op_type_name = PyUnicode_AsUTF8(py_op_type_name); + const char *op_type_name = ((PyTypeObject *)py_op_type)->tp_name; if (!op_type_name) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Operation must be an integer, but got an " From 94f1a166b2abbe910f0aa6243e2e9c56ec6910e7 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Tue, 28 Apr 2026 19:15:21 -0700 Subject: [PATCH 052/127] Use better method --- src/main/client/operate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/client/operate.c b/src/main/client/operate.c index 68c92dccf2..c83daa99dd 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -1480,13 +1480,13 @@ static as_status get_operation(as_error *err, PyObject *op_dict, "Operation must contain an \"op\" entry"); } if (!PyLong_Check(py_operation)) { - PyObject *py_op_type = PyObject_Type(py_operation); + PyTypeObject *py_op_type = Py_TYPE(py_operation); if (!py_op_type) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Operation must be an integer, but got an " "indeterminate type instead"); } - const char *op_type_name = ((PyTypeObject *)py_op_type)->tp_name; + const char *op_type_name = py_op_type->tp_name; if (!op_type_name) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Operation must be an integer, but got an " From e184e7062af816eaaabfe67744d60a875fc9e7d9 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:46:19 -0700 Subject: [PATCH 053/127] Printed out the type of the module constant, but try seeing what is actually stored in the operation --- doc/aerospike.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index a1b0574e01..aba9670491 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -161,9 +161,12 @@ Types # [1, 2, 3] and [1, 'a'] operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] import sys - print(operations, file=sys.stderr) print(aerospike.OP_LIST_GET_BY_VALUE, file=sys.stderr) print(type(aerospike.OP_LIST_GET_BY_VALUE), file=sys.stderr) + print(operations, file=sys.stderr) + print(operations[0]) + print(operations[0]['op']) + print(type(operations[0]['op'])) _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 From 26e0cf3ad9197c158dc97facb028ce399cc5cd5f Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:50:27 -0700 Subject: [PATCH 054/127] Forgot to print to stderr. doctest captures stdout --- doc/aerospike.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index aba9670491..65d2ce0c73 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -164,9 +164,9 @@ Types print(aerospike.OP_LIST_GET_BY_VALUE, file=sys.stderr) print(type(aerospike.OP_LIST_GET_BY_VALUE), file=sys.stderr) print(operations, file=sys.stderr) - print(operations[0]) - print(operations[0]['op']) - print(type(operations[0]['op'])) + print(operations[0], file=sys.stderr) + print(operations[0]['op'], file=sys.stderr) + print(type(operations[0]['op']), file=sys.stderr) _, _, bins = client.operate(key, operations) .. versionadded:: 3.5.0 From 21e30b68825e2a34466699898eff6583f563081a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:04:32 -0700 Subject: [PATCH 055/127] Try not mocking out aerospike to see if doctest extension depends on this setting to run aerospike_helper API methods --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index c603734228..fa05ddac4e 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -26,7 +26,7 @@ # return MagicMock() -autodoc_mock_imports = ["aerospike"] +# autodoc_mock_imports = ["aerospike"] # sys.path.append(os.path.abspath('/usr/local/lib/python2.7/site-packages/aerospike-1.0.44-py2.7-macosx-10.9-x86_64.egg/')) From d319647846e8799a08e3099bd2f7ba9aecea185b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:27:41 -0700 Subject: [PATCH 056/127] Conditionally mock out aerospike if it isn't installed. This way doctest can run the code examples and aerospike_helpers doesn't use a mocked out aerospike when it does exist --- doc/conf.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index fa05ddac4e..94c23ce51d 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -26,7 +26,10 @@ # return MagicMock() -# autodoc_mock_imports = ["aerospike"] +try: + import aerospike +except ImportError: + autodoc_mock_imports = ["aerospike"] # sys.path.append(os.path.abspath('/usr/local/lib/python2.7/site-packages/aerospike-1.0.44-py2.7-macosx-10.9-x86_64.egg/')) From 792b1b6948fd3ed928dec53bfeb1bdd1e4d38599 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:37:57 -0700 Subject: [PATCH 057/127] Fix 2 code examples in aerospike.rst. This assumes the elements are returned in list order --- doc/aerospike.rst | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 65d2ce0c73..ceb2409b80 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -156,18 +156,16 @@ Types client = aerospike.client({'hosts': [('localhost', 3000)]}) key = 'test', 'demo', 1 - # get all values of the form [1, ] from a list of lists. - # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - # [1, 2, 3] and [1, 'a'] + client.put(key, bins={"list_bin": [[1, 2, 3], [2, 3, 4], [1, 'a']]}) + + # get all values of the form [1, ...] from a list of lists. operations = [list_ops.list_get_by_value('list_bin', [1, aerospike.CDTWildcard()], aerospike.LIST_RETURN_VALUE)] - import sys - print(aerospike.OP_LIST_GET_BY_VALUE, file=sys.stderr) - print(type(aerospike.OP_LIST_GET_BY_VALUE), file=sys.stderr) - print(operations, file=sys.stderr) - print(operations[0], file=sys.stderr) - print(operations[0]['op'], file=sys.stderr) - print(type(operations[0]['op']), file=sys.stderr) _, _, bins = client.operate(key, operations) + print(bins["list_bin"]) + + .. testoutput:: + + [[1, 2, 3], [1, 'a']] .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater @@ -187,11 +185,16 @@ Types client = aerospike.client({'hosts': [('localhost', 3000)]}) key = 'test', 'demo', 1 - # get all values of the form [1, ] from a list of lists. - # For example if list is [[1, 2, 3], [2, 3, 4], [1, 'a']], this operation will match - # [1, 2, 3] and [1, 'a'] + client.put(key, bins={"list_bin": [[1, 2, 3], [2, 3, 4], [1, 'a']]}) + + # get all values of the form [1, ...] from a list of lists. operations = [list_ops.list_get_by_value_range('list_bin', aerospike.LIST_RETURN_VALUE, [1], [1, aerospike.CDTInfinite()])] _, _, bins = client.operate(key, operations) + print(bins["list_bin"]) + + .. testoutput:: + + [[1, 2, 3], [1, 'a']] .. versionadded:: 3.5.0 .. note:: This requires Aerospike Server 4.3.1.3 or greater From f4e3b8422fbb5837c7b50ed487a00575f45d4625 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:43:00 -0700 Subject: [PATCH 058/127] Expand code example for hyperloglog --- aerospike_helpers/__init__.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index aa482023aa..d7a405a46e 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -20,9 +20,20 @@ class HyperLogLog(bytes): The constructor takes in any argument that the :class:`bytes` constructor takes in. - >>> from aerospike_helpers import HyperLogLog - >>> h = HyperLogLog([1, 2, 3]) - >>> client.put(key, {"hyperloglog": h}) + .. testcode:: + + from aerospike_helpers import HyperLogLog + h = HyperLogLog([1, 2, 3]) + + client = aerospike.client({'hosts': [('localhost', 3000)]}) + client.put(key, {"hyperloglog": h}) + + _, _, bins = client.get(key) + print(bins["hyperloglog"]) + + .. testoutput:: + + HyperLogLog(...) """ def __new__(cls, o) -> "HyperLogLog": return super().__new__(cls, o) From dca10b08daa554766ebc58e134a0443eeed93f59 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:50:58 -0700 Subject: [PATCH 059/127] Address a few more code examples failing. --- aerospike_helpers/batch/records.py | 15 +++++++++------ aerospike_helpers/cdt_ctx.py | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/aerospike_helpers/batch/records.py b/aerospike_helpers/batch/records.py index c4bf02810b..78e665fcc6 100644 --- a/aerospike_helpers/batch/records.py +++ b/aerospike_helpers/batch/records.py @@ -339,12 +339,15 @@ def __init__(self, batch_records: Optional[TypeBatchRecordList] = None) -> None: for br in brs.batch_records: print(br.result) print(br.record) - # 0 - # (('test', 'demo', 1, bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) - # 0 - # (('test', 'demo', 2, bytearray(b'...')), {'ttl': 2592000, 'gen': 4}, {'id': 100}) - # 0 - # (('test', 'demo', 3, bytearray(b'...')), {'ttl': 2592000, 'gen': 3}, {'id': 1}) + + .. testoutput:: + + 0 + (('test', 'demo', 1, bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) + 0 + (('test', 'demo', 2, bytearray(b'...')), {'ttl': 2592000, 'gen': 4}, {'id': 100}) + 0 + (('test', 'demo', 3, bytearray(b'...')), {'ttl': 2592000, 'gen': 3}, {'id': 1}) """ if batch_records is None: diff --git a/aerospike_helpers/cdt_ctx.py b/aerospike_helpers/cdt_ctx.py index f8d30eb84d..8449244e72 100644 --- a/aerospike_helpers/cdt_ctx.py +++ b/aerospike_helpers/cdt_ctx.py @@ -83,6 +83,7 @@ # Example 3: create a CDT secondary index from a base64 encoded _cdt_ctx with info command policy = {} + ctx_list_index = [cdt_ctx.cdt_ctx_list_index(0)] bs_b4_cdt = client.get_cdtctx_base64(ctx_list_index) r = [] From 46b4a20a060a146d8f9687efe41f305386d7bad0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:03:07 -0700 Subject: [PATCH 060/127] Address more code example failures. Use ellipsis for nonexact matches --- .../operations/bitwise_operations.py | 18 ++++++++++++------ .../operations/expression_operations.py | 12 ++++++++---- aerospike_helpers/operations/hll_operations.py | 18 ++++++++++++------ doc/client.rst | 12 ++++++------ 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index 67b48c1b9e..364ed724ad 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -54,13 +54,11 @@ _, _, bins = client.get(key) print("5 bytes: ", bins) - # 5 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01'} _, _, _ = client.operate(key, ops) _, _, newbins = client.get(key) print("After resize to 10 bytes: ", newbins) - # After resize to 10 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00'} # EXAMPLE 2: shrink the five_ones bin to a bytesize of 5 from the front. @@ -73,12 +71,17 @@ _, _, _ = client.operate(key, ops) _, _, newbins = client.get(key) print("After resize to 5 bytes again: ", newbins) - # After resize to 5 bytes again: {'bitwise1': b'\x00\x00\x00\x00\x00'} # Cleanup and close the connection to the Aerospike cluster. client.remove(key) client.close() + .. testoutput:: + + 5 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01'} + After resize to 10 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00'} + After resize to 5 bytes again: {'bitwise1': b'\x00\x00\x00\x00\x00'} + Example: .. testcode:: @@ -110,7 +113,6 @@ ] _, _, results = client.operate(key, ops) print(results) - # {'bitwise1': b'\x01\x01\x01\x01\x01'} # Example 2: modify bits using the 'or' op, then read bits # 0 = offset @@ -123,7 +125,6 @@ ] _, _, results = client.operate(key, ops) print(results) - # {'bitwise1': b'\xff\x01\x01\x01\x01'} # Example 3: modify bits using the 'remove' op, then read bits' # offset = 0 @@ -135,10 +136,15 @@ ] _, _, results = client.operate(key, ops) print(results) - # {'bitwise1': b'\x01\x01\x01'} client.close() + .. testoutput:: + + {'bitwise1': b'\x01\x01\x01\x01\x01'} + {'bitwise1': b'\xff\x01\x01\x01\x01'} + {'bitwise1': b'\x01\x01\x01'} + .. seealso:: `Bits (Data Types) `_. """ import aerospike diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index 0573006bf7..eed98b0f28 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -54,7 +54,6 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r # Read the value of int bin "balance". # Let 'client' be a connected aerospike client. # Let int bin 'balance' == 50. - from aerospike_helpers.operations import expression_operations as expressions from aerospike_helpers.expressions import * @@ -65,7 +64,10 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r _, _, res = client.operate(self.key, ops) print(res) - # EXPECTED OUTPUT: {"balance": 50} + .. testoutput:: + + {"balance": 50} + """ op_dict = { @@ -98,7 +100,6 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ # Write the value of int bin "balance" + 50 back to "balance". # Let 'client' be a connected aerospike client. # Let int bin 'balance' == 50. - from aerospike_helpers.operations import expression_operations as expr_ops from aerospike_helpers.expressions import * @@ -110,7 +111,10 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ _, _, res = client.get(self.key) print(res) - # EXPECTED OUTPUT: {"balance": 100} + .. testoutput:: + + {"balance": 100} + """ op_dict = { diff --git a/aerospike_helpers/operations/hll_operations.py b/aerospike_helpers/operations/hll_operations.py index 73ea55de5f..40ebcd804b 100644 --- a/aerospike_helpers/operations/hll_operations.py +++ b/aerospike_helpers/operations/hll_operations.py @@ -80,24 +80,30 @@ # Pass in Amy's key _, _, res = client.operate(keys[0], ops) print("Estimated items viewed intersection:", res["viewed"]) - # Estimated items viewed intersection: 251 - # Actual intersection: 250 + print("Actual intersection: 250") # Find out how many unique products Amy, Farnsworth, and Scruffy have viewed. ops = [hll_ops.hll_get_union_count("viewed", viewed)] _, _, res = client.operate(keys[0], ops) print("Estimated items viewed union:", res["viewed"]) - # Estimated items viewed union: 1010 - # Actual union: 1000 + print("Actual union: 1000") # Find the similarity of Amy, Farnsworth, and Scruffy's product views. ops = [hll_ops.hll_get_similarity("viewed", viewed)] _, _, res = client.operate(keys[0], ops) print("Estimated items viewed similarity: %f%%" % (res["viewed"] * 100)) - # Estimated items viewed similarity: 24.888393% - # Actual similarity: 25% + print("Actual similarity: 25%") + +.. testoutput:: + + Estimated items viewed intersection: 251 + Actual intersection: 250 + Estimated items viewed union: 1010 + Actual union: 1000 + Estimated items viewed similarity: 24.888393% + Actual similarity: 25% """ diff --git a/doc/client.rst b/doc/client.rst index e7b5b75d0c..10384c12de 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -489,7 +489,7 @@ String Operations .. testoutput:: - {"bin1": "Gordon Freeman"} + {'bin1': 'Gordon Freeman'} .. index:: single: Numeric Operations @@ -523,12 +523,12 @@ Numeric Operations # Gain health client.increment(keyTuple, 'lives', 10) (key, meta, bins) = client.get(keyTuple) - print("Lives:", bins) + print("Lives:", bins["lives"]) # Take damage client.increment(keyTuple, 'lives', -90) (key, meta, bins) = client.get(keyTuple) - print("Lives:", bins) + print("Lives:", bins["lives"]) .. testoutput:: @@ -766,7 +766,7 @@ Info Operations .. testoutput:: - [{'address': '1.1.1.1', 'port': 3000, 'node_name': 'BCER199932C'}, {'address': '1.1.1.1', 'port': 3010, 'node_name': 'ADFFE7782CD'}] + [{'address': '...', 'port': ..., 'node_name': '...'}...] .. versionchanged:: 6.0.0 @@ -785,7 +785,7 @@ Info Operations .. testoutput:: - [('127.0.0.1', 3000), ('127.0.0.1', 3010)] + [('127.0.0.1', 3000)...] .. versionchanged:: 3.0.0 @@ -821,7 +821,7 @@ Info Operations .. testoutput:: - {'BB9020011AC4202': (None, 'test\n')} + {'...': (None, 'test\n')} .. versionadded:: 3.0.0 From 2477217eebc3e14de840e823f1f182957c7bd9f2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:11:17 -0700 Subject: [PATCH 061/127] Fix udf examples --- doc/client.rst | 63 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/doc/client.rst b/doc/client.rst index 10384c12de..3c6d77c094 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -591,6 +591,35 @@ Transactions User Defined Functions ---------------------- +.. testcode:: udf + + import aerospike + + config = { + 'hosts': [ ('127.0.0.1', 3000)], + 'lua': { 'user_path': '~/lua-scripts/'} + } + client = aerospike.client(config) + # Register the UDF module and copy it to the Lua 'user_path' + client.udf_put('doc/examples/scan/my_udf.lua') + + print("Before remove:", client.udf_list()) + + client.udf_remove('my_udf.lua') + print("After remove:", client.udf_list()) + + client.close() + +.. testoutput:: + + [ + {'content': bytearray(b'...'), + 'hash': bytearray(b'...'), + 'name': 'my_udf.lua', + 'type': 0} + ] + [] + .. class:: Client :noindex: @@ -612,19 +641,6 @@ User Defined Functions .. :emphasize-lines: 5,9 - .. testcode:: - - import aerospike - - config = { - 'hosts': [ ('127.0.0.1', 3000)], - 'lua': { 'user_path': '/path/to/lua/user_path'} - } - client = aerospike.client(config) - # Register the UDF module and copy it to the Lua 'user_path' - client.udf_put('/path/to/my_module.lua') - client.close() - .. method:: udf_remove(module[, policy: dict]) Remove a previously registered UDF module from the cluster. @@ -635,10 +651,6 @@ User Defined Functions :param dict policy: currently **timeout** in milliseconds is the available policy. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. testcode:: - - client.udf_remove('my_module.lua') - .. method:: udf_list([policy: dict]) -> [] Return the list of UDF modules registered with the cluster. @@ -647,23 +659,6 @@ User Defined Functions :rtype: :class:`list` :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. testcode:: - - print(client.udf_list()) - - .. testoutput:: - - [ - {'content': bytearray(b''), - 'hash': bytearray(b'195e39ceb51c110950bd'), - 'name': 'my_udf1.lua', - 'type': 0}, - {'content': bytearray(b''), - 'hash': bytearray(b'8a2528e8475271877b3b'), - 'name': 'stream_udf.lua', - 'type': 0} - ] - .. method:: udf_get(module: str[, language: int = aerospike.UDF_TYPE_LUA[, policy: dict]]) -> str Return the content of a UDF module which is registered with the cluster. From 5bb7d3f48750318c25b187a377852c02661264ad Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:15:03 -0700 Subject: [PATCH 062/127] Add missing import --- aerospike_helpers/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index d7a405a46e..05274bb325 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -23,6 +23,8 @@ class HyperLogLog(bytes): .. testcode:: from aerospike_helpers import HyperLogLog + import aerospike + h = HyperLogLog([1, 2, 3]) client = aerospike.client({'hosts': [('localhost', 3000)]}) From 3145ba71f6bd72cb1d8af56ddb8b68fe7aa1327e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:17:42 -0700 Subject: [PATCH 063/127] Make sure udf output matches code example --- doc/client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index 3c6d77c094..215b454d5f 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -610,7 +610,7 @@ User Defined Functions client.close() -.. testoutput:: +.. testoutput:: udf [ {'content': bytearray(b'...'), From fa2a5f68087804b8235b0e3d0b2b16b07e2fcf44 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:22:33 -0700 Subject: [PATCH 064/127] Fix indentation --- .../operations/bitwise_operations.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index 364ed724ad..7f234bd916 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -76,11 +76,11 @@ client.remove(key) client.close() - .. testoutput:: +.. testoutput:: - 5 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01'} - After resize to 10 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00'} - After resize to 5 bytes again: {'bitwise1': b'\x00\x00\x00\x00\x00'} + 5 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01'} + After resize to 10 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00'} + After resize to 5 bytes again: {'bitwise1': b'\x00\x00\x00\x00\x00'} Example: @@ -139,11 +139,11 @@ client.close() - .. testoutput:: +.. testoutput:: - {'bitwise1': b'\x01\x01\x01\x01\x01'} - {'bitwise1': b'\xff\x01\x01\x01\x01'} - {'bitwise1': b'\x01\x01\x01'} + {'bitwise1': b'\x01\x01\x01\x01\x01'} + {'bitwise1': b'\xff\x01\x01\x01\x01'} + {'bitwise1': b'\x01\x01\x01'} .. seealso:: `Bits (Data Types) `_. """ From 45f8580ef10c2c8c64ac1fffdd47a8052283b798 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:24:31 -0700 Subject: [PATCH 065/127] Add missing key --- aerospike_helpers/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 05274bb325..133e2e0229 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -28,6 +28,7 @@ class HyperLogLog(bytes): h = HyperLogLog([1, 2, 3]) client = aerospike.client({'hosts': [('localhost', 3000)]}) + key = ("test", "demo", 1) client.put(key, {"hyperloglog": h}) _, _, bins = client.get(key) From e5b0d3a8b7d3f7e9336a02411eead1d36581da2a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:26:26 -0700 Subject: [PATCH 066/127] Attempt to fix udf code example error --- doc/client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index 215b454d5f..c5f8bfecf2 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -601,7 +601,7 @@ User Defined Functions } client = aerospike.client(config) # Register the UDF module and copy it to the Lua 'user_path' - client.udf_put('doc/examples/scan/my_udf.lua') + client.udf_put('./examples/scan/my_udf.lua') print("Before remove:", client.udf_list()) From ef1a6b18545606267ecb7109cdba099c1d4a79df Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:29:31 -0700 Subject: [PATCH 067/127] This example doesn't explicitly test ttl/gen so we can just ignore it --- aerospike_helpers/batch/records.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aerospike_helpers/batch/records.py b/aerospike_helpers/batch/records.py index 78e665fcc6..5bd9fd3696 100644 --- a/aerospike_helpers/batch/records.py +++ b/aerospike_helpers/batch/records.py @@ -343,11 +343,11 @@ def __init__(self, batch_records: Optional[TypeBatchRecordList] = None) -> None: .. testoutput:: 0 - (('test', 'demo', 1, bytearray(b'...')), {'ttl': 4294967295, 'gen': 0}, {}) + (('test', 'demo', 1, bytearray(b'...')), {...}, {}) 0 - (('test', 'demo', 2, bytearray(b'...')), {'ttl': 2592000, 'gen': 4}, {'id': 100}) + (('test', 'demo', 2, bytearray(b'...')), {...}, {'id': 100}) 0 - (('test', 'demo', 3, bytearray(b'...')), {'ttl': 2592000, 'gen': 3}, {'id': 1}) + (('test', 'demo', 3, bytearray(b'...')), {...}, {'id': 1}) """ if batch_records is None: From 3a0a1aebc25fc054732876c9a16e7c93227ac6a2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:05:39 -0700 Subject: [PATCH 068/127] Address one test failure --- aerospike_helpers/expressions/base.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/aerospike_helpers/expressions/base.py b/aerospike_helpers/expressions/base.py index 3527976451..dc70139333 100644 --- a/aerospike_helpers/expressions/base.py +++ b/aerospike_helpers/expressions/base.py @@ -1055,14 +1055,17 @@ def __init__(self, *exprs: _BaseExpr): ] record = client.operate(keyTuple, ops) print(record) - # (('test', 'demo', 'key', bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'results': 70}) client.put(keyTuple, {"operation": "divide"}) record = client.operate(keyTuple, ops) print(record) # Divide isn't supported, so we get -1 - # (('test', 'demo', 'key', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'results': -1}) + + .. testoutput:: + (('test', 'demo', 'key', bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'results': 70}) + (('test', 'demo', 'key', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'results': -1}) + """ self._children = exprs + (_GenericExpr(_ExprOp._AS_EXP_CODE_END_OF_VA_ARGS, 0, {}),) From 5ec69d2d2194cebc99913bc8b3663f79a49bcb36 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 1 May 2026 16:06:18 -0700 Subject: [PATCH 069/127] Address a few errors --- aerospike_helpers/expressions/base.py | 1 + doc/client.rst | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/aerospike_helpers/expressions/base.py b/aerospike_helpers/expressions/base.py index dc70139333..e263e48faa 100644 --- a/aerospike_helpers/expressions/base.py +++ b/aerospike_helpers/expressions/base.py @@ -1063,6 +1063,7 @@ def __init__(self, *exprs: _BaseExpr): # Divide isn't supported, so we get -1 .. testoutput:: + (('test', 'demo', 'key', bytearray(b'...')), {'ttl': 2592000, 'gen': 1}, {'results': 70}) (('test', 'demo', 'key', bytearray(b'...')), {'ttl': 2592000, 'gen': 2}, {'results': -1}) diff --git a/doc/client.rst b/doc/client.rst index c5f8bfecf2..b8513e4859 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -612,13 +612,8 @@ User Defined Functions .. testoutput:: udf - [ - {'content': bytearray(b'...'), - 'hash': bytearray(b'...'), - 'name': 'my_udf.lua', - 'type': 0} - ] - [] + Before remove: [{'content': bytearray(b'...'), 'hash': bytearray(b'...'), 'name': 'my_udf.lua', 'type': 0}] + After remove: [] .. class:: Client :noindex: From 8d9675aa51e4bf149515d33dead302369b4765de Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 1 May 2026 16:10:35 -0700 Subject: [PATCH 070/127] Address a few errors --- aerospike_helpers/cdt_ctx.py | 7 +++++-- aerospike_helpers/operations/bitwise_operations.py | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/aerospike_helpers/cdt_ctx.py b/aerospike_helpers/cdt_ctx.py index 8449244e72..d886c5fd29 100644 --- a/aerospike_helpers/cdt_ctx.py +++ b/aerospike_helpers/cdt_ctx.py @@ -53,7 +53,6 @@ _, _, result = client.operate(key, ops) print(result) - # {'users': 200} # Example 2: add a new person and get their rating of Facebook cindy = { @@ -78,7 +77,6 @@ _, _, result = client.operate(key, ops) print(result) - # {'users': 4} # Example 3: create a CDT secondary index from a base64 encoded _cdt_ctx with info command policy = {} @@ -104,6 +102,11 @@ client.remove(key) client.close() +.. testoutput:: + + {'users': 200} + {'users': 4} + .. _path_expressions_contexts: Path Expressions Contexts diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index 7f234bd916..a4182a338d 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -78,9 +78,9 @@ .. testoutput:: - 5 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01'} - After resize to 10 bytes: {'bitwise1': b'\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00'} - After resize to 5 bytes again: {'bitwise1': b'\x00\x00\x00\x00\x00'} + 5 bytes: {'bitwise1': b'\\x01\\x01\\x01\\x01\\x01'} + After resize to 10 bytes: {'bitwise1': b'\\x01\\x01\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00'} + After resize to 5 bytes again: {'bitwise1': b'\\x00\\x00\\x00\\x00\\x00'} Example: @@ -141,9 +141,9 @@ .. testoutput:: - {'bitwise1': b'\x01\x01\x01\x01\x01'} - {'bitwise1': b'\xff\x01\x01\x01\x01'} - {'bitwise1': b'\x01\x01\x01'} + {'bitwise1': b'\\x01\\x01\\x01\\x01\\x01'} + {'bitwise1': b'\\xff\\x01\\x01\\x01\\x01'} + {'bitwise1': b'\\x01\\x01\\x01'} .. seealso:: `Bits (Data Types) `_. """ From 6d295c9128f734386ecd9bf8d57493f5d9b7fd7b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 1 May 2026 16:13:39 -0700 Subject: [PATCH 071/127] Getting import errors for expr op examples. Not sure why this is happening so try moving imports to the top --- aerospike_helpers/operations/expression_operations.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index eed98b0f28..ae4b55555f 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -51,12 +51,12 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r .. testcode:: - # Read the value of int bin "balance". - # Let 'client' be a connected aerospike client. - # Let int bin 'balance' == 50. from aerospike_helpers.operations import expression_operations as expressions from aerospike_helpers.expressions import * + # Read the value of int bin "balance". + # Let 'client' be a connected aerospike client. + # Let int bin 'balance' == 50. expr = IntBin("balance").compile() ops = [ expressions.expression_read("balance", expr) @@ -97,11 +97,12 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ .. testcode:: + from aerospike_helpers.operations import expression_operations as expr_ops + from aerospike_helpers.expressions import * + # Write the value of int bin "balance" + 50 back to "balance". # Let 'client' be a connected aerospike client. # Let int bin 'balance' == 50. - from aerospike_helpers.operations import expression_operations as expr_ops - from aerospike_helpers.expressions import * expr = Add(IntBin("balance"), 50).compile() ops = [ From 23e1d2a74bc984e3cf7258971cafd2e4f8cc3bb5 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 1 May 2026 16:20:02 -0700 Subject: [PATCH 072/127] Show logs to see why HyperLogLog code example fails --- .github/workflows/smoke-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index e8760c9901..641fe87303 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -429,6 +429,9 @@ jobs: run: sphinx-build -b doctest . doctest working-directory: doc + - if: ${{ !cancelled() }} + run: docker logs aerospike + test-ee: runs-on: ubuntu-22.04 needs: build From e36bc42d19b870fb0cfee52419a4cdad042c5112 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 1 May 2026 16:46:25 -0700 Subject: [PATCH 073/127] Add scaffolding for hll code examples TODO not done --- aerospike_helpers/expressions/hll.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index bcb364573d..a2c7ab9552 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -42,6 +42,18 @@ class HLLInit(_BaseExpr): If 1 of index_bit_count or mh_bit_count are set, an existing HLL bin will set that config and retain its current value for the unset config. If the HLL bin does not exist, index_bit_count is required to create it, mh_bit_count is optional. + + .. testsetup:: + + import aerospike + client = aerospike.client({'hosts': [('localhost', 3000)]}) + ops = [ + expr = exp.HLLInit(None, 12, 24, exp.HLLBin("d")) + ] + key = ("test", "demo", 1) + client.operate(key, ops) + + values = [] """ _op = aerospike.OP_HLL_INIT From 2e0c039ad878430caabd06f29d1f27fc9ec16c7a Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 11:09:38 -0700 Subject: [PATCH 074/127] Just ignore todo for this pr --- aerospike_helpers/operations/bitwise_operations.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index 32d283a273..23b7144c59 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -167,11 +167,9 @@ def bit_resize(bin_name: str, byte_size, policy=None, resize_flags: int = 0): bin_name (str): The name of the bin containing the map. byte_size (int): The new size of the bytes. policy (dict): The :ref:`bit_policy ` dictionary. default: None. - # TODO: this is the only method referencing these flags, but should document explicitly that - # the flags can be bitwise OR'd together. resize_flags (int): :ref:`aerospike_bitwise_resize_flag` modifying the resize behavior - (default ``aerospike.BIT_RESIZE_DEFAULT``), such as ``aerospike.BIT_RESIZE_GROW_ONLY | - aerospike.BIT_RESIZE_FROM_FRONT``. + (default :py:data:`aerospike.BIT_RESIZE_DEFAULT`), such as py:data:`aerospike.BIT_RESIZE_GROW_ONLY` | + :py:data:`aerospike.BIT_RESIZE_FROM_FRONT`. Returns: A dictionary usable in operate or operate_ordered. The format of the dictionary From 41971fa166ebe09d691632df4bf49978a19738f3 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 11:10:48 -0700 Subject: [PATCH 075/127] Cant find cross reference from google so just ignore --- doc/aerospike.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 111d1a3083..3503d3ceb7 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -140,8 +140,6 @@ Types A type for distinguishing a server-side null from a Python :py:obj:`None`. Replaces the constant ``aerospike.null``. - .. Needs cross reference to server docs - :return: a type representing the server-side type ``as_null``. .. versionadded:: 2.0.1 From d789c99213eaf81a37f2cb13ff365e9545fef921 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 11:19:19 -0700 Subject: [PATCH 076/127] Replace aerospike constants with cross references to verify that they are correct with sphinx nitpicky mode --- aerospike_helpers/cdt_ctx.py | 4 ++-- .../operations/expression_operations.py | 8 +++++--- aerospike_helpers/operations/list_operations.py | 2 +- doc/client.rst | 14 +++++++------- doc/query.rst | 4 ++-- doc/scan.rst | 4 ++-- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/aerospike_helpers/cdt_ctx.py b/aerospike_helpers/cdt_ctx.py index 7aa758fd40..37693e91f8 100644 --- a/aerospike_helpers/cdt_ctx.py +++ b/aerospike_helpers/cdt_ctx.py @@ -231,7 +231,7 @@ def cdt_ctx_list_index_create(index: int, order: int = 0, pad: bool = False) -> Args: index (int): The index to create the list at. order (int): The :ref:`sort order ` to create the List with. - (default: ``aerospike.LIST_UNORDERED``) + (default: :py:data:`aerospike.LIST_UNORDERED`) pad (bool): If index is out of bounds and ``pad`` is :py:obj:`True`, then the list will be created at the index with :py:obj:`None` elements inserted behind it. ``pad`` is only compatible with unordered lists. @@ -309,7 +309,7 @@ def cdt_ctx_map_key_create(key: Any, order: int = 0) -> _cdt_ctx: Args: key (object): The key to create the map at. order (int): The :ref:`sort order ` to create the List with. - (default: ``aerospike.MAP_UNORDERED``) + (default: :py:data:`aerospike.MAP_UNORDERED`) Returns: :class:`~aerospike_helpers.cdt_ctx._cdt_ctx` diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index da5ec131ab..fa1719b36f 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -43,7 +43,8 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r bin_name (str): The name of the bin to read from. Even if no bin is being read from, the value will be returned with this bin name. expression: A compiled Aerospike expression, see :ref:`aerospike_operation_helpers.expressions`. - expression_read_flags (int): :ref:`aerospike_expression_read_flags` (default ``aerospike.EXP_READ_DEFAULT``) + expression_read_flags (int): :ref:`aerospike_expression_read_flags` + (default :py:data:`aerospike.EXP_READ_DEFAULT`) Returns: A dictionary to be passed to operate or operate_ordered. @@ -84,8 +85,9 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ Args: bin_name (str): The name of the bin to write to. expression: A compiled Aerospike expression, see :ref:`aerospike_operation_helpers.expressions`. - expression_write_flags (int): :ref:`aerospike_expression_write_flags` such as ``aerospike.EXP_WRITE_UPDATE_ONLY - | aerospike.EXP_WRITE_POLICY_NO_FAIL`` (default ``aerospike.EXP_WRITE_DEFAULT``). + expression_write_flags (int): :ref:`aerospike_expression_write_flags` such as + :py:data:`aerospike.EXP_WRITE_UPDATE_ONLY` | :py:data:`aerospike.EXP_WRITE_POLICY_NO_FAIL` + (default :py:data:`aerospike.EXP_WRITE_DEFAULT`). Returns: A dictionary to be passed to operate or operate_ordered. diff --git a/aerospike_helpers/operations/list_operations.py b/aerospike_helpers/operations/list_operations.py index 238929bd4a..6338394fd7 100755 --- a/aerospike_helpers/operations/list_operations.py +++ b/aerospike_helpers/operations/list_operations.py @@ -1013,7 +1013,7 @@ def list_sort(bin_name: str, sort_flags: int = 0, ctx: Optional[list] = None): Args: bin_name (str): The name of the bin to sort. sort_flags (int): :ref:`aerospike_list_sort_flag` modifying the sorting behavior - (default ``aerospike.DEFAULT_LIST_SORT``). + (default :py:data`aerospike.DEFAULT_LIST_SORT`). ctx (list): An optional list of nested CDT :class:`cdt_ctx ` context operation objects. Returns: diff --git a/doc/client.rst b/doc/client.rst index 7ca4a3009d..4ed579f1c2 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -668,7 +668,7 @@ User Defined Functions :param dict policy: optional dictionary that takes in both :ref:`aerospike_scan_policies` and :ref:`aerospike_info_policies`. :param dict options: the :ref:`aerospike_scan_options` that will apply to the scan. :rtype: :class:`int` - :return: a job ID that can be used with :meth:`job_info` to check the status of the ``aerospike.JOB_SCAN``. + :return: a job ID that can be used with :meth:`job_info` to check the status of the :py:data:`aerospike.JOB_SCAN`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. .. method:: query_apply(ns, set, predicate, module, function[, args[, policy: dict]]) -> int @@ -685,7 +685,7 @@ User Defined Functions :param list args: the arguments to the UDF. :param dict policy: optional dictionary that takes in both :ref:`aerospike_write_policies` and :ref:`aerospike_info_policies`. :rtype: :class:`int` - :return: a job ID that can be used with :meth:`job_info` to check the status of the ``aerospike.JOB_QUERY``. + :return: a job ID that can be used with :meth:`job_info` to check the status of the :py:data:`aerospike.JOB_QUERY`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. .. method:: job_info(job_id, module[, policy: dict]) -> dict @@ -1804,7 +1804,7 @@ Read Policies * **replica** | One of the :ref:`POLICY_REPLICA` values such as :data:`aerospike.POLICY_REPLICA_MASTER` | - | Default: ``aerospike.POLICY_REPLICA_SEQUENCE`` + | Default: :py:data:`aerospike.POLICY_REPLICA_SEQUENCE` .. _aerospike_operate_policies: @@ -1968,7 +1968,7 @@ Remove Policies * **replica** | One of the :ref:`POLICY_REPLICA` values such as :data:`aerospike.POLICY_REPLICA_MASTER` | - | Default: ``aerospike.POLICY_REPLICA_SEQUENCE`` + | Default: :py:data:`aerospike.POLICY_REPLICA_SEQUENCE` .. _aerospike_batch_policies: @@ -2287,12 +2287,12 @@ List Policies * **write_flags** | Write flags for the operation. - | One of the :ref:`aerospike_list_write_flag` values such as :data:`aerospike.LIST_WRITE_DEFAULT` + | One of the :ref:`aerospike_list_write_flag` values. | | Default: :data:`aerospike.LIST_WRITE_DEFAULT` | | Values should be or'd together: - | ``aerospike.LIST_WRITE_ADD_UNIQUE | aerospike.LIST_WRITE_INSERT_BOUNDED`` + | :py:data:`aerospike.LIST_WRITE_ADD_UNIQUE` | :py:data:`aerospike.LIST_WRITE_INSERT_BOUNDED` * **list_order** | Ordering to maintain for the list. @@ -2328,7 +2328,7 @@ Map Policies | Default: :data:`aerospike.MAP_WRITE_FLAGS_DEFAULT` | | Values should be or'd together: - | ``aerospike.LIST_WRITE_ADD_UNIQUE | aerospike.LIST_WRITE_INSERT_BOUNDED`` + | :data:`aerospike.LIST_WRITE_ADD_UNIQUE` | :data:`aerospike.LIST_WRITE_INSERT_BOUNDED` .. note:: This is only valid for Aerospike Server versions >= 4.3.0. diff --git a/doc/query.rst b/doc/query.rst index cbcbed7e6e..7e8d410d3c 100755 --- a/doc/query.rst +++ b/doc/query.rst @@ -327,7 +327,7 @@ Assume this boilerplate code is run before all examples below: :param dict policy: optional :ref:`aerospike_write_policies`. - :return: a job ID that can be used with :meth:`~aerospike.Client.job_info` to track the status of the ``aerospike.JOB_QUERY`` , as it runs in the background. + :return: a job ID that can be used with :meth:`~aerospike.Client.job_info` to track the status of the :py:data:`aerospike.JOB_QUERY` , as it runs in the background. .. code-block:: python @@ -532,7 +532,7 @@ Policies * **replica** | One of the :ref:`POLICY_REPLICA` values such as :data:`aerospike.POLICY_REPLICA_MASTER` | - | Default: ``aerospike.POLICY_REPLICA_SEQUENCE`` + | Default: :py:data:`aerospike.POLICY_REPLICA_SEQUENCE` .. _aerospike_query_options: diff --git a/doc/scan.rst b/doc/scan.rst index 258172fc31..b6247111a8 100755 --- a/doc/scan.rst +++ b/doc/scan.rst @@ -298,7 +298,7 @@ Methods :param dict policy: optional :ref:`aerospike_write_policies`. - :return: a job ID that can be used with :meth:`~aerospike.Client.job_info` to track the status of the ``aerospike.JOB_SCAN``, as it runs in the background. + :return: a job ID that can be used with :meth:`~aerospike.Client.job_info` to track the status of the :py:data:`aerospike.JOB_SCAN`, as it runs in the background. .. note:: Python client version 3.10.0 implemented scan execute_background. @@ -481,7 +481,7 @@ Policies * **replica** | One of the :ref:`POLICY_REPLICA` values such as :data:`aerospike.POLICY_REPLICA_MASTER` | - | Default: ``aerospike.POLICY_REPLICA_SEQUENCE`` + | Default: :py:data:`aerospike.POLICY_REPLICA_SEQUENCE` * **ttl** (:class:`int`) The default time-to-live (expiration) of the record in seconds. This field will only be used on background scan writes if :py:attr:`aerospike.Scan.ttl` is set to From 97602559cfb9e9b37798051f67bb56290042a067 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 14:57:26 -0700 Subject: [PATCH 077/127] Fix formatting. Remove TODO comment that is causing formatting error --- aerospike_helpers/operations/expression_operations.py | 4 ++-- doc/client.rst | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index fa1719b36f..bdefc04eef 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -86,8 +86,8 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ bin_name (str): The name of the bin to write to. expression: A compiled Aerospike expression, see :ref:`aerospike_operation_helpers.expressions`. expression_write_flags (int): :ref:`aerospike_expression_write_flags` such as - :py:data:`aerospike.EXP_WRITE_UPDATE_ONLY` | :py:data:`aerospike.EXP_WRITE_POLICY_NO_FAIL` - (default :py:data:`aerospike.EXP_WRITE_DEFAULT`). + :py:data:`aerospike.EXP_WRITE_UPDATE_ONLY` | :py:data:`aerospike.EXP_WRITE_POLICY_NO_FAIL` + (default :py:data:`aerospike.EXP_WRITE_DEFAULT`). Returns: A dictionary to be passed to operate or operate_ordered. diff --git a/doc/client.rst b/doc/client.rst index 4ed579f1c2..ee9c225b5c 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -483,8 +483,6 @@ Numeric Operations :param int offset: the value by which to increment the value in *bin*. :type offset: :py:class:`int` or :py:class:`float` :param dict meta: record metadata to be set. See :ref:`metadata_dict`. - .. Does this still apply? - :param dict policy: optional :ref:`aerospike_operate_policies`. Note: the ``exists`` policy option may not be: :py:data:`aerospike.POLICY_EXISTS_CREATE_OR_REPLACE` nor :py:data:`aerospike.POLICY_EXISTS_REPLACE` :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. From ba84596ff90b481bff762cf2b128d5cdca5f2819 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 15:06:38 -0700 Subject: [PATCH 078/127] Fix bad syntax. --- aerospike_helpers/operations/list_operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/operations/list_operations.py b/aerospike_helpers/operations/list_operations.py index 6338394fd7..a84ad11708 100755 --- a/aerospike_helpers/operations/list_operations.py +++ b/aerospike_helpers/operations/list_operations.py @@ -1013,7 +1013,7 @@ def list_sort(bin_name: str, sort_flags: int = 0, ctx: Optional[list] = None): Args: bin_name (str): The name of the bin to sort. sort_flags (int): :ref:`aerospike_list_sort_flag` modifying the sorting behavior - (default :py:data`aerospike.DEFAULT_LIST_SORT`). + (default :py:data:`aerospike.DEFAULT_LIST_SORT`). ctx (list): An optional list of nested CDT :class:`cdt_ctx ` context operation objects. Returns: From e99684bf4d1640f2c448f7d4901b08eb9c1897e2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 15:13:08 -0700 Subject: [PATCH 079/127] Fix invalid cross ref --- aerospike_helpers/operations/list_operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/operations/list_operations.py b/aerospike_helpers/operations/list_operations.py index a84ad11708..9ba0d1f175 100755 --- a/aerospike_helpers/operations/list_operations.py +++ b/aerospike_helpers/operations/list_operations.py @@ -1013,7 +1013,7 @@ def list_sort(bin_name: str, sort_flags: int = 0, ctx: Optional[list] = None): Args: bin_name (str): The name of the bin to sort. sort_flags (int): :ref:`aerospike_list_sort_flag` modifying the sorting behavior - (default :py:data:`aerospike.DEFAULT_LIST_SORT`). + (default :py:data:`aerospike.LIST_SORT_DEFAULT`). ctx (list): An optional list of nested CDT :class:`cdt_ctx ` context operation objects. Returns: From bbeeb8e297f0c5517df705a376f1fd4b3fe85122 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 16:59:56 -0700 Subject: [PATCH 080/127] Remove this to try fixing docs build error --- doc/aerospike.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/aerospike.rst b/doc/aerospike.rst index 3503d3ceb7..4ed1b8c5ad 100644 --- a/doc/aerospike.rst +++ b/doc/aerospike.rst @@ -1250,11 +1250,11 @@ List Sort Flags Flags used by list sort. -.. data:: aerospike.LIST_SORT_DEFAULT +.. data:: LIST_SORT_DEFAULT Default. Preserve duplicates when sorting the list. -.. data:: aerospike.LIST_SORT_DROP_DUPLICATES +.. data:: LIST_SORT_DROP_DUPLICATES Drop duplicate values when sorting the list. From 047f82e65c65a6e3daf7fae62d6047d37d33f419 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 17:07:31 -0700 Subject: [PATCH 081/127] Fix code example by creating hll object in server instead of in code example... --- aerospike_helpers/__init__.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 133e2e0229..806ece989e 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -16,27 +16,27 @@ class HyperLogLog(bytes): """ - Represents a HyperLogLog value. This can be returned from the server or created in order to be sent to the server. - - The constructor takes in any argument that the :class:`bytes` constructor takes in. + Represents a HyperLogLog value. This can be returned from or sent to the server. .. testcode:: + from aerospike_helpers.operations import hll_operations - from aerospike_helpers import HyperLogLog - import aerospike - - h = HyperLogLog([1, 2, 3]) + BIN_NAME="hll" + ops = [ + hll_operations.hll_init(BIN_NAME, index_bit_count=4, mh_bit_count=4) + ] + client.operate(keyTuple, ops) + _, _, bins = client.get(keyTuple) + print(bins[BIN_NAME]) - client = aerospike.client({'hosts': [('localhost', 3000)]}) - key = ("test", "demo", 1) - client.put(key, {"hyperloglog": h}) - - _, _, bins = client.get(key) - print(bins["hyperloglog"]) + client.put(keyTuple, bins) + _, _, bins = client.get(keyTuple) + print(bins[BIN_NAME]) .. testoutput:: HyperLogLog(...) + HyperLogLog(...) """ def __new__(cls, o) -> "HyperLogLog": return super().__new__(cls, o) From 86daa850b10f8e626868a04915bb0fe2c7bac286 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 19:18:20 -0700 Subject: [PATCH 082/127] Fix indenting --- aerospike_helpers/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 806ece989e..77d3cee734 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -19,6 +19,7 @@ class HyperLogLog(bytes): Represents a HyperLogLog value. This can be returned from or sent to the server. .. testcode:: + from aerospike_helpers.operations import hll_operations BIN_NAME="hll" From 4676596e8b1f0d15e14de2ab8c4da2dbf832090e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 19:22:21 -0700 Subject: [PATCH 083/127] Address failing test case --- aerospike_helpers/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 77d3cee734..05f1b412d2 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -21,6 +21,9 @@ class HyperLogLog(bytes): .. testcode:: from aerospike_helpers.operations import hll_operations + import aerospike + + client = aerospike.client({'hosts': [('localhost', 3000)]}) BIN_NAME="hll" ops = [ From 0e11e733437e509fcc2bb7cfd54355fc8e339eda Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 19:26:11 -0700 Subject: [PATCH 084/127] Address failing test case --- aerospike_helpers/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aerospike_helpers/__init__.py b/aerospike_helpers/__init__.py index 05f1b412d2..731c3c286e 100644 --- a/aerospike_helpers/__init__.py +++ b/aerospike_helpers/__init__.py @@ -29,6 +29,7 @@ class HyperLogLog(bytes): ops = [ hll_operations.hll_init(BIN_NAME, index_bit_count=4, mh_bit_count=4) ] + keyTuple = ("test", "demo", 1) client.operate(keyTuple, ops) _, _, bins = client.get(keyTuple) print(bins[BIN_NAME]) From ae9e462bd7f281b746811289b106c51796846575 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 7 May 2026 19:31:37 -0700 Subject: [PATCH 085/127] Fix bad syntax in example --- aerospike_helpers/expressions/hll.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index a2c7ab9552..327d653d62 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -48,7 +48,7 @@ class HLLInit(_BaseExpr): import aerospike client = aerospike.client({'hosts': [('localhost', 3000)]}) ops = [ - expr = exp.HLLInit(None, 12, 24, exp.HLLBin("d")) + exp.HLLInit(None, 12, 24, exp.HLLBin("d")) ] key = ("test", "demo", 1) client.operate(key, ops) From 7966d2c3f14c0d131c95aa623d1c06999f4ded95 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 8 May 2026 07:57:09 -0700 Subject: [PATCH 086/127] Just noticed this is a duplicate code example for the same class --- aerospike_helpers/expressions/hll.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 327d653d62..bcb364573d 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -42,18 +42,6 @@ class HLLInit(_BaseExpr): If 1 of index_bit_count or mh_bit_count are set, an existing HLL bin will set that config and retain its current value for the unset config. If the HLL bin does not exist, index_bit_count is required to create it, mh_bit_count is optional. - - .. testsetup:: - - import aerospike - client = aerospike.client({'hosts': [('localhost', 3000)]}) - ops = [ - exp.HLLInit(None, 12, 24, exp.HLLBin("d")) - ] - key = ("test", "demo", 1) - client.operate(key, ops) - - values = [] """ _op = aerospike.OP_HLL_INIT From f7673264f8696e08938c50ae28cce89bf9bfe697 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 8 May 2026 08:15:51 -0700 Subject: [PATCH 087/127] Revise one code example to make more clear how this works --- aerospike_helpers/expressions/hll.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index bcb364573d..b9247e94c6 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -17,6 +17,14 @@ HyperLogLog expressions contain expressions for performing HLL operations. Most of these operations are equivalent to the :mod:`HyperLogLog API `. +# TODO: Run once for entire file +.. testsetup:: + + from aerospike_helpers.operations import hll_operations + + config = {"hosts": [("127.0.0.1", 3000)]} + client = aerospike.client(config) + key = ("test", "demo", 1) """ # from __future__ import annotations @@ -103,9 +111,27 @@ def __init__( .. testcode:: # Let HLL bin "d" have the following elements, ['key1', 'key2', 'key3'], index_bits 8, mh_bits 8. + ops = [ + hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("d", ['key1', 'key2', 'key3']) + ] + client.operate(key, ops) + + from aerospike_helpers.operations import expression_operations + from aerospike_helpers.operations import operations # Add ['key4', 'key5', 'key6'] so that the returned value is ['key1', 'key2', 'key3', 'key4', 'key5', # 'key6'] expr = exp.HLLAdd(None, ['key4', 'key5', 'key6'], 8, 8, exp.HLLBin("d")).compile() + ops = [ + expression_operations.expression_write("d", expr) + operations.read("d") + ] + _, _, bins = client.operate(key, ops) + print(bins["d"]) + + .. testoutput:: + + HyperLogLog(...) """ self._children = ( list, From cbe173efe5d388f1b787bbeaee4eeff660fc9825 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 8 May 2026 08:38:23 -0700 Subject: [PATCH 088/127] Fix syntax error --- aerospike_helpers/expressions/hll.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index b9247e94c6..ccad302018 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -123,7 +123,7 @@ def __init__( # 'key6'] expr = exp.HLLAdd(None, ['key4', 'key5', 'key6'], 8, 8, exp.HLLBin("d")).compile() ops = [ - expression_operations.expression_write("d", expr) + expression_operations.expression_write("d", expr), operations.read("d") ] _, _, bins = client.operate(key, ops) From bd60059b68763318166a5889e1c105d1393aa4d1 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 8 May 2026 08:43:37 -0700 Subject: [PATCH 089/127] Fix invalid syntax --- aerospike_helpers/operations/bitwise_operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/operations/bitwise_operations.py b/aerospike_helpers/operations/bitwise_operations.py index 23b7144c59..bd18a9cded 100644 --- a/aerospike_helpers/operations/bitwise_operations.py +++ b/aerospike_helpers/operations/bitwise_operations.py @@ -168,7 +168,7 @@ def bit_resize(bin_name: str, byte_size, policy=None, resize_flags: int = 0): byte_size (int): The new size of the bytes. policy (dict): The :ref:`bit_policy ` dictionary. default: None. resize_flags (int): :ref:`aerospike_bitwise_resize_flag` modifying the resize behavior - (default :py:data:`aerospike.BIT_RESIZE_DEFAULT`), such as py:data:`aerospike.BIT_RESIZE_GROW_ONLY` | + (default :py:data:`aerospike.BIT_RESIZE_DEFAULT`), such as :py:data:`aerospike.BIT_RESIZE_GROW_ONLY` | :py:data:`aerospike.BIT_RESIZE_FROM_FRONT`. Returns: From 25821d1fca639dc3b19c03d9b2d1182d7393ceab Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 09:32:14 -0700 Subject: [PATCH 090/127] Convert remaining python code examples --- doc/aerospike_helpers.expressions.rst | 4 +++- doc/client.rst | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/aerospike_helpers.expressions.rst b/doc/aerospike_helpers.expressions.rst index b32f11690f..fcdc4f165f 100644 --- a/doc/aerospike_helpers.expressions.rst +++ b/doc/aerospike_helpers.expressions.rst @@ -23,7 +23,9 @@ such as :meth:`~aerospike_helpers.expressions.base.Eq` or :meth:`~aerospike_help while passing them other expressions and constants as arguments, and finally calling the :meth:`~aerospike_helpers.expressions.resources._BaseExpr.compile` method. -Example:: +Example: + +.. testcode:: # See if integer bin "bin_name" contains a value equal to 10. from aerospike_helpers import expressions as exp diff --git a/doc/client.rst b/doc/client.rst index b8513e4859..cbb41215a6 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -2491,7 +2491,7 @@ Privilege Objects If not specified, the privilege applies to the entire namespace. - Example:: + .. testcode:: {'code': aerospike.PRIV_READ, 'ns': 'test', 'set': 'demo'} From 58fb349f79714ceeec36ad2acf894d81e1ce3428 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 09:38:21 -0700 Subject: [PATCH 091/127] Address doctest failure by checking dict in the order the keys are returned by the client --- doc/client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index cbb41215a6..c29b7d2dbf 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -612,7 +612,7 @@ User Defined Functions .. testoutput:: udf - Before remove: [{'content': bytearray(b'...'), 'hash': bytearray(b'...'), 'name': 'my_udf.lua', 'type': 0}] + Before remove: [{'name': 'my_udf.lua', 'hash': bytearray(b'...'), 'type': 0, 'content': bytearray(b'...')}] After remove: [] .. class:: Client From 7d6ad8e5a1e0466a0035108803eee6a3df03a1a4 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 09:49:26 -0700 Subject: [PATCH 092/127] Address indent error in code examples --- .../operations/expression_operations.py | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index ae4b55555f..86dff56441 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -49,24 +49,24 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r Example: - .. testcode:: + .. testcode:: - from aerospike_helpers.operations import expression_operations as expressions - from aerospike_helpers.expressions import * + from aerospike_helpers.operations import expression_operations as expressions + from aerospike_helpers.expressions import * - # Read the value of int bin "balance". - # Let 'client' be a connected aerospike client. - # Let int bin 'balance' == 50. - expr = IntBin("balance").compile() - ops = [ - expressions.expression_read("balance", expr) - ] - _, _, res = client.operate(self.key, ops) - print(res) + # Read the value of int bin "balance". + # Let 'client' be a connected aerospike client. + # Let int bin 'balance' == 50. + expr = IntBin("balance").compile() + ops = [ + expressions.expression_read("balance", expr) + ] + _, _, res = client.operate(self.key, ops) + print(res) - .. testoutput:: + .. testoutput:: - {"balance": 50} + {"balance": 50} """ @@ -95,26 +95,26 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ Example: - .. testcode:: + .. testcode:: - from aerospike_helpers.operations import expression_operations as expr_ops - from aerospike_helpers.expressions import * + from aerospike_helpers.operations import expression_operations as expr_ops + from aerospike_helpers.expressions import * - # Write the value of int bin "balance" + 50 back to "balance". - # Let 'client' be a connected aerospike client. - # Let int bin 'balance' == 50. + # Write the value of int bin "balance" + 50 back to "balance". + # Let 'client' be a connected aerospike client. + # Let int bin 'balance' == 50. - expr = Add(IntBin("balance"), 50).compile() - ops = [ - expr_ops.expression_write("balance", expr) - ] - client.operate(self.key, ops) - _, _, res = client.get(self.key) - print(res) + expr = Add(IntBin("balance"), 50).compile() + ops = [ + expr_ops.expression_write("balance", expr) + ] + client.operate(self.key, ops) + _, _, res = client.get(self.key) + print(res) - .. testoutput:: + .. testoutput:: - {"balance": 100} + {"balance": 100} """ From e4d0a554e9f33528e053d33e225e2ba616a421df Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 09:55:56 -0700 Subject: [PATCH 093/127] Address doctest failure --- aerospike_helpers/operations/expression_operations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index 86dff56441..c23867aeea 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -61,7 +61,7 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r ops = [ expressions.expression_read("balance", expr) ] - _, _, res = client.operate(self.key, ops) + _, _, res = client.operate(key, ops) print(res) .. testoutput:: @@ -108,9 +108,9 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ ops = [ expr_ops.expression_write("balance", expr) ] - client.operate(self.key, ops) + client.operate(key, ops) _, _, res = client.get(self.key) - print(res) + print(res) .. testoutput:: From d1effb901d89a19d8fc1f54257bd789f36e9f74e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 10:01:39 -0700 Subject: [PATCH 094/127] Fix formatting --- aerospike_helpers/operations/expression_operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index c23867aeea..8b5cb47f7f 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -66,7 +66,7 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r .. testoutput:: - {"balance": 50} + {"balance": 50} """ From e3ab81c68a3239bacff1ac7ddcf40e8879887673 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 10:04:15 -0700 Subject: [PATCH 095/127] Address doctest failures --- .../operations/expression_operations.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index 8b5cb47f7f..be5d128fe0 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -56,13 +56,14 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r # Read the value of int bin "balance". # Let 'client' be a connected aerospike client. - # Let int bin 'balance' == 50. + client.put(key, bins={'balance': 50}) + expr = IntBin("balance").compile() ops = [ expressions.expression_read("balance", expr) ] - _, _, res = client.operate(key, ops) - print(res) + _, _, bins = client.operate(key, ops) + print(bins) .. testoutput:: @@ -102,15 +103,15 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ # Write the value of int bin "balance" + 50 back to "balance". # Let 'client' be a connected aerospike client. - # Let int bin 'balance' == 50. + client.put(key, bins={'balance': 50}) expr = Add(IntBin("balance"), 50).compile() ops = [ expr_ops.expression_write("balance", expr) ] client.operate(key, ops) - _, _, res = client.get(self.key) - print(res) + _, _, bins = client.get(self.key) + print(bins) .. testoutput:: From 552305f7f8ff497b5a3001fb9c0f3dc6170353ba Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 10:18:50 -0700 Subject: [PATCH 096/127] Address test failure. Assuming doctest only returns strs as single quotes --- aerospike_helpers/operations/expression_operations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index be5d128fe0..1f046bb5d7 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -67,7 +67,7 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r .. testoutput:: - {"balance": 50} + {'balance': 50} """ @@ -110,12 +110,12 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ expr_ops.expression_write("balance", expr) ] client.operate(key, ops) - _, _, bins = client.get(self.key) + _, _, bins = client.get(key) print(bins) .. testoutput:: - {"balance": 100} + {'balance': 100} """ From 570c7710cb6e2e92a2b24a4f6a99c85aaad16528 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 10:24:56 -0700 Subject: [PATCH 097/127] Addr test failures --- aerospike_helpers/operations/expression_operations.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aerospike_helpers/operations/expression_operations.py b/aerospike_helpers/operations/expression_operations.py index 1f046bb5d7..b9f6ca218c 100644 --- a/aerospike_helpers/operations/expression_operations.py +++ b/aerospike_helpers/operations/expression_operations.py @@ -63,11 +63,11 @@ def expression_read(bin_name: str, expression: resources._BaseExpr, expression_r expressions.expression_read("balance", expr) ] _, _, bins = client.operate(key, ops) - print(bins) + print(bins['balance']) .. testoutput:: - {'balance': 50} + 50 """ @@ -111,11 +111,11 @@ def expression_write(bin_name: str, expression: resources._BaseExpr, expression_ ] client.operate(key, ops) _, _, bins = client.get(key) - print(bins) + print(bins['balance']) .. testoutput:: - {'balance': 100} + 100 """ From 70229912fe3cf3f803440c8a253d6860f3478d20 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 11:55:45 -0700 Subject: [PATCH 098/127] Add missing import --- aerospike_helpers/expressions/hll.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index ccad302018..0d037f41af 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -110,6 +110,8 @@ def __init__( .. testcode:: + from aerospike_helpers.operations import hll_operations + # Let HLL bin "d" have the following elements, ['key1', 'key2', 'key3'], index_bits 8, mh_bits 8. ops = [ hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), From 50103a37372d62fd09e1095ba734f2d5c02f8178 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 12:04:22 -0700 Subject: [PATCH 099/127] Fill in missing boilerplate code for HLL examples. --- aerospike_helpers/expressions/hll.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 0d037f41af..7c18248ba9 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -187,6 +187,17 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): .. testcode:: # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. + ops = [ + hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("d", ['key%s' % str(i) for i in range(10000)]), + hll_operations.hll_init("e", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("e", ['key%s' % str(i) for i in range(5000, 15000)]) + ] + client.operate(key, ops) + + _, _, bins = client.get(key) + values = [bins["e"]] + # Let values be a list containing HLL objects retrieved from the aerospike database. # Find the union of HLL bin "d" and all HLLs in values. expr = exp.HLLGetUnion(values, exp.HLLBin("d")).compile() @@ -215,6 +226,17 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing one HLL object with keys ['key%s' % str(i) for i in range(5000, 15000)]. + ops = [ + hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("d", ['key%s' % str(i) for i in range(10000)]), + hll_operations.hll_init("e", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("e", ['key%s' % str(i) for i in range(5000, 15000)]) + ] + client.operate(key, ops) + + _, _, bins = client.get(key) + values = [bins["e"]] + # Find the count of keys in the union of HLL bin "d" and all HLLs in values. (Should be around 15000) expr = exp.HLLGetUnionCount(values, exp.HLLBin("d")).compile() """ @@ -242,6 +264,17 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing one HLL object with keys ['key%s' % str(i) for i in range(5000, 15000)]. + ops = [ + hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("d", ['key%s' % str(i) for i in range(10000)]), + hll_operations.hll_init("e", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("e", ['key%s' % str(i) for i in range(5000, 15000)]) + ] + client.operate(key, ops) + + _, _, bins = client.get(key) + values = [bins["e"]] + # Find the count of keys in the intersection of HLL bin "d" and all HLLs in values. (Should be around 5000) expr = exp.HLLGetIntersectCount(values, exp.HLLBin("d")).compile() """ @@ -269,6 +302,17 @@ def __init__(self, values: "TypeValue", bin: "TypeBinName"): # Let HLLBin "d" contain keys ['key%s' % str(i) for i in range(10000)]. # Let values be a list containing one HLL object with keys ['key%s' % str(i) for i in range(5000, 15000)]. + ops = [ + hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("d", ['key%s' % str(i) for i in range(10000)]), + hll_operations.hll_init("e", index_bit_count=8, mh_bit_count=8), + hll_operations.hll_add("e", ['key%s' % str(i) for i in range(5000, 15000)]) + ] + client.operate(key, ops) + + _, _, bins = client.get(key) + values = [bins["e"]] + # Find the similarity the HLL in values to HLL bin "d". (Should be around 0.33) # Note that similarity is defined as intersect(A, B, ...) / union(A, B, ...). expr = exp.HLLGetSimilarity(values, exp.HLLBin("d")).compile() From dcb6f458395336a7e7b6eb0456efcc2f951c1429 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 12:11:10 -0700 Subject: [PATCH 100/127] testsetup block has key defined. Make sure it runs --- aerospike_helpers/expressions/hll.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 7c18248ba9..c69a51b248 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -17,7 +17,6 @@ HyperLogLog expressions contain expressions for performing HLL operations. Most of these operations are equivalent to the :mod:`HyperLogLog API `. -# TODO: Run once for entire file .. testsetup:: from aerospike_helpers.operations import hll_operations From 9e348a14d149629acf6f80966f1c0dd30a281cb0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 12:27:41 -0700 Subject: [PATCH 101/127] Revert code changes used for debugging failing doctest errors. --- src/main/client/operate.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/main/client/operate.c b/src/main/client/operate.c index 8b70d23af0..8cf97362e1 100644 --- a/src/main/client/operate.c +++ b/src/main/client/operate.c @@ -1480,22 +1480,8 @@ static as_status get_operation(as_error *err, PyObject *op_dict, "Operation must contain an \"op\" entry"); } if (!PyLong_Check(py_operation)) { - PyTypeObject *py_op_type = Py_TYPE(py_operation); - if (!py_op_type) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "Operation must be an integer, but got an " - "indeterminate type instead"); - } - const char *op_type_name = py_op_type->tp_name; - if (!op_type_name) { - return as_error_update(err, AEROSPIKE_ERR_PARAM, - "Operation must be an integer, but got an " - "indeterminate type instead"); - } return as_error_update(err, AEROSPIKE_ERR_PARAM, - "Operation must be an integer, but got a value " - "with type %s instead", - op_type_name); + "Operation must be an integer"); } *operation_ptr = PyLong_AsLong(py_operation); From 8c7b5500f7518483fd35ece86a6f01e2454d45da Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 12:38:38 -0700 Subject: [PATCH 102/127] Since testblock is hidden and there's no option to show it, have a duplicate code block that shows the testblock code. --- aerospike_helpers/expressions/hll.py | 15 +++++++++++++++ doc/aerospike_helpers.expressions.rst | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index c69a51b248..e3fb075d8d 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -19,11 +19,26 @@ .. testsetup:: + import aerospike + import aerospike_helpers.expressions as exp from aerospike_helpers.operations import hll_operations config = {"hosts": [("127.0.0.1", 3000)]} client = aerospike.client(config) key = ("test", "demo", 1) + +Assume all inline code examples run this beforehand: + +.. code-block:: Python + + import aerospike + import aerospike_helpers.expressions as exp + from aerospike_helpers.operations import hll_operations + + config = {"hosts": [("127.0.0.1", 3000)]} + client = aerospike.client(config) + key = ("test", "demo", 1) + """ # from __future__ import annotations diff --git a/doc/aerospike_helpers.expressions.rst b/doc/aerospike_helpers.expressions.rst index fcdc4f165f..48eb097167 100644 --- a/doc/aerospike_helpers.expressions.rst +++ b/doc/aerospike_helpers.expressions.rst @@ -166,6 +166,11 @@ Assume all in-line examples run this code beforehand: import aerospike import aerospike_helpers.expressions as exp +.. code-block:: Python + + import aerospike + import aerospike_helpers.expressions as exp + aerospike\_helpers\.expressions\.base module --------------------------------------------- From a758b7caf3e6bc40dfd8ff25fa4f0f0ae6f1dcd9 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 15 May 2026 12:43:17 -0700 Subject: [PATCH 103/127] Rm dup import. --- aerospike_helpers/expressions/hll.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index e3fb075d8d..bb35ab9908 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -124,8 +124,6 @@ def __init__( .. testcode:: - from aerospike_helpers.operations import hll_operations - # Let HLL bin "d" have the following elements, ['key1', 'key2', 'key3'], index_bits 8, mh_bits 8. ops = [ hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=8), From e9452d173e6f86f7e2fea0861aa8f56184780503 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:12:37 -0700 Subject: [PATCH 104/127] Use minimum supported python version for current sphinx version --- .github/workflows/smoke-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 71f40a36be..a5c2505c81 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -246,7 +246,7 @@ jobs: - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: ${{ env.LOWEST_SUPPORTED_PY_VERSION }} + python-version: 3.12 architecture: 'x64' - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 From 2688b0b7c710736255290cf7155c754b652ca7f3 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:15:54 -0700 Subject: [PATCH 105/127] Prevent either a failing stubtest or doctest run from affecting each other --- .github/workflows/smoke-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index a5c2505c81..752df3bf41 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -232,6 +232,7 @@ jobs: stubtest, doctest ] + fail-fast: false needs: build runs-on: ${{ needs.build.outputs.runner-os-used-for-build }} steps: From 2e9dcfd9543991ac12af81edaf607480c083a597 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:19:59 -0700 Subject: [PATCH 106/127] Use env var to determine python version to pull wheel and install python --- .github/workflows/smoke-tests.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 752df3bf41..b0407ad462 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -234,6 +234,8 @@ jobs: ] fail-fast: false needs: build + env: + SPHINX_MINIMUM_SUPPORTED_PYTHON_VERSION: 3.12 runs-on: ${{ needs.build.outputs.runner-os-used-for-build }} steps: - name: Harden the runner (Audit all outbound calls) @@ -247,12 +249,12 @@ jobs: - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: 3.12 + python-version: ${{ env.SPHINX_MINIMUM_SUPPORTED_PYTHON_VERSION }} architecture: 'x64' - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - pattern: ${{ vars.GH_ARTIFACT_NAME_PREFIX_FOR_BUILDS }}-${{ env.LOWEST_SUPPORTED_PY_VERSION }}-${{ env.PLATFORM_TAG }} + pattern: ${{ vars.GH_ARTIFACT_NAME_PREFIX_FOR_BUILDS }}-${{ env.SPHINX_MINIMUM_SUPPORTED_PYTHON_VERSION }}-${{ env.PLATFORM_TAG }} - name: Install client run: pip install ./*.whl From 54de454c9a60ac1f1060f1dd8ad86412af91bbe9 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:07:28 -0700 Subject: [PATCH 107/127] Address regression where doctest doesn't have a server to run against --- .github/workflows/smoke-tests.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index b0407ad462..1d97f59667 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -233,7 +233,10 @@ jobs: doctest ] fail-fast: false - needs: build + needs: [ + get-env-vars, + build + ] env: SPHINX_MINIMUM_SUPPORTED_PYTHON_VERSION: 3.12 runs-on: ${{ needs.build.outputs.runner-os-used-for-build }} @@ -259,6 +262,15 @@ jobs: - name: Install client run: pip install ./*.whl + - if: ${{ matrix.test == 'doctest' }} + uses: aerospike/shared-workflows/.github/actions/setup-aerospike-server@CLIENT-4793-setup-aerospike-server-use-named-volumes-instead-of-bind-mounts-to-support-windows-and-macos-runners + with: + num-nodes: 1 + oidc-provider: ${{ vars.OIDC_PROVIDER_NAME }} + oidc-audience: ${{ vars.OIDC_AUDIENCE }} + server-tag: ${{ needs.get-env-vars.outputs.server-tag }} + server-container-repo: database-docker-virtual/aerospike-server + - name: Install test dependencies if: ${{ matrix.test == 'doctest' }} run: | From 37a28c6a5735d07c8f0a8d96d45328744cb161b0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:16:52 -0700 Subject: [PATCH 108/127] Address doctest failures --- .github/workflows/smoke-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 1d97f59667..791bb706b5 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -270,6 +270,7 @@ jobs: oidc-audience: ${{ vars.OIDC_AUDIENCE }} server-tag: ${{ needs.get-env-vars.outputs.server-tag }} server-container-repo: database-docker-virtual/aerospike-server + config-file: ./.github/workflows/aerospike-ce.conf - name: Install test dependencies if: ${{ matrix.test == 'doctest' }} From bead2a725906a3b7e9c9acfe75563f345299a50e Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:37:20 -0700 Subject: [PATCH 109/127] Make HLLAdd code example more meaningful --- aerospike_helpers/expressions/hll.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index bb35ab9908..91d74cb6fb 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -136,16 +136,17 @@ def __init__( # Add ['key4', 'key5', 'key6'] so that the returned value is ['key1', 'key2', 'key3', 'key4', 'key5', # 'key6'] expr = exp.HLLAdd(None, ['key4', 'key5', 'key6'], 8, 8, exp.HLLBin("d")).compile() + expr_to_read = exp.HLLGetCount(exp.HLLBin("d")).compile() ops = [ expression_operations.expression_write("d", expr), - operations.read("d") + expression_operations.expression_read("d", expr_to_read) ] _, _, bins = client.operate(key, ops) print(bins["d"]) .. testoutput:: - HyperLogLog(...) + 6 """ self._children = ( list, From a468903cc892107feca34103c1136c31082c08e7 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:46:55 -0700 Subject: [PATCH 110/127] Provide more helpful output for HLLDescribe code example --- aerospike_helpers/expressions/hll.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 91d74cb6fb..3b125440d3 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -350,9 +350,24 @@ def __init__(self, bin: "TypeBinName"): Example: .. testcode:: + # Let HLL bin "d" have the following elements, ['key1', 'key2', 'key3'], index_bits 8, mh_bits 8. + ops = [ + hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=16), + hll_operations.hll_add("d", ['key1', 'key2', 'key3']) + ] + client.operate(key, ops) # Get description of HLL bin "d". expr = exp.HLLDescribe(exp.HLLBin("d")).compile() + ops = [ + expression_operations.expression_read("d", expr), + ] + _, _, bins = client.operate(key, ops) + print(bins["d"]) + + .. testoutput:: + + [8, 16] """ self._children = (bin if isinstance(bin, _BaseExpr) else HLLBin(bin),) From 40d752f740120466b0492584f33a820cf4143b0c Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:20:40 -0700 Subject: [PATCH 111/127] Address doctest failures by fixing the testcode syntax. --- aerospike_helpers/expressions/hll.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 3b125440d3..5238cde885 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -350,6 +350,7 @@ def __init__(self, bin: "TypeBinName"): Example: .. testcode:: + # Let HLL bin "d" have the following elements, ['key1', 'key2', 'key3'], index_bits 8, mh_bits 8. ops = [ hll_operations.hll_init("d", index_bit_count=8, mh_bit_count=16), From 9703b25acaac95b56795b35b7f1221bf1e8bc301 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:38:39 -0700 Subject: [PATCH 112/127] Use f-strings and a separate python script to reduce code duplication --- aerospike_helpers/expressions/hll.py | 29 ++++++++++++++-------------- doc/client.rst | 28 ++++++--------------------- 2 files changed, 21 insertions(+), 36 deletions(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 5238cde885..97df48fe57 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -19,27 +19,28 @@ .. testsetup:: - import aerospike - import aerospike_helpers.expressions as exp - from aerospike_helpers.operations import hll_operations - - config = {"hosts": [("127.0.0.1", 3000)]} - client = aerospike.client(config) - key = ("test", "demo", 1) + {testsetup} Assume all inline code examples run this beforehand: .. code-block:: Python - import aerospike - import aerospike_helpers.expressions as exp - from aerospike_helpers.operations import hll_operations + {testsetup} +""" + +if __doc__: + __doc__ = __doc__.format( + """ + import aerospike + import aerospike_helpers.expressions as exp + from aerospike_helpers.operations import hll_operations - config = {"hosts": [("127.0.0.1", 3000)]} - client = aerospike.client(config) - key = ("test", "demo", 1) + config = {"hosts": [("127.0.0.1", 3000)]} + client = aerospike.client(config) + key = ("test", "demo", 1) + """ + ) -""" # from __future__ import annotations from typing import List, Union, Dict, Any diff --git a/doc/client.rst b/doc/client.rst index 8c2c4ab1d3..e46ec530d9 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -28,32 +28,16 @@ Boilerplate Code For Examples Assume every in-line example runs this code beforehand: -.. warning:: - Only run example code on a brand new Aerospike server. This code deletes all records in the ``demo`` set! - - .. testsetup:: - # Imports - import aerospike - from aerospike import exception as ex - import sys + import runpy + runpy.run_path("client-boilerplate.py") - # Configure the client - config = { - 'hosts': [ ('127.0.0.1', 3000)] - } - - # Create a client and connect it to the cluster - try: - client = aerospike.client(config) - client.truncate('test', "demo", 0) - except ex.ClientError as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - sys.exit(1) +.. include:: client-boilerplate.py + :code: python - # Record key tuple: (namespace, set, key) - keyTuple = ('test', 'demo', 'key') +.. warning:: + Only run example code on a brand new Aerospike server. This code deletes all records in the ``demo`` set! Basic example: From 7fbc6827a1f0aa3fca90754e0a43a101981819e6 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:40:50 -0700 Subject: [PATCH 113/127] Fix python syntax --- aerospike_helpers/expressions/hll.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aerospike_helpers/expressions/hll.py b/aerospike_helpers/expressions/hll.py index 97df48fe57..c76d18c6cb 100644 --- a/aerospike_helpers/expressions/hll.py +++ b/aerospike_helpers/expressions/hll.py @@ -19,13 +19,13 @@ .. testsetup:: - {testsetup} + {0} Assume all inline code examples run this beforehand: .. code-block:: Python - {testsetup} + {0} """ if __doc__: From 44ab35640f88e0b5111318ae5e8cfc70f6070d69 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:43:33 -0700 Subject: [PATCH 114/127] Add missing boilerplate script --- doc/client-boilerplate.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 doc/client-boilerplate.py diff --git a/doc/client-boilerplate.py b/doc/client-boilerplate.py new file mode 100644 index 0000000000..74a7adff80 --- /dev/null +++ b/doc/client-boilerplate.py @@ -0,0 +1,20 @@ +# Imports +import aerospike +from aerospike import exception as ex +import sys + +# Configure the client +config = { + 'hosts': [ ('127.0.0.1', 3000)] +} + +# Create a client and connect it to the cluster +try: + client = aerospike.client(config) + client.truncate('test', "demo", 0) +except ex.ClientError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) + +# Record key tuple: (namespace, set, key) +keyTuple = ('test', 'demo', 'key') From 3eae3641ad8423e9f2583d3bc466af939292b2e2 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:53:59 -0700 Subject: [PATCH 115/127] Rerun doctest if the boilerplate testsetup code changes --- .github/workflows/smoke-tests.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 791bb706b5..3f710e1df8 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -12,16 +12,18 @@ on: # Using push instead of pull_request so that commits with no applicable code changes will not trigger a rebuild # This saves compute cost push: - paths-ignore: - # Only doc related changes - - doc/**/* - - .readthedocs.yml + paths: + # We run doctest in this workflow + - '!doc/**/*' + - 'doc/client-boilerplate.py' + # Only for building docs + - '!.readthedocs.yml' # Only for ignoring git commits when using 'git blame' - - .git-blame-ignore-revs + - '!.git-blame-ignore-revs' # Only used by QE for building their own wheels - - .build.yml + - '!.build.yml' # Other misc files not related to source code changes - - benchmarks/**/* + - '!benchmarks/**/*' tags-ignore: - '**' # Have to add this or tags-ignore will cause workflow to never run From e46eae2256b33111defca5c716d0a926a75ee712 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:56:30 -0700 Subject: [PATCH 116/127] Fix smoke tests not triggering. --- .github/workflows/smoke-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 3f710e1df8..287c31423d 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -12,7 +12,10 @@ on: # Using push instead of pull_request so that commits with no applicable code changes will not trigger a rebuild # This saves compute cost push: + # We have to use paths key to allow changes in the boilerplate code in doc/ to trigger doctest + # paths-ignore does not support ! operator paths: + - '**/*' # We run doctest in this workflow - '!doc/**/*' - 'doc/client-boilerplate.py' From 7104128513788d8665a6c6caac9d13398ecfb230 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:22:01 -0700 Subject: [PATCH 117/127] Revert "Add missing boilerplate script" This reverts commit 44ab35640f88e0b5111318ae5e8cfc70f6070d69. --- doc/client-boilerplate.py | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 doc/client-boilerplate.py diff --git a/doc/client-boilerplate.py b/doc/client-boilerplate.py deleted file mode 100644 index 74a7adff80..0000000000 --- a/doc/client-boilerplate.py +++ /dev/null @@ -1,20 +0,0 @@ -# Imports -import aerospike -from aerospike import exception as ex -import sys - -# Configure the client -config = { - 'hosts': [ ('127.0.0.1', 3000)] -} - -# Create a client and connect it to the cluster -try: - client = aerospike.client(config) - client.truncate('test', "demo", 0) -except ex.ClientError as e: - print("Error: {0} [{1}]".format(e.msg, e.code)) - sys.exit(1) - -# Record key tuple: (namespace, set, key) -keyTuple = ('test', 'demo', 'key') From 7f0c5a89f4169782de1fdebf9426c479c77c56a4 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:23:27 -0700 Subject: [PATCH 118/127] Revert "Fix smoke tests not triggering." This reverts commit e46eae2256b33111defca5c716d0a926a75ee712. --- .github/workflows/smoke-tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 287c31423d..3f710e1df8 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -12,10 +12,7 @@ on: # Using push instead of pull_request so that commits with no applicable code changes will not trigger a rebuild # This saves compute cost push: - # We have to use paths key to allow changes in the boilerplate code in doc/ to trigger doctest - # paths-ignore does not support ! operator paths: - - '**/*' # We run doctest in this workflow - '!doc/**/*' - 'doc/client-boilerplate.py' From 7b153cb322b057360a6a74fb0892a15bf7ab7537 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:23:34 -0700 Subject: [PATCH 119/127] Revert "Rerun doctest if the boilerplate testsetup code changes" This reverts commit 3eae3641ad8423e9f2583d3bc466af939292b2e2. --- .github/workflows/smoke-tests.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 3f710e1df8..791bb706b5 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -12,18 +12,16 @@ on: # Using push instead of pull_request so that commits with no applicable code changes will not trigger a rebuild # This saves compute cost push: - paths: - # We run doctest in this workflow - - '!doc/**/*' - - 'doc/client-boilerplate.py' - # Only for building docs - - '!.readthedocs.yml' + paths-ignore: + # Only doc related changes + - doc/**/* + - .readthedocs.yml # Only for ignoring git commits when using 'git blame' - - '!.git-blame-ignore-revs' + - .git-blame-ignore-revs # Only used by QE for building their own wheels - - '!.build.yml' + - .build.yml # Other misc files not related to source code changes - - '!benchmarks/**/*' + - benchmarks/**/* tags-ignore: - '**' # Have to add this or tags-ignore will cause workflow to never run From 82eba7dfb046ba06fd1428c3ca6df3a947237a0d Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:24:52 -0700 Subject: [PATCH 120/127] Always run doctest when docs/ has changes. --- .github/workflows/smoke-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 791bb706b5..58dbed3090 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -13,8 +13,8 @@ on: # This saves compute cost push: paths-ignore: - # Only doc related changes - - doc/**/* + # Changes to code examples in API docs may affect doctest results + # - doc/**/* - .readthedocs.yml # Only for ignoring git commits when using 'git blame' - .git-blame-ignore-revs From c3bd949ff7947a6e58d6aa172cdbd17b473787a1 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:27:34 -0700 Subject: [PATCH 121/127] Add back duplicated python setup code as a workaround for testsetup not being able to take in a script file --- doc/client.rst | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/doc/client.rst b/doc/client.rst index e46ec530d9..5c450f9ba7 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -28,13 +28,54 @@ Boilerplate Code For Examples Assume every in-line example runs this code beforehand: +.. Would prefer to have an external Python script to reduce code duplication here. +.. But there's no way to call a Python script for testsetup + .. testsetup:: - import runpy - runpy.run_path("client-boilerplate.py") + # Imports + import aerospike + from aerospike import exception as ex + import sys + + # Configure the client + config = { + 'hosts': [ ('127.0.0.1', 3000)] + } + + # Create a client and connect it to the cluster + try: + client = aerospike.client(config) + client.truncate('test', "demo", 0) + except ex.ClientError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) + + # Record key tuple: (namespace, set, key) + keyTuple = ('test', 'demo', 'key') + +.. code-block:: python + + # Imports + import aerospike + from aerospike import exception as ex + import sys + + # Configure the client + config = { + 'hosts': [ ('127.0.0.1', 3000)] + } + + # Create a client and connect it to the cluster + try: + client = aerospike.client(config) + client.truncate('test', "demo", 0) + except ex.ClientError as e: + print("Error: {0} [{1}]".format(e.msg, e.code)) + sys.exit(1) -.. include:: client-boilerplate.py - :code: python + # Record key tuple: (namespace, set, key) + keyTuple = ('test', 'demo', 'key') .. warning:: Only run example code on a brand new Aerospike server. This code deletes all records in the ``demo`` set! From 621ecaf2e2b38d00bc03963dd463ddf819913873 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:34:17 -0700 Subject: [PATCH 122/127] Lock setup-aerospike-server for doctest. --- .github/workflows/smoke-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 7f0a4c9ba5..5dc5da4935 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -263,7 +263,7 @@ jobs: run: pip install ./*.whl - if: ${{ matrix.test == 'doctest' }} - uses: aerospike/shared-workflows/.github/actions/setup-aerospike-server@CLIENT-4793-setup-aerospike-server-use-named-volumes-instead-of-bind-mounts-to-support-windows-and-macos-runners + uses: aerospike/shared-workflows/.github/actions/setup-aerospike-server@bd168dedaa4fc0b17a560541779d815540eded2d # v3.7.0 with: num-nodes: 1 oidc-provider: ${{ vars.OIDC_PROVIDER_NAME }} From 431ca2d55c8603b27dc1b214da29c2511f04f3a4 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:41:57 -0700 Subject: [PATCH 123/127] Dont think these comments matter anymore if the code example for client.udf_put() was removed --- doc/client.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/client.rst b/doc/client.rst index 5c450f9ba7..3a4806a910 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -657,10 +657,6 @@ User Defined Functions .. note:: To run this example, do not run the boilerplate code. - .. TODO - probably there is better syntax than using emphasize-lines with hardcoded numbers - - .. :emphasize-lines: 5,9 - .. method:: udf_remove(module[, policy: dict]) Remove a previously registered UDF module from the cluster. From e3cd0faee181b2e6bece08e4a4a685ff22aa3005 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:43:42 -0700 Subject: [PATCH 124/127] Remove old mock logic now that autodoc_mock_imports is used. --- doc/conf.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 94c23ce51d..eaacfc55cf 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -2,30 +2,14 @@ import sys, os -# try: -# from unittest.mock import MagicMock -# except ImportError: -# try: -# from mock import Mock as MagicMock -# except ImportError as e: -# print("mock is missing: pip install mock") -# raise e - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__name__), ".."))) -# Mock out aerospike, +# Mock out aerospike if it's not installed # see https://docs.readthedocs.io/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules - -# class Mock(MagicMock): -# @classmethod -# def __getattr__(cls, name): -# return MagicMock() - - try: import aerospike except ImportError: From fb0850f53ea26f9d0f54c07f76885aa430f1012b Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:57:43 -0700 Subject: [PATCH 125/127] Elaborate the format of client.info_all()'s output since the tested code example doesn't show the MAC address anymore. --- doc/client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index 3a4806a910..74d33054ed 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -822,7 +822,7 @@ Info Operations :param str command: see `Info Command Reference `_. :param dict policy: optional :ref:`aerospike_info_policies`. - :rtype: :class:`dict` + :rtype: :class:`dict` mapping the node name to a tuple with the second element being the info response string or :py:obj:`None`. :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. .. testcode:: From d4999e64d453d7bfe8bb2ae0b0d120097c8595be Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:32:01 -0700 Subject: [PATCH 126/127] Use testcode block instead of doctest. Doesn't make sense to print out the zero result codes from put and append. --- doc/client.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/client.rst b/doc/client.rst index 74d33054ed..2e18adf07f 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -481,14 +481,15 @@ String Operations :raises: a subclass of :exc:`~aerospike.exception.AerospikeError`. - .. doctest:: - - >>> client.put(keyTuple, {'bin1': 'Martin Luther King'}) - 0 - >>> client.append(keyTuple, 'bin1', ' jr.') - 0 - >>> (_, _, bins) = client.get(keyTuple) - >>> print(bins) + .. testcode:: + + client.put(keyTuple, {'bin1': 'Martin Luther King'}) + client.append(keyTuple, 'bin1', ' jr.') + (_, _, bins) = client.get(keyTuple) + print(bins) + + .. testoutput:: + {'bin1': 'Martin Luther King jr.'} .. method:: prepend(key, bin, val[, meta: dict[, policy: dict]]) From 9f96184a71cbf0a5eec4f9c93fb4c468aa75c4b0 Mon Sep 17 00:00:00 2001 From: Julian Nguyen <109386615+juliannguyen4@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:39:40 -0700 Subject: [PATCH 127/127] To make code review less confusing, always use :py:data: instead of :data: (there's no difference between the two) --- doc/client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/client.rst b/doc/client.rst index 68f4a7352b..f9285e80d1 100755 --- a/doc/client.rst +++ b/doc/client.rst @@ -2388,7 +2388,7 @@ Map Policies | Default: :data:`aerospike.MAP_WRITE_FLAGS_DEFAULT` | | Values should be or'd together: - | :data:`aerospike.LIST_WRITE_ADD_UNIQUE` | :data:`aerospike.LIST_WRITE_INSERT_BOUNDED` + | :py:data:`aerospike.LIST_WRITE_ADD_UNIQUE` | :py:data:`aerospike.LIST_WRITE_INSERT_BOUNDED` .. note:: This is only valid for Aerospike Server versions >= 4.3.0.