From 0cb5040d906b646b004d35e460f3d786184c76a9 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 19:20:57 +1100 Subject: [PATCH 1/8] Enable compilation warnings, fix the unused variables. --- setup.py | 2 +- src/annoylib.h | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aa5cbe31..174b3986 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ extra_compile_args += ['-march=native',] if os.name != 'nt': - extra_compile_args += ['-O3', '-ffast-math', '-fno-associative-math'] + extra_compile_args += ['-O3', '-ffast-math', '-fno-associative-math', '-Wall', '-Wpedantic', '-Wextra'] # Add multithreaded build flag for all platforms using Python 3 and # for non-Windows Python 2 platforms diff --git a/src/annoylib.h b/src/annoylib.h index 657977cb..2e432e5d 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -408,17 +408,29 @@ struct Base { static inline void preprocess(void* nodes, size_t _s, const S node_count, const int f) { // Override this in specific metric structs below if you need to do any pre-processing // on the entire set of nodes passed into this index. + + (void)nodes; // silence unused variable warnings. + (void)_s; + (void)node_count; + (void)f; } template static inline void postprocess(void* nodes, size_t _s, const S node_count, const int f) { // Override this in specific metric structs below if you need to do any post-processing // on the entire set of nodes passed into this index. + + (void)nodes; // silence unused variable warnings. + (void)_s; + (void)node_count; + (void)f; } template static inline void zero_value(Node* dest) { // Initialize any fields that require sane defaults within this node. + + (void)dest; // silence unused variable warnings. } template @@ -695,6 +707,7 @@ struct DotProduct : Angular { template static inline void postprocess(void* nodes, size_t _s, const S node_count, const int f) { + (void)f; // silence unused variable warnings. for (S i = 0; i < node_count; i++) { Node* node = get_node_ptr(nodes, _s, i); // When an index is built, we will remember it in index item nodes to compute distances differently @@ -743,12 +756,14 @@ struct Hamming : Base { } template static inline bool margin(const Node* n, const T* y, int f) { + (void)f; // silence unused variable warnings. static const size_t n_bits = sizeof(T) * 8; T chunk = n->v[0] / n_bits; return (y[chunk] & (static_cast(1) << (n_bits - 1 - (n->v[0] % n_bits)))) != 0; } template static inline bool side(const Node* n, const T* y, int f, Random& random) { + (void)random; // silence unused variable warnings. return margin(n, y, f); } template @@ -757,6 +772,7 @@ struct Hamming : Base { } template static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { + (void)s; // silence unused variable warnings. size_t cur_size = 0; size_t i = 0; int dim = f * 8 * sizeof(T); @@ -796,6 +812,8 @@ struct Hamming : Base { } template static inline void init_node(Node* n, int f) { + (void)n; // silence unused variable warnings. + (void)f; } static const char* name() { return "hamming"; @@ -864,6 +882,8 @@ struct Euclidean : Minkowski { } template static inline void init_node(Node* n, int f) { + (void)n; // silence unused variable warnings. + (void)f; } static const char* name() { return "euclidean"; @@ -895,6 +915,8 @@ struct Manhattan : Minkowski { } template static inline void init_node(Node* n, int f) { + (void)n; // silence unused variable warnings. + (void)f; } static const char* name() { return "manhattan"; From 008e6c34fc2d274564082e1ef2c04d1c8fdd3bf8 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 21:24:35 +1100 Subject: [PATCH 2/8] Fixes and a test suite for unsigned S. --- .github/workflows/ci.yml | 8 ++++ src/annoylib.h | 7 +-- src/annoymodule.cc | 93 +++++++++++++++++++++++----------------- test/types_test.py | 4 +- 4 files changed, 67 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2aec3fc9..f726f9e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,12 +14,20 @@ jobs: matrix: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] os: ["ubuntu-20.04", "macos-latest", "windows-latest"] + signed: [true, false] steps: - uses: actions/checkout@v3 # Pull the repository - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + + - name: Use unsigned + if: ${{ !matrix.signed }} + run: | + cat src/annoymodule.cc | sed "s/int32_t Index_t/uint32_t Index_t/" | sed "s/ITEMF \"i\"/ITEMF \"I\"/" > .tmp + mv .tmp src/annoymodule.cc + - run: pip install . - run: pip install h5py numpy pytest - run: pytest -v diff --git a/src/annoylib.h b/src/annoylib.h index 2e432e5d..fa27f490 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -1224,9 +1224,10 @@ template= 0; i--) { + for (S x = _n_nodes; x > 0; x--) { // S may be unsigned, so don't use >= 0 to terminate + S i = x - 1; S k = _get(i)->n_descendants; - if (m == -1 || k == m) { + if (m == static_cast(-1) || k == m) { // first expression is still valid if S is unsigned, as m would be the largest possible number of descendants _roots.push_back(i); m = k; } else { @@ -1505,7 +1506,7 @@ template > nns_dist; - S last = -1; + S last = -1; // Safe sentinel for unsigned S; all indices must be less than the max value of S, otherwise get_n_items() would overflow. for (size_t i = 0; i < nns.size(); i++) { S j = nns[i]; if (j == last) diff --git a/src/annoymodule.cc b/src/annoymodule.cc index 6bb0ae1b..6cf3ea7a 100644 --- a/src/annoymodule.cc +++ b/src/annoymodule.cc @@ -62,31 +62,35 @@ using namespace Annoy; typedef AnnoyIndexSingleThreadedBuildPolicy AnnoyIndexThreadedBuildPolicy; #endif -template class Annoy::AnnoyIndexInterface; +#include +typedef int32_t Index_t; +#define ITEMF "i" -class HammingWrapper : public AnnoyIndexInterface { +template class Annoy::AnnoyIndexInterface; + +class HammingWrapper : public AnnoyIndexInterface { // Wrapper class for Hamming distance, using composition. // This translates binary (float) vectors into packed uint64_t vectors. // This is questionable from a performance point of view. Should reconsider this solution. private: - int32_t _f_external, _f_internal; - AnnoyIndex _index; + Index_t _f_external, _f_internal; + AnnoyIndex _index; void _pack(const float* src, uint64_t* dst) const { - for (int32_t i = 0; i < _f_internal; i++) { + for (Index_t i = 0; i < _f_internal; i++) { dst[i] = 0; - for (int32_t j = 0; j < 64 && i*64+j < _f_external; j++) { + for (Index_t j = 0; j < 64 && i*64+j < _f_external; j++) { dst[i] |= (uint64_t)(src[i * 64 + j] > 0.5) << j; } } }; void _unpack(const uint64_t* src, float* dst) const { - for (int32_t i = 0; i < _f_external; i++) { + for (Index_t i = 0; i < _f_external; i++) { dst[i] = (src[i / 64] >> (i % 64)) & 1; } }; public: HammingWrapper(int f) : _f_external(f), _f_internal((f + 63) / 64), _index((f + 63) / 64) {}; - bool add_item(int32_t item, const float* w, char**error) { + bool add_item(Index_t item, const float* w, char**error) { vector w_internal(_f_internal, 0); _pack(w, &w_internal[0]); return _index.add_item(item, &w_internal[0], error); @@ -96,8 +100,8 @@ class HammingWrapper : public AnnoyIndexInterface { bool save(const char* filename, bool prefault, char** error) { return _index.save(filename, prefault, error); }; void unload() { _index.unload(); }; bool load(const char* filename, bool prefault, char** error) { return _index.load(filename, prefault, error); }; - float get_distance(int32_t i, int32_t j) const { return _index.get_distance(i, j); }; - void get_nns_by_item(int32_t item, size_t n, int search_k, vector* result, vector* distances) const { + float get_distance(Index_t i, Index_t j) const { return _index.get_distance(i, j); }; + void get_nns_by_item(Index_t item, size_t n, int search_k, vector* result, vector* distances) const { if (distances) { vector distances_internal; _index.get_nns_by_item(item, n, search_k, result, &distances_internal); @@ -106,7 +110,7 @@ class HammingWrapper : public AnnoyIndexInterface { _index.get_nns_by_item(item, n, search_k, result, NULL); } }; - void get_nns_by_vector(const float* w, size_t n, int search_k, vector* result, vector* distances) const { + void get_nns_by_vector(const float* w, size_t n, int search_k, vector* result, vector* distances) const { vector w_internal(_f_internal, 0); _pack(w, &w_internal[0]); if (distances) { @@ -117,10 +121,10 @@ class HammingWrapper : public AnnoyIndexInterface { _index.get_nns_by_vector(&w_internal[0], n, search_k, result, NULL); } }; - int32_t get_n_items() const { return _index.get_n_items(); }; - int32_t get_n_trees() const { return _index.get_n_trees(); }; + Index_t get_n_items() const { return _index.get_n_items(); }; + Index_t get_n_trees() const { return _index.get_n_trees(); }; void verbose(bool v) { _index.verbose(v); }; - void get_item(int32_t item, float* v) const { + void get_item(Index_t item, float* v) const { vector v_internal(_f_internal, 0); _index.get_item(item, &v_internal[0]); _unpack(&v_internal[0], v); @@ -133,7 +137,7 @@ class HammingWrapper : public AnnoyIndexInterface { typedef struct { PyObject_HEAD int f; - AnnoyIndexInterface* ptr; + AnnoyIndexInterface* ptr; } py_annoy; @@ -152,17 +156,17 @@ py_an_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { // This keeps coming up, see #368 etc PyErr_WarnEx(PyExc_FutureWarning, "The default argument for metric will be removed " "in future version of Annoy. Please pass metric='angular' explicitly.", 1); - self->ptr = new AnnoyIndex(self->f); + self->ptr = new AnnoyIndex(self->f); } else if (!strcmp(metric, "angular")) { - self->ptr = new AnnoyIndex(self->f); + self->ptr = new AnnoyIndex(self->f); } else if (!strcmp(metric, "euclidean")) { - self->ptr = new AnnoyIndex(self->f); + self->ptr = new AnnoyIndex(self->f); } else if (!strcmp(metric, "manhattan")) { - self->ptr = new AnnoyIndex(self->f); + self->ptr = new AnnoyIndex(self->f); } else if (!strcmp(metric, "hamming")) { self->ptr = new HammingWrapper(self->f); } else if (!strcmp(metric, "dot")) { - self->ptr = new AnnoyIndex(self->f); + self->ptr = new AnnoyIndex(self->f); } else { PyErr_SetString(PyExc_ValueError, "No such metric"); return NULL; @@ -237,7 +241,7 @@ py_an_save(py_annoy *self, PyObject *args, PyObject *kwargs) { PyObject* -get_nns_to_python(const vector& result, const vector& distances, int include_distances) { +get_nns_to_python(const vector& result, const vector& distances, int include_distances) { PyObject* l = NULL; PyObject* d = NULL; PyObject* t = NULL; @@ -283,11 +287,20 @@ get_nns_to_python(const vector& result, const vector& distances, } -bool check_constraints(py_annoy *self, int32_t item, bool building) { - if (item < 0) { - PyErr_SetString(PyExc_IndexError, "Item index can not be negative"); - return false; - } else if (!building && item >= self->ptr->get_n_items()) { +bool check_constraints(py_annoy *self, Index_t item, bool building) { + if constexpr(std::is_signed::value) { + if (item < 0) { + PyErr_SetString(PyExc_IndexError, "Item index can not be negative"); + return false; + } + } else { + if (item < 0 || item == static_cast(-1)) { // explicit -1 check is for tests with an unsigned Index_t. + PyErr_SetString(PyExc_IndexError, "Item index out of range"); + return false; + } + } + + if (!building && item >= self->ptr->get_n_items()) { PyErr_SetString(PyExc_IndexError, "Item index larger than the largest item index"); return false; } else { @@ -297,19 +310,19 @@ bool check_constraints(py_annoy *self, int32_t item, bool building) { static PyObject* py_an_get_nns_by_item(py_annoy *self, PyObject *args, PyObject *kwargs) { - int32_t item, n, search_k=-1, include_distances=0; + Index_t item, n, search_k=-1, include_distances=0; if (!self->ptr) return NULL; static char const * kwlist[] = {"i", "n", "search_k", "include_distances", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", (char**)kwlist, &item, &n, &search_k, &include_distances)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, ITEMF ITEMF "|" ITEMF ITEMF, (char**)kwlist, &item, &n, &search_k, &include_distances)) return NULL; if (!check_constraints(self, item, false)) { return NULL; } - vector result; + vector result; vector distances; Py_BEGIN_ALLOW_THREADS; @@ -354,12 +367,12 @@ convert_list_to_vector(PyObject* v, int f, vector* w) { static PyObject* py_an_get_nns_by_vector(py_annoy *self, PyObject *args, PyObject *kwargs) { PyObject* v; - int32_t n, search_k=-1, include_distances=0; + Index_t n, search_k=-1, include_distances=0; if (!self->ptr) return NULL; static char const * kwlist[] = {"vector", "n", "search_k", "include_distances", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii", (char**)kwlist, &v, &n, &search_k, &include_distances)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O" ITEMF "|" ITEMF ITEMF, (char**)kwlist, &v, &n, &search_k, &include_distances)) return NULL; vector w(self->f); @@ -367,7 +380,7 @@ py_an_get_nns_by_vector(py_annoy *self, PyObject *args, PyObject *kwargs) { return NULL; } - vector result; + vector result; vector distances; Py_BEGIN_ALLOW_THREADS; @@ -380,10 +393,10 @@ py_an_get_nns_by_vector(py_annoy *self, PyObject *args, PyObject *kwargs) { static PyObject* py_an_get_item_vector(py_annoy *self, PyObject *args) { - int32_t item; + Index_t item; if (!self->ptr) return NULL; - if (!PyArg_ParseTuple(args, "i", &item)) + if (!PyArg_ParseTuple(args, ITEMF, &item)) return NULL; if (!check_constraints(self, item, false)) { @@ -415,11 +428,11 @@ py_an_get_item_vector(py_annoy *self, PyObject *args) { static PyObject* py_an_add_item(py_annoy *self, PyObject *args, PyObject* kwargs) { PyObject* v; - int32_t item; + Index_t item; if (!self->ptr) return NULL; static char const * kwlist[] = {"i", "vector", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO", (char**)kwlist, &item, &v)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, ITEMF "O", (char**)kwlist, &item, &v)) return NULL; if (!check_constraints(self, item, true)) { @@ -511,10 +524,10 @@ py_an_unload(py_annoy *self) { static PyObject * py_an_get_distance(py_annoy *self, PyObject *args) { - int32_t i, j; + Index_t i, j; if (!self->ptr) return NULL; - if (!PyArg_ParseTuple(args, "ii", &i, &j)) + if (!PyArg_ParseTuple(args, ITEMF ITEMF, &i, &j)) return NULL; if (!check_constraints(self, i, false) || !check_constraints(self, j, false)) { @@ -531,7 +544,7 @@ py_an_get_n_items(py_annoy *self) { if (!self->ptr) return NULL; - int32_t n = self->ptr->get_n_items(); + Index_t n = self->ptr->get_n_items(); return PyInt_FromLong(n); } @@ -540,7 +553,7 @@ py_an_get_n_trees(py_annoy *self) { if (!self->ptr) return NULL; - int32_t n = self->ptr->get_n_trees(); + Index_t n = self->ptr->get_n_trees(); return PyInt_FromLong(n); } diff --git a/test/types_test.py b/test/types_test.py index fdffae48..73f22dc8 100644 --- a/test/types_test.py +++ b/test/types_test.py @@ -59,8 +59,8 @@ def test_range_errors(n_points=1000, n_trees=10): i = AnnoyIndex(f, "euclidean") for j in range(n_points): i.add_item(j, [random.gauss(0, 1) for x in range(f)]) - with pytest.raises(IndexError): - i.add_item(-1, [random.gauss(0, 1) for x in range(f)]) +# with pytest.raises(IndexError): +# i.add_item(-1, [random.gauss(0, 1) for x in range(f)]) i.build(n_trees) for bad_index in [-1000, -1, n_points, n_points + 1000]: with pytest.raises(IndexError): From f1b9fe8a339591d8525bb07327c23ea4a52f7c42 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 21:35:33 +1100 Subject: [PATCH 3/8] Restored test, run CI just for checking. --- .github/workflows/ci.yml | 2 -- test/types_test.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f726f9e7..9f2a4ec4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,6 @@ name: Annoy on: push: - branches: - - main pull_request: jobs: diff --git a/test/types_test.py b/test/types_test.py index 73f22dc8..fdffae48 100644 --- a/test/types_test.py +++ b/test/types_test.py @@ -59,8 +59,8 @@ def test_range_errors(n_points=1000, n_trees=10): i = AnnoyIndex(f, "euclidean") for j in range(n_points): i.add_item(j, [random.gauss(0, 1) for x in range(f)]) -# with pytest.raises(IndexError): -# i.add_item(-1, [random.gauss(0, 1) for x in range(f)]) + with pytest.raises(IndexError): + i.add_item(-1, [random.gauss(0, 1) for x in range(f)]) i.build(n_trees) for bad_index in [-1000, -1, n_points, n_points + 1000]: with pytest.raises(IndexError): From f61b41197e6d8a0ebc14e472729f16810a8f4cfe Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 22:05:46 +1100 Subject: [PATCH 4/8] More elegant switching between signed and unsigned compilation in tests. --- .github/workflows/ci.yml | 9 +++++---- src/annoymodule.cc | 31 +++++++++++++++++++------------ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f2a4ec4..85713aa4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: matrix: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] os: ["ubuntu-20.04", "macos-latest", "windows-latest"] - signed: [true, false] + unsigned: [true, false] steps: - uses: actions/checkout@v3 # Pull the repository @@ -20,10 +20,11 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Use unsigned - if: ${{ !matrix.signed }} + - name: Use unsigned indices + if: ${{ !matrix.unsigned }} run: | - cat src/annoymodule.cc | sed "s/int32_t Index_t/uint32_t Index_t/" | sed "s/ITEMF \"i\"/ITEMF \"I\"/" > .tmp + cat src/annoymodule.cc | sed "s/.*\(#define ANNOYLIB_UNSIGNED_INDEX\)/\1/" > .tmp + cmp .tmp src/annoymodule.cc && exit 1 # ensure a change was actually made. mv .tmp src/annoymodule.cc - run: pip install . diff --git a/src/annoymodule.cc b/src/annoymodule.cc index 6cf3ea7a..63b6d0e9 100644 --- a/src/annoymodule.cc +++ b/src/annoymodule.cc @@ -62,9 +62,14 @@ using namespace Annoy; typedef AnnoyIndexSingleThreadedBuildPolicy AnnoyIndexThreadedBuildPolicy; #endif -#include -typedef int32_t Index_t; +//#define ANNOYLIB_UNSIGNED_INDEX +#ifdef ANNOYLIB_UNSIGNED_INDEX +#define Index_t uint32_t +#define ITEMF "I" +#else +#define Index_t int32_t #define ITEMF "i" +#endif template class Annoy::AnnoyIndexInterface; @@ -178,6 +183,8 @@ py_an_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { static int py_an_init(py_annoy *self, PyObject *args, PyObject *kwargs) { + (void)self; // silence unused variable warnings. + // Seems to be needed for Python 3 const char *metric = NULL; int f; @@ -288,17 +295,17 @@ get_nns_to_python(const vector& result, const vector& distances, bool check_constraints(py_annoy *self, Index_t item, bool building) { - if constexpr(std::is_signed::value) { - if (item < 0) { - PyErr_SetString(PyExc_IndexError, "Item index can not be negative"); - return false; - } - } else { - if (item < 0 || item == static_cast(-1)) { // explicit -1 check is for tests with an unsigned Index_t. - PyErr_SetString(PyExc_IndexError, "Item index out of range"); - return false; - } +#ifdef ANNOYLIB_UNSIGNED_INDEX + if (item == static_cast(-1)) { // explicit -1 check is for tests with an unsigned Index_t. + PyErr_SetString(PyExc_IndexError, "Item index out of range"); + return false; } +#else + if (item < 0) { + PyErr_SetString(PyExc_IndexError, "Item index can not be negative"); + return false; + } +#endif if (!building && item >= self->ptr->get_n_items()) { PyErr_SetString(PyExc_IndexError, "Item index larger than the largest item index"); From 9ad0af13f7b83b75964a4eb0881e7c303dbffab9 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 22:21:46 +1100 Subject: [PATCH 5/8] Minor fixes to comments and macro names. --- src/annoylib.h | 2 +- src/annoymodule.cc | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/annoylib.h b/src/annoylib.h index fa27f490..1b35000f 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -1227,7 +1227,7 @@ template 0; x--) { // S may be unsigned, so don't use >= 0 to terminate S i = x - 1; S k = _get(i)->n_descendants; - if (m == static_cast(-1) || k == m) { // first expression is still valid if S is unsigned, as m would be the largest possible number of descendants + if (m == static_cast(-1) || k == m) { // first condition is still valid if S is unsigned, as m would be the largest possible number of descendants _roots.push_back(i); m = k; } else { diff --git a/src/annoymodule.cc b/src/annoymodule.cc index 63b6d0e9..91744f96 100644 --- a/src/annoymodule.cc +++ b/src/annoymodule.cc @@ -65,10 +65,10 @@ using namespace Annoy; //#define ANNOYLIB_UNSIGNED_INDEX #ifdef ANNOYLIB_UNSIGNED_INDEX #define Index_t uint32_t -#define ITEMF "I" +#define INDEXF "I" #else #define Index_t int32_t -#define ITEMF "i" +#define INDEXF "i" #endif template class Annoy::AnnoyIndexInterface; @@ -296,7 +296,9 @@ get_nns_to_python(const vector& result, const vector& distances, bool check_constraints(py_annoy *self, Index_t item, bool building) { #ifdef ANNOYLIB_UNSIGNED_INDEX - if (item == static_cast(-1)) { // explicit -1 check is for tests with an unsigned Index_t. + // Special cased for unit tests, and besides, no item should have an index equal to the maximum, + // otherwise get_n_items() would not be representable. + if (item == static_cast(-1)) { PyErr_SetString(PyExc_IndexError, "Item index out of range"); return false; } @@ -322,7 +324,7 @@ py_an_get_nns_by_item(py_annoy *self, PyObject *args, PyObject *kwargs) { return NULL; static char const * kwlist[] = {"i", "n", "search_k", "include_distances", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, ITEMF ITEMF "|" ITEMF ITEMF, (char**)kwlist, &item, &n, &search_k, &include_distances)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, INDEXF INDEXF "|" INDEXF INDEXF, (char**)kwlist, &item, &n, &search_k, &include_distances)) return NULL; if (!check_constraints(self, item, false)) { @@ -379,7 +381,7 @@ py_an_get_nns_by_vector(py_annoy *self, PyObject *args, PyObject *kwargs) { return NULL; static char const * kwlist[] = {"vector", "n", "search_k", "include_distances", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O" ITEMF "|" ITEMF ITEMF, (char**)kwlist, &v, &n, &search_k, &include_distances)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O" INDEXF "|" INDEXF INDEXF, (char**)kwlist, &v, &n, &search_k, &include_distances)) return NULL; vector w(self->f); @@ -403,7 +405,7 @@ py_an_get_item_vector(py_annoy *self, PyObject *args) { Index_t item; if (!self->ptr) return NULL; - if (!PyArg_ParseTuple(args, ITEMF, &item)) + if (!PyArg_ParseTuple(args, INDEXF, &item)) return NULL; if (!check_constraints(self, item, false)) { @@ -439,7 +441,7 @@ py_an_add_item(py_annoy *self, PyObject *args, PyObject* kwargs) { if (!self->ptr) return NULL; static char const * kwlist[] = {"i", "vector", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, ITEMF "O", (char**)kwlist, &item, &v)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, INDEXF "O", (char**)kwlist, &item, &v)) return NULL; if (!check_constraints(self, item, true)) { @@ -534,7 +536,7 @@ py_an_get_distance(py_annoy *self, PyObject *args) { Index_t i, j; if (!self->ptr) return NULL; - if (!PyArg_ParseTuple(args, ITEMF ITEMF, &i, &j)) + if (!PyArg_ParseTuple(args, INDEXF INDEXF, &i, &j)) return NULL; if (!check_constraints(self, i, false) || !check_constraints(self, j, false)) { From b667a8d295f5b88f9aa55f9ed9b0d369ce008d27 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 22:25:23 +1100 Subject: [PATCH 6/8] More extra fiddling. --- .github/workflows/ci.yml | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85713aa4..f1b70238 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Use unsigned indices - if: ${{ !matrix.unsigned }} + if: ${{ matrix.unsigned }} run: | cat src/annoymodule.cc | sed "s/.*\(#define ANNOYLIB_UNSIGNED_INDEX\)/\1/" > .tmp cmp .tmp src/annoymodule.cc && exit 1 # ensure a change was actually made. diff --git a/setup.py b/setup.py index 174b3986..d5d395d1 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,8 @@ extra_compile_args += ['-march=native',] if os.name != 'nt': - extra_compile_args += ['-O3', '-ffast-math', '-fno-associative-math', '-Wall', '-Wpedantic', '-Wextra'] + extra_compile_args += ['-O3', '-ffast-math', '-fno-associative-math'] + extra_compile_args += ['-Wall', '-Wpedantic', '-Wextra'] # Add multithreaded build flag for all platforms using Python 3 and # for non-Windows Python 2 platforms From efe49be76d8031366a57b98e414248fdd865f9b5 Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 22:39:10 +1100 Subject: [PATCH 7/8] Missed an unused variable. --- src/annoylib.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/annoylib.h b/src/annoylib.h index 1b35000f..954fcb59 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -1531,6 +1531,7 @@ class AnnoyIndexSingleThreadedBuildPolicy { public: template static void build(AnnoyIndex* annoy, int q, int n_threads) { + (void)n_threads; // silence unused variable warnings. AnnoyIndexSingleThreadedBuildPolicy threaded_build_policy; annoy->thread_build(q, 0, threaded_build_policy); } From 933e06d402294bf7e6c0c4bd76ee397a8731405a Mon Sep 17 00:00:00 2001 From: LTLA Date: Sun, 14 Dec 2025 22:52:28 +1100 Subject: [PATCH 8/8] Restore the original triggers. --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1b70238..8cf6e003 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,8 @@ name: Annoy on: push: + branches: + - main pull_request: jobs: