From 5f00f710c1b20030d93b727e6ddcbe011c5f18e3 Mon Sep 17 00:00:00 2001 From: phanirithvij Date: Wed, 25 Mar 2026 19:49:20 +0530 Subject: [PATCH 1/2] python3Packages.typesense: 1.1.1 -> 2.0.0 - trap kill typesense-server in preCheck to prevent infinite loop on test failure - disable test_snapshot_async which is flaky when building python31{3,4}.pkgs.typesense in parallel - use dynamic ports in tests to prevent parallel build collisions Co-Authored-By: Fanis Tharropoulos Signed-off-by: phanirithvij --- .../typesense/0001-linux-only-metrics.patch | 62 +++ ...h.patch => 0002-generated-temp-path.patch} | 23 +- .../0003-tests-fix-endpoint-path.patch | 106 +++++ .../typesense/0004-tests-fix-rule_id.patch | 29 ++ ...e-v30-collection-schema-expectations.patch | 385 ++++++++++++++++++ ...d-types-for-stem-and-synonyms-for-Ty.patch | 194 +++++++++ .../python-modules/typesense/default.nix | 71 +++- .../typesense/linux-only-metrics.patch | 48 --- 8 files changed, 843 insertions(+), 75 deletions(-) create mode 100644 pkgs/development/python-modules/typesense/0001-linux-only-metrics.patch rename pkgs/development/python-modules/typesense/{generated-temp-path.patch => 0002-generated-temp-path.patch} (56%) create mode 100644 pkgs/development/python-modules/typesense/0003-tests-fix-endpoint-path.patch create mode 100644 pkgs/development/python-modules/typesense/0004-tests-fix-rule_id.patch create mode 100644 pkgs/development/python-modules/typesense/0005-test-gate-v30-collection-schema-expectations.patch create mode 100644 pkgs/development/python-modules/typesense/0006-feat-curation-add-types-for-stem-and-synonyms-for-Ty.patch delete mode 100644 pkgs/development/python-modules/typesense/linux-only-metrics.patch diff --git a/pkgs/development/python-modules/typesense/0001-linux-only-metrics.patch b/pkgs/development/python-modules/typesense/0001-linux-only-metrics.patch new file mode 100644 index 000000000000..528a1e38d674 --- /dev/null +++ b/pkgs/development/python-modules/typesense/0001-linux-only-metrics.patch @@ -0,0 +1,62 @@ +From 24cb4ac0fcc6bfa88432c5c6a1b061f601458baf Mon Sep 17 00:00:00 2001 +From: phanirithvij +Date: Wed, 25 Mar 2026 18:30:32 +0530 +Subject: [PATCH 1/6] linux only metrics + +Signed-off-by: phanirithvij +--- + tests/metrics_test.py | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/tests/metrics_test.py b/tests/metrics_test.py +index 12fc300..6f2295d 100644 +--- a/tests/metrics_test.py ++++ b/tests/metrics_test.py +@@ -42,18 +42,22 @@ def test_init_async(fake_async_api_call: AsyncApiCall) -> None: + ) + assert metrics.resource_path == "/metrics.json" # noqa: WPS437 + ++import platform ++ + + def test_actual_retrieve(actual_metrics: Metrics) -> None: + """Test that the Metrics object can retrieve metrics on Typesense server and verify response structure.""" + response = actual_metrics.retrieve() + +- assert "system_cpu_active_percentage" in response ++ if platform.system() == "Linux": ++ assert "system_cpu_active_percentage" in response ++ assert "system_network_received_bytes" in response ++ assert "system_network_sent_bytes" in response ++ + assert "system_disk_total_bytes" in response + assert "system_disk_used_bytes" in response + assert "system_memory_total_bytes" in response + assert "system_memory_used_bytes" in response +- assert "system_network_received_bytes" in response +- assert "system_network_sent_bytes" in response + assert "typesense_memory_active_bytes" in response + assert "typesense_memory_allocated_bytes" in response + assert "typesense_memory_fragmentation_ratio" in response +@@ -68,13 +72,15 @@ async def test_actual_retrieve_async(actual_async_metrics: AsyncMetrics) -> None + """Test that the AsyncMetrics object can retrieve metrics on Typesense server and verify response structure.""" + response = await actual_async_metrics.retrieve() + +- assert "system_cpu_active_percentage" in response ++ if platform.system() == "Linux": ++ assert "system_cpu_active_percentage" in response ++ assert "system_network_received_bytes" in response ++ assert "system_network_sent_bytes" in response ++ + assert "system_disk_total_bytes" in response + assert "system_disk_used_bytes" in response + assert "system_memory_total_bytes" in response + assert "system_memory_used_bytes" in response +- assert "system_network_received_bytes" in response +- assert "system_network_sent_bytes" in response + assert "typesense_memory_active_bytes" in response + assert "typesense_memory_allocated_bytes" in response + assert "typesense_memory_fragmentation_ratio" in response +-- +2.53.0 + diff --git a/pkgs/development/python-modules/typesense/generated-temp-path.patch b/pkgs/development/python-modules/typesense/0002-generated-temp-path.patch similarity index 56% rename from pkgs/development/python-modules/typesense/generated-temp-path.patch rename to pkgs/development/python-modules/typesense/0002-generated-temp-path.patch index ddb15e2ea64d..902d4712efb6 100644 --- a/pkgs/development/python-modules/typesense/generated-temp-path.patch +++ b/pkgs/development/python-modules/typesense/0002-generated-temp-path.patch @@ -1,11 +1,21 @@ +From ed2f6f31f74fae21cb00b6ea3da92b300fadce76 Mon Sep 17 00:00:00 2001 +From: phanirithvij +Date: Wed, 25 Mar 2026 19:05:02 +0530 +Subject: [PATCH 2/6] generated temp path + +Signed-off-by: phanirithvij +--- + tests/operations_test.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + diff --git a/tests/operations_test.py b/tests/operations_test.py -index 34bb74c..39f9265 100644 +index b48b946..71c134e 100644 --- a/tests/operations_test.py +++ b/tests/operations_test.py -@@ -51,11 +51,11 @@ def test_cache_clear(actual_operations: Operations) -> None: +@@ -69,11 +69,11 @@ def test_cache_clear(actual_operations: Operations) -> None: assert response["success"] - - + + -def test_snapshot(actual_operations: Operations) -> None: +def test_snapshot(actual_operations: Operations, tmp_path) -> None: """Test that the Operations object can perform the snapshot operation.""" @@ -14,5 +24,8 @@ index 34bb74c..39f9265 100644 - {"snapshot_path": "/tmp"}, # noqa: S108 + {"snapshot_path": str(tmp_path)}, # noqa: S108 ) - + assert response["success"] +-- +2.53.0 + diff --git a/pkgs/development/python-modules/typesense/0003-tests-fix-endpoint-path.patch b/pkgs/development/python-modules/typesense/0003-tests-fix-endpoint-path.patch new file mode 100644 index 000000000000..42c8988f074f --- /dev/null +++ b/pkgs/development/python-modules/typesense/0003-tests-fix-endpoint-path.patch @@ -0,0 +1,106 @@ +From 90047673e4191ffaa6431059f9a8c1ffb31ea3cb Mon Sep 17 00:00:00 2001 +From: phanirithvij +Date: Wed, 25 Mar 2026 19:02:46 +0530 +Subject: [PATCH 3/6] tests fix endpoint path + +_endpoint_path is not a function anymore + +Signed-off-by: phanirithvij +--- + tests/override_test.py | 4 ++-- + tests/overrides_test.py | 4 ++-- + tests/synonym_test.py | 4 ++-- + tests/synonyms_test.py | 4 ++-- + 4 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/tests/override_test.py b/tests/override_test.py +index 89526c7..47fc81b 100644 +--- a/tests/override_test.py ++++ b/tests/override_test.py +@@ -47,7 +47,7 @@ def test_init(fake_api_call: ApiCall) -> None: + fake_api_call.config.nearest_node, + ) + assert ( +- override._endpoint_path() # noqa: WPS437 ++ override._endpoint_path # noqa: WPS437 + == "/collections/companies/overrides/company_override" + ) + +@@ -104,7 +104,7 @@ def test_init_async(fake_async_api_call: AsyncApiCall) -> None: + fake_async_api_call.config.nearest_node, + ) + assert ( +- override._endpoint_path() # noqa: WPS437 ++ override._endpoint_path # noqa: WPS437 + == "/collections/companies/overrides/company_override" + ) + +diff --git a/tests/overrides_test.py b/tests/overrides_test.py +index a9376b5..de4de73 100644 +--- a/tests/overrides_test.py ++++ b/tests/overrides_test.py +@@ -61,7 +61,7 @@ def test_get_missing_override(fake_overrides: Overrides) -> None: + ) + assert override.collection_name == "companies" + assert ( +- override._endpoint_path() # noqa: WPS437 ++ override._endpoint_path # noqa: WPS437 + == "/collections/companies/overrides/company_override" + ) + +@@ -186,7 +186,7 @@ def test_get_missing_override_async(fake_async_overrides) -> None: + ) + assert override.collection_name == "companies" + assert ( +- override._endpoint_path() # noqa: WPS437 ++ override._endpoint_path # noqa: WPS437 + == "/collections/companies/overrides/company_override" + ) + +diff --git a/tests/synonym_test.py b/tests/synonym_test.py +index cfe6e51..d9affd9 100644 +--- a/tests/synonym_test.py ++++ b/tests/synonym_test.py +@@ -45,7 +45,7 @@ def test_init(fake_api_call: ApiCall) -> None: + fake_api_call.config.nearest_node, + ) + assert ( +- synonym._endpoint_path() # noqa: WPS437 ++ synonym._endpoint_path # noqa: WPS437 + == "/collections/companies/synonyms/company_synonym" + ) + +@@ -99,7 +99,7 @@ def test_init_async(fake_async_api_call: AsyncApiCall) -> None: + fake_async_api_call.config.nearest_node, + ) + assert ( +- synonym._endpoint_path() # noqa: WPS437 ++ synonym._endpoint_path # noqa: WPS437 + == "/collections/companies/synonyms/company_synonym" + ) + +diff --git a/tests/synonyms_test.py b/tests/synonyms_test.py +index 3402493..afe8638 100644 +--- a/tests/synonyms_test.py ++++ b/tests/synonyms_test.py +@@ -62,7 +62,7 @@ def test_get_missing_synonym(fake_synonyms: Synonyms) -> None: + ) + assert synonym.collection_name == "companies" + assert ( +- synonym._endpoint_path() # noqa: WPS437 ++ synonym._endpoint_path # noqa: WPS437 + == "/collections/companies/synonyms/company_synonym" + ) + +@@ -174,7 +174,7 @@ def test_get_missing_synonym_async(fake_async_synonyms) -> None: + ) + assert synonym.collection_name == "companies" + assert ( +- synonym._endpoint_path() # noqa: WPS437 ++ synonym._endpoint_path # noqa: WPS437 + == "/collections/companies/synonyms/company_synonym" + ) + +-- +2.53.0 + diff --git a/pkgs/development/python-modules/typesense/0004-tests-fix-rule_id.patch b/pkgs/development/python-modules/typesense/0004-tests-fix-rule_id.patch new file mode 100644 index 000000000000..5e6f6fae4ddf --- /dev/null +++ b/pkgs/development/python-modules/typesense/0004-tests-fix-rule_id.patch @@ -0,0 +1,29 @@ +From f6989c8e29fcb03bf76f1b143d116f081091fad1 Mon Sep 17 00:00:00 2001 +From: phanirithvij +Date: Wed, 25 Mar 2026 19:35:38 +0530 +Subject: [PATCH 4/6] tests fix rule_id + +rule_id is not found for this class but rule_name exists with the +expected value + +Signed-off-by: phanirithvij +--- + tests/analytics_rules_v1_test.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/analytics_rules_v1_test.py b/tests/analytics_rules_v1_test.py +index d222d99..d1021d8 100644 +--- a/tests/analytics_rules_v1_test.py ++++ b/tests/analytics_rules_v1_test.py +@@ -45,7 +45,7 @@ def test_get_missing_analytics_rule(fake_analytics_rules: AnalyticsRulesV1) -> N + """Test that the AnalyticsRulesV1 object can get a missing analytics_rule.""" + analytics_rule = fake_analytics_rules["company_analytics_rule"] + +- assert analytics_rule.rule_id == "company_analytics_rule" ++ assert analytics_rule.rule_name == "company_analytics_rule" + assert_match_object(analytics_rule.api_call, fake_analytics_rules.api_call) + assert_object_lists_match( + analytics_rule.api_call.node_manager.nodes, +-- +2.53.0 + diff --git a/pkgs/development/python-modules/typesense/0005-test-gate-v30-collection-schema-expectations.patch b/pkgs/development/python-modules/typesense/0005-test-gate-v30-collection-schema-expectations.patch new file mode 100644 index 000000000000..ecaad48a362b --- /dev/null +++ b/pkgs/development/python-modules/typesense/0005-test-gate-v30-collection-schema-expectations.patch @@ -0,0 +1,385 @@ +From 6eecb90897548584b8907731abf981237e032997 Mon Sep 17 00:00:00 2001 +From: Fanis Tharropoulos +Date: Thu, 7 May 2026 18:59:56 +0300 +Subject: [PATCH 5/6] test: gate v30 collection schema expectations + +- keep v29.0 and earlier tests passing +--- + tests/collection_test.py | 31 +++-- + tests/collections_test.py | 242 +++++++++++++------------------------- + 2 files changed, 106 insertions(+), 167 deletions(-) + +diff --git a/tests/collection_test.py b/tests/collection_test.py +index 7eb29fa..30093d1 100644 +--- a/tests/collection_test.py ++++ b/tests/collection_test.py +@@ -5,12 +5,24 @@ from tests.utils.object_assertions import ( + assert_object_lists_match, + assert_to_contain_object, + ) ++from tests.utils.version import is_v30_or_above + from typesense.sync.api_call import ApiCall ++from typesense.sync.client import Client + from typesense.sync.collection import Collection + from typesense.sync.collections import Collections + from typesense.types.collection import CollectionSchema + + ++is_v30_or_above_server = is_v30_or_above( ++ Client( ++ { ++ "api_key": "xyz", ++ "nodes": [{"host": "localhost", "port": 8108, "protocol": "http"}], ++ } ++ ) ++) ++ ++ + def test_init(fake_api_call: ApiCall) -> None: + """Test that the Collection object is initialized correctly.""" + collection = Collection(fake_api_call, "companies") +@@ -52,7 +64,6 @@ def test_actual_retrieve( + "infix": False, + "stem": False, + "stem_dictionary": "", +- "truncate_len": 100, + "store": True, + }, + { +@@ -66,7 +77,6 @@ def test_actual_retrieve( + "infix": False, + "stem": False, + "stem_dictionary": "", +- "truncate_len": 100, + "store": True, + }, + ], +@@ -74,9 +84,12 @@ def test_actual_retrieve( + "num_documents": 0, + "symbols_to_index": [], + "token_separators": [], +- "synonym_sets": [], +- "curation_sets": [], + } ++ if is_v30_or_above_server: ++ expected["synonym_sets"] = [] ++ expected["curation_sets"] = [] ++ expected["fields"][0]["truncate_len"] = 100 ++ expected["fields"][1]["truncate_len"] = 100 + + response.pop("created_at") + +@@ -93,10 +106,8 @@ def test_actual_update( + {"fields": [{"name": "num_locations", "type": "int32"}]}, + ) + +- expected: CollectionSchema = { +- "fields": [ +- {"name": "num_locations", "truncate_len": 100, "type": "int32"}, +- ], +- } ++ expected_field = {"name": "num_locations", "type": "int32"} ++ if is_v30_or_above_server: ++ expected_field["truncate_len"] = 100 + +- assert_to_contain_object(response.get("fields")[0], expected.get("fields")[0]) ++ assert_to_contain_object(response.get("fields")[0], expected_field) +diff --git a/tests/collections_test.py b/tests/collections_test.py +index 788e3dc..94e3c60 100644 +--- a/tests/collections_test.py ++++ b/tests/collections_test.py +@@ -1,6 +1,5 @@ + """Tests for the Collections class.""" + +- + import sys + + from typesense.async_.api_call import AsyncApiCall +@@ -13,11 +12,68 @@ else: + + from tests.utils.object_assertions import assert_match_object, assert_object_lists_match + from typesense.sync.api_call import ApiCall ++from tests.utils.version import is_v30_or_above + from typesense.sync.collections import Collections + from typesense.async_.collections import AsyncCollections ++from typesense.sync.client import Client + from typesense.types.collection import CollectionSchema + + ++IS_V30_OR_ABOVE = is_v30_or_above( ++ Client( ++ { ++ "api_key": "xyz", ++ "nodes": [{"host": "localhost", "port": 8108, "protocol": "http"}], ++ } ++ ) ++) ++ ++ ++def expected_collection_field( ++ name: str, ++ type_: str, ++ *, ++ sort: bool, ++) -> dict[str, typing.Any]: ++ field: dict[str, typing.Any] = { ++ "name": name, ++ "type": type_, ++ "facet": False, ++ "index": True, ++ "optional": False, ++ "locale": "", ++ "sort": sort, ++ "infix": False, ++ "stem": False, ++ "stem_dictionary": "", ++ "store": True, ++ } ++ if IS_V30_OR_ABOVE: ++ field["truncate_len"] = 100 ++ return field ++ ++ ++def expected_collection_schema( ++ *, ++ default_sorting_field: str, ++ fields: typing.List[dict[str, typing.Any]], ++ name: str, ++) -> CollectionSchema: ++ expected: CollectionSchema = { ++ "default_sorting_field": default_sorting_field, ++ "enable_nested_fields": False, ++ "fields": fields, ++ "name": name, ++ "num_documents": 0, ++ "symbols_to_index": [], ++ "token_separators": [], ++ } ++ if IS_V30_OR_ABOVE: ++ expected["synonym_sets"] = [] ++ expected["curation_sets"] = [] ++ return expected ++ ++ + def test_init(fake_api_call: ApiCall) -> None: + """Test that the Collections object is initialized correctly.""" + collections = Collections(fake_api_call) +@@ -98,46 +154,14 @@ def test_get_existing_collection(fake_collections: Collections) -> None: + + def test_actual_create(actual_collections: Collections, delete_all: None) -> None: + """Test that the Collections object can create a collection on Typesense Server.""" +- expected: CollectionSchema = { +- "default_sorting_field": "", +- "enable_nested_fields": False, +- "fields": [ +- { +- "name": "company_name", +- "type": "string", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": False, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, +- { +- "name": "num_employees", +- "type": "int32", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": False, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, ++ expected = expected_collection_schema( ++ default_sorting_field="", ++ fields=[ ++ expected_collection_field("company_name", "string", sort=False), ++ expected_collection_field("num_employees", "int32", sort=False), + ], +- "name": "companies", +- "num_documents": 0, +- "symbols_to_index": [], +- "token_separators": [], +- "synonym_sets": [], +- "curation_sets": [], +- } ++ name="companies", ++ ) + + response = actual_collections.create( + { +@@ -170,46 +194,14 @@ def test_actual_retrieve( + response = actual_collections.retrieve() + + expected: typing.List[CollectionSchema] = [ +- { +- "default_sorting_field": "num_employees", +- "enable_nested_fields": False, +- "fields": [ +- { +- "name": "company_name", +- "type": "string", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": False, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, +- { +- "name": "num_employees", +- "type": "int32", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": True, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, ++ expected_collection_schema( ++ default_sorting_field="num_employees", ++ fields=[ ++ expected_collection_field("company_name", "string", sort=False), ++ expected_collection_field("num_employees", "int32", sort=True), + ], +- "name": "companies", +- "num_documents": 0, +- "symbols_to_index": [], +- "token_separators": [], +- "synonym_sets": [], +- "curation_sets": [], +- }, ++ name="companies", ++ ), + ] + + response[0].pop("created_at") +@@ -235,46 +227,14 @@ async def test_actual_create_async( + actual_async_collections: AsyncCollections, delete_all: None + ) -> None: + """Test that the Collections object can create a collection on Typesense Server.""" +- expected: CollectionSchema = { +- "default_sorting_field": "", +- "enable_nested_fields": False, +- "fields": [ +- { +- "name": "company_name", +- "type": "string", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": False, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, +- { +- "name": "num_employees", +- "type": "int32", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": False, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, ++ expected = expected_collection_schema( ++ default_sorting_field="", ++ fields=[ ++ expected_collection_field("company_name", "string", sort=False), ++ expected_collection_field("num_employees", "int32", sort=False), + ], +- "name": "companies", +- "num_documents": 0, +- "symbols_to_index": [], +- "token_separators": [], +- "synonym_sets": [], +- "curation_sets": [], +- } ++ name="companies", ++ ) + + response = await actual_async_collections.create( + { +@@ -307,46 +267,14 @@ async def test_actual_retrieve_async( + response = await actual_async_collections.retrieve() + + expected: typing.List[CollectionSchema] = [ +- { +- "default_sorting_field": "num_employees", +- "enable_nested_fields": False, +- "fields": [ +- { +- "name": "company_name", +- "type": "string", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": False, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, +- { +- "name": "num_employees", +- "type": "int32", +- "facet": False, +- "index": True, +- "optional": False, +- "locale": "", +- "sort": True, +- "infix": False, +- "stem": False, +- "stem_dictionary": "", +- "truncate_len": 100, +- "store": True, +- }, ++ expected_collection_schema( ++ default_sorting_field="num_employees", ++ fields=[ ++ expected_collection_field("company_name", "string", sort=False), ++ expected_collection_field("num_employees", "int32", sort=True), + ], +- "name": "companies", +- "num_documents": 0, +- "symbols_to_index": [], +- "token_separators": [], +- "synonym_sets": [], +- "curation_sets": [], +- }, ++ name="companies", ++ ), + ] + + response[0].pop("created_at") +-- +2.53.0 + diff --git a/pkgs/development/python-modules/typesense/0006-feat-curation-add-types-for-stem-and-synonyms-for-Ty.patch b/pkgs/development/python-modules/typesense/0006-feat-curation-add-types-for-stem-and-synonyms-for-Ty.patch new file mode 100644 index 000000000000..dff3a3ccb3e6 --- /dev/null +++ b/pkgs/development/python-modules/typesense/0006-feat-curation-add-types-for-stem-and-synonyms-for-Ty.patch @@ -0,0 +1,194 @@ +From a5f8ce27a246a1c0a3dd5236cffa0afeaaf166b5 Mon Sep 17 00:00:00 2001 +From: Fanis Tharropoulos +Date: Thu, 7 May 2026 19:26:14 +0300 +Subject: [PATCH 6/6] feat(curation): add types for `stem` and `synonyms` for + Typesense v30.2 + +--- + src/typesense/types/curation_set.py | 56 ++++++++++++++++++++++++++--- + tests/curation_set_test.py | 13 ++++++- + tests/curation_sets_test.py | 4 +++ + 3 files changed, 67 insertions(+), 6 deletions(-) + +diff --git a/src/typesense/types/curation_set.py b/src/typesense/types/curation_set.py +index 6468166..f99a020 100644 +--- a/src/typesense/types/curation_set.py ++++ b/src/typesense/types/curation_set.py +@@ -33,15 +33,36 @@ class CurationRuleTagsSchema(typing.TypedDict): + tags: typing.List[str] + + +-class CurationRuleQuerySchema(typing.TypedDict): ++class CurationRuleQueryBaseSchema(typing.TypedDict): + """ +- Schema for a curation rule using query and match. ++ Schema for the base of a curation rule using query and match. + """ + + query: str + match: typing.Literal["exact", "contains"] + + ++class CurationRuleQueryCreateSchema(CurationRuleQueryBaseSchema): ++ """ ++ Schema for creating a curation rule using query and match. ++ """ ++ ++ stem: typing.NotRequired[bool] ++ synonyms: typing.NotRequired[bool] ++ ++ ++class CurationRuleQuerySchema(CurationRuleQueryBaseSchema): ++ """ ++ Schema for retrieving a curation rule using query and match. ++ """ ++ ++ stem: typing.NotRequired[ ++ bool ++ ] # TODO: This is a bug in Typesense server upstream, so until 31.0 is ++ # released, this is staying as notrequired. ++ synonyms: typing.NotRequired[bool] ++ ++ + class CurationRuleFilterBySchema(typing.TypedDict): + """ + Schema for a curation rule using filter_by. +@@ -85,20 +106,45 @@ class CurationItemSchema(typing.TypedDict): + metadata: typing.NotRequired[typing.Dict[str, typing.Any]] + + ++class CurationItemUpsertSchema(typing.TypedDict): ++ """ ++ Schema for creating or updating a single curation item. ++ """ ++ ++ id: str ++ rule: typing.Union[ ++ CurationRuleTagsSchema, ++ CurationRuleQueryCreateSchema, ++ CurationRuleFilterBySchema, ++ ] ++ includes: typing.NotRequired[typing.List[CurationIncludeSchema]] ++ excludes: typing.NotRequired[typing.List[CurationExcludeSchema]] ++ filter_by: typing.NotRequired[str] ++ sort_by: typing.NotRequired[str] ++ replace_query: typing.NotRequired[str] ++ remove_matched_tokens: typing.NotRequired[bool] ++ filter_curated_hits: typing.NotRequired[bool] ++ stop_processing: typing.NotRequired[bool] ++ effective_from_ts: typing.NotRequired[int] ++ effective_to_ts: typing.NotRequired[int] ++ metadata: typing.NotRequired[typing.Dict[str, typing.Any]] ++ ++ + class CurationSetUpsertSchema(typing.TypedDict): + """ + Payload schema to create or replace a curation set. + """ + +- items: typing.List[CurationItemSchema] ++ items: typing.List[CurationItemUpsertSchema] + + +-class CurationSetSchema(CurationSetUpsertSchema, total=False): ++class CurationSetSchema(typing.TypedDict): + """ + Response schema for a curation set. + """ + +- name: typing.NotRequired[str] ++ items: typing.List[CurationItemSchema] ++ name: str + + + class CurationSetsListEntrySchema(typing.TypedDict): +diff --git a/tests/curation_set_test.py b/tests/curation_set_test.py +index a75196a..fe07278 100644 +--- a/tests/curation_set_test.py ++++ b/tests/curation_set_test.py +@@ -1,6 +1,5 @@ + """Tests for the CurationSet class including items APIs.""" + +- + import pytest + + from tests.utils.version import is_v30_or_above +@@ -62,6 +61,8 @@ def test_actual_retrieve( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + }, +@@ -108,6 +109,8 @@ def test_actual_list_items( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + }, +@@ -140,6 +143,8 @@ def test_actual_get_item( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + } +@@ -218,6 +223,8 @@ async def test_actual_retrieve_async( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + }, +@@ -263,6 +270,8 @@ async def test_actual_list_items_async( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + }, +@@ -295,6 +304,8 @@ async def test_actual_get_item_async( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + } +diff --git a/tests/curation_sets_test.py b/tests/curation_sets_test.py +index 0a06a05..e4a635a 100644 +--- a/tests/curation_sets_test.py ++++ b/tests/curation_sets_test.py +@@ -87,6 +87,8 @@ def test_actual_upsert( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + }, +@@ -150,6 +152,8 @@ async def test_actual_upsert_async( + "rule": { + "match": "contains", + "query": "shoe", ++ "stem": False, ++ "synonyms": False, + }, + "stop_processing": True, + }, +-- +2.53.0 + diff --git a/pkgs/development/python-modules/typesense/default.nix b/pkgs/development/python-modules/typesense/default.nix index 78441621aa8d..ebcb8f64419a 100644 --- a/pkgs/development/python-modules/typesense/default.nix +++ b/pkgs/development/python-modules/typesense/default.nix @@ -7,29 +7,38 @@ pytestCheckHook, typesense, curl, - pytest-mock, - requests-mock, - python-dotenv, faker, + httpx, isort, + pytest-asyncio, + pytest-httpx, + pytest-mock, + python-dotenv, + requests-mock, + respx, }: -buildPythonPackage rec { +buildPythonPackage (finalAttrs: { pname = "typesense"; - version = "1.1.1"; + version = "2.0.0"; pyproject = true; src = fetchFromGitHub { owner = "typesense"; repo = "typesense-python"; - tag = "v${version}"; - hash = "sha256-vo9DW4kinb00zWW4yX8ibyelQxW3eVabn+oMddPEd18="; + tag = "v${finalAttrs.version}"; + hash = "sha256-GzapEl26FS6yMGeLC54y9ysl0mt9l6ceYHr84E6BqBo="; }; patches = [ - # See . - ./linux-only-metrics.patch - ./generated-temp-path.patch + # See . + # See . + ./0001-linux-only-metrics.patch + ./0002-generated-temp-path.patch + ./0003-tests-fix-endpoint-path.patch + ./0004-tests-fix-rule_id.patch + ./0005-test-gate-v30-collection-schema-expectations.patch + ./0006-feat-curation-add-types-for-stem-and-synonyms-for-Ty.patch ]; build-system = [ setuptools ]; @@ -40,31 +49,47 @@ buildPythonPackage rec { pytestCheckHook typesense curl - pytest-mock - requests-mock - python-dotenv faker + httpx isort + pytest-asyncio + pytest-httpx + pytest-mock + python-dotenv + requests-mock + respx ]; disabledTestMarks = [ "open_ai" ]; - disabledTests = [ "import_typing_extensions" ]; + disabledTests = [ + "import_typing_extensions" + # tests/operations_test.py::test_snapshot_async fails when tests are run + # in parallel due to writing to the shared /tmp directory on macOS + "test_snapshot_async" + ]; __darwinAllowLocalNetworking = true; + # For darwin, the nix sandbox doesn't isolate loopback interfaces like linux does. + # So nixpkgs-review builds of python3.x will collide on the hardcoded port 8108. + # So for tests patch it to use random port. preCheck = '' + export TYPESENSE_PORT=$(( 10000 + $$ % 30000 )) + export PEERING_PORT=$(( TYPESENSE_PORT - 1 )) + find tests -type f -exec sed -i "s/8108/$TYPESENSE_PORT/g" {} + + TYPESENSE_API_KEY="xyz" \ TYPESENSE_DATA_DIR="$(mktemp -d)" \ - typesense-server & + typesense-server --api-port=$TYPESENSE_PORT --peering-port=$PEERING_PORT & typesense_pid=$! + # Ensure typesense-server is killed even if tests fail + trap "kill $typesense_pid" EXIT + # Wait for typesense to finish starting. - timeout 20 bash -c ' - while ! curl -s --fail localhost:8108/health; do sleep 1; done - ' || false - ''; - postCheck = '' - kill $typesense_pid + timeout 20 bash -c " + while ! curl -s --fail localhost:$TYPESENSE_PORT/health; do sleep 1; done + " || false ''; pythonImportsCheck = [ "typesense" ]; @@ -74,5 +99,7 @@ buildPythonPackage rec { homepage = "https://github.com/typesense/typesense-python"; license = lib.licenses.asl20; teams = [ lib.teams.ngi ]; + # on x86_64-darwin the typesense server doesn't start + badPlatforms = [ "x86_64-darwin" ]; }; -} +}) diff --git a/pkgs/development/python-modules/typesense/linux-only-metrics.patch b/pkgs/development/python-modules/typesense/linux-only-metrics.patch deleted file mode 100644 index ac667a3e42f1..000000000000 --- a/pkgs/development/python-modules/typesense/linux-only-metrics.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 9f0024ae3a0377d15788256c72af7a692d250afc Mon Sep 17 00:00:00 2001 -From: dotlambda -Date: Thu, 23 Oct 2025 13:40:33 -0700 -Subject: [PATCH] test(metrics): some metrics only exists on Linux - ---- - tests/metrics_test.py | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - -diff --git a/tests/metrics_test.py b/tests/metrics_test.py -index 1e1ea47..fecf3c0 100644 ---- a/tests/metrics_test.py -+++ b/tests/metrics_test.py -@@ -2,6 +2,8 @@ - - from __future__ import annotations - -+import platform -+ - from typesense.metrics import Metrics - - -@@ -9,13 +11,15 @@ def test_actual_retrieve(actual_metrics: Metrics) -> None: - """Test that the Debug object can retrieve a debug on Typesense server and verify response structure.""" - response = actual_metrics.retrieve() - -- assert "system_cpu_active_percentage" in response -+ if platform.system() == "Linux": -+ assert "system_cpu_active_percentage" in response -+ assert "system_network_received_bytes" in response -+ assert "system_network_sent_bytes" in response -+ - assert "system_disk_total_bytes" in response - assert "system_disk_used_bytes" in response - assert "system_memory_total_bytes" in response - assert "system_memory_used_bytes" in response -- assert "system_network_received_bytes" in response -- assert "system_network_sent_bytes" in response - assert "typesense_memory_active_bytes" in response - assert "typesense_memory_allocated_bytes" in response - assert "typesense_memory_fragmentation_ratio" in response -@@ -23,4 +27,4 @@ def test_actual_retrieve(actual_metrics: Metrics) -> None: - assert "typesense_memory_mapped_bytes" in response - assert "typesense_memory_metadata_bytes" in response - assert "typesense_memory_resident_bytes" in response -- assert "typesense_memory_retained_bytes" in response -\ No newline at end of file -+ assert "typesense_memory_retained_bytes" in response From 9d519d5cb0e0f45a61d3aa36242b8e6a062b8765 Mon Sep 17 00:00:00 2001 From: oddlama Date: Sun, 3 May 2026 14:37:38 +0200 Subject: [PATCH 2/2] typesense: 29.0 -> 30.2 --- pkgs/by-name/ty/typesense/sources.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/by-name/ty/typesense/sources.json b/pkgs/by-name/ty/typesense/sources.json index cb443f452229..77b5d2383b22 100644 --- a/pkgs/by-name/ty/typesense/sources.json +++ b/pkgs/by-name/ty/typesense/sources.json @@ -1,17 +1,17 @@ { - "version": "29.0", + "version": "30.2", "platforms": { "aarch64-linux": { "arch": "linux-arm64", - "hash": "sha256-ztjCyK2378tg9yT9D6ProINlMT5GyUIEH8duHivCQ2E=" + "hash": "sha256-qbnW4pJ8hjkAMGKQ6FMsF/Zr3/NO8q0EOyTtTM7Iprw=" }, "x86_64-linux": { "arch": "linux-amd64", - "hash": "sha256-BD0qE1dEVKVUPwO2fMO0SMXiwG9dD0RMnAzkVZitFKA=" + "hash": "sha256-zXkWBefGzHvkV3lPU0zUufnTYXga2xDkACDvwihA68M=" }, "aarch64-darwin": { "arch": "darwin-arm64", - "hash": "sha256-yAOR/bGYBQGp8Mllhh0yKyqmItd2+IfLib3W+lIHwr0=" + "hash": "sha256-fY1tDDOTCtIOoj3RhCUFR7FmFZRL6JGyB46KB1FS+n4=" } } }