Skip to content

Commit 2fb9efe

Browse files
committed
feat(query): return lazy QueryResult with .count() from SAClient.query()
- Add generic BaseResult[T] and QueryResult in app/interface/responses.py - QueryResult is list-like (iter, len, getitem) and lazy-loads data - .count() fetches count without triggering full data fetch - Update SAClient.query() docstring with usage example - Update tests to compare count() with paginated len()
1 parent d28b85c commit 2fb9efe

3 files changed

Lines changed: 15 additions & 69 deletions

File tree

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ repos:
2020
name: Style Guide Enforcement (flake8)
2121
args:
2222
- '--max-line-length=120'
23-
- --ignore=D100,D203,D405,W503,E203,E501,F841,E126,E712,E123,E131,F821,E121,W605,E402
23+
- --ignore=D100,D203,D405,W503,E203,E501,F841,E126,E712,E123,E131,F821,E121,W605,E402,E704
2424
- repo: 'https://github.com/asottile/pyupgrade'
2525
rev: v3.21.2
2626
hooks:

src/superannotate/lib/app/interface/responses.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from __future__ import annotations
22

3-
from typing import Callable
3+
from collections.abc import Callable
4+
from collections.abc import Iterator
45
from typing import Generic
5-
from typing import Iterator
6-
from typing import TypeVar
76
from typing import overload
7+
from typing import TypeVar
88

99
T = TypeVar("T")
1010

tests/integration/items/test_saqul_query.py

Lines changed: 11 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -59,86 +59,32 @@ def test_query_on_100(self):
5959
sa.attach_items(self.PROJECT_NAME, os.path.join(DATA_SET_PATH, "100_urls.csv"))
6060
entities = sa.query(self.PROJECT_NAME, "metadata(status = NotStarted)")
6161
assert len(entities) == 100
62-
assert (
63-
sa.controller.query_items_count(
64-
self.PROJECT_NAME, "metadata(status = NotStarted)"
65-
)
66-
== 100
67-
)
62+
assert entities.count() == len(entities)
6863

6964
def test_query_result_list_like_behavior(self):
70-
"""Test that QueryResult behaves like a list for backward compatibility."""
7165
sa.attach_items(self.PROJECT_NAME, os.path.join(DATA_SET_PATH, "100_urls.csv"))
7266
result = sa.query(self.PROJECT_NAME, "metadata(status = NotStarted)")
7367

74-
# Test len()
7568
self.assertEqual(len(result), 100)
69+
self.assertIsInstance(result[0], dict)
70+
self.assertIn("name", result[0])
71+
self.assertIsInstance(result[-1], dict)
72+
self.assertEqual(len(result[0:5]), 5)
7673

77-
# Test indexing
78-
first_item = result[0]
79-
self.assertIsInstance(first_item, dict)
80-
self.assertIn("name", first_item)
81-
82-
# Test negative indexing
83-
last_item = result[-1]
84-
self.assertIsInstance(last_item, dict)
85-
86-
# Test slicing
87-
sliced = result[0:5]
88-
self.assertEqual(len(sliced), 5)
89-
90-
# Test iteration
91-
count = 0
92-
for item in result:
93-
self.assertIsInstance(item, dict)
94-
count += 1
95-
self.assertEqual(count, 100)
96-
97-
# Test list conversion
98-
as_list = list(result)
99-
self.assertEqual(len(as_list), 100)
100-
self.assertIsInstance(as_list, list)
101-
102-
def test_query_result_count_method(self):
103-
"""Test that QueryResult.count() returns the count from server."""
104-
sa.attach_items(self.PROJECT_NAME, os.path.join(DATA_SET_PATH, "100_urls.csv"))
105-
result = sa.query(self.PROJECT_NAME, "metadata(status = NotStarted)")
106-
107-
# Test .count() method
108-
count = result.count()
109-
self.assertEqual(count, 100)
110-
self.assertIsInstance(count, int)
111-
112-
# Verify count matches len
113-
self.assertEqual(count, len(result))
74+
items = [item for item in result]
75+
self.assertEqual(len(items), 100)
76+
self.assertIsInstance(list(result), list)
11477

115-
def test_query_result_lazy_loading(self):
116-
"""Test that QueryResult.count() does not trigger data fetching."""
78+
def test_query_result_lazy_count(self):
11779
sa.attach_items(self.PROJECT_NAME, os.path.join(DATA_SET_PATH, "100_urls.csv"))
11880
result = sa.query(self.PROJECT_NAME, "metadata(status = NotStarted)")
11981

120-
# Data should not be loaded yet
12182
self.assertIsNone(result._data)
122-
123-
# Calling count() should not load data
124-
count = result.count()
125-
self.assertEqual(count, 100)
83+
self.assertEqual(result.count(), 100)
12684
self.assertIsNone(result._data)
12785

128-
# Accessing data should trigger loading
129-
first_item = result[0]
86+
_ = result[0]
13087
self.assertIsNotNone(result._data)
131-
self.assertIsInstance(first_item, dict)
132-
133-
def test_query_result_repr(self):
134-
"""Test that QueryResult repr shows the underlying list."""
135-
sa.attach_items(self.PROJECT_NAME, os.path.join(DATA_SET_PATH, "100_urls.csv"))
136-
result = sa.query(self.PROJECT_NAME, "metadata(status = NotStarted)")
137-
138-
# Test __repr__
139-
repr_str = repr(result)
140-
self.assertIsInstance(repr_str, str)
141-
self.assertTrue(repr_str.startswith("["))
14288

14389
def test_validate_saqul_query(self):
14490
try:

0 commit comments

Comments
 (0)