Skip to content

Commit 80a31e4

Browse files
committed
linting and reorganized pre-push hook
1 parent fa2d782 commit 80a31e4

12 files changed

Lines changed: 127 additions & 143 deletions

File tree

.githooks/pre-push

Lines changed: 5 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,11 @@
11
#!/usr/bin/env bash
22

3-
# ensure generated python stubs are up-to-date, from sync clients and sift_stream_bindings
4-
5-
# Store the root directory of the repository
63
REPO_ROOT="$(git rev-parse --show-toplevel)"
7-
PYTHON_DIR="$REPO_ROOT/python"
8-
BINDINGS_DIR="$REPO_ROOT/rust/crates/sift_stream_bindings"
9-
STUBS_DIR="$PYTHON_DIR/lib/sift_client/resources/sync_stubs"
10-
11-
# Function to check if generated stub files have changed
12-
check_stub_changes() {
13-
local target_path="$1"
14-
local changed_files=$(git status --porcelain "$target_path" | grep -E '\.pyi$' || true)
15-
16-
if [ -n "$changed_files" ]; then
17-
echo "ERROR: Generated python stubs are not up-to-date. Please commit the changed files:"
18-
echo "$changed_files"
19-
exit 1
20-
fi
21-
}
22-
23-
# Function to generate Python stubs
24-
generate_python_stubs() {
25-
echo "Generating Python stubs..."
26-
cd "$PYTHON_DIR"
27-
28-
if [[ ! -d "$PYTHON_DIR/venv" ]]; then
29-
echo "Running bootstrap script..."
30-
bash ./scripts/dev bootstrap
31-
fi
32-
33-
bash ./scripts/dev gen-stubs
34-
check_stub_changes "$STUBS_DIR"
35-
}
36-
37-
# Function to generate bindings stubs
38-
generate_bindings_stubs() {
39-
echo "Generating bindings stubs..."
40-
cd "$BINDINGS_DIR"
41-
cargo run --bin stub_gen
42-
43-
# The stub file is generated in the bindings directory
44-
local stub_file="$BINDINGS_DIR/sift_stream_bindings.pyi"
45-
check_stub_changes "$stub_file"
46-
}
47-
48-
# Check for changes in relevant files
49-
python_changed_files=($(git diff --name-only --diff-filter=ACM | grep '^python/lib/sift_client/' || true))
50-
bindings_changed_files=($(git diff --name-only --diff-filter=ACM | grep '^rust/crates/sift_stream_bindings/src/' || true))
51-
52-
# Generate stubs if needed
53-
if [[ -n "$python_changed_files" ]]; then
54-
generate_python_stubs
55-
fi
564

57-
if [[ -n "$bindings_changed_files" ]]; then
58-
generate_bindings_stubs
5+
# Run Python-specific checks if Python files changed
6+
python_files_changed=($(git diff --name-only --diff-filter=ACM | grep '^python/' || true))
7+
if [[ -n "$python_files_changed" ]]; then
8+
echo "Python files changed, running Python checks..."
9+
bash "$REPO_ROOT/.githooks/pre-push-python/stubs-check.sh"
5910
fi
6011

61-
echo "All stubs are up-to-date."
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
set -e
3+
4+
5+
# ensure generated python stubs are up-to-date, from sync clients and sift_stream_bindings
6+
7+
REPO_ROOT="$(git rev-parse --show-toplevel)"
8+
PYTHON_DIR="$REPO_ROOT/python"
9+
BINDINGS_DIR="$REPO_ROOT/rust/crates/sift_stream_bindings"
10+
STUBS_DIR="$PYTHON_DIR/lib/sift_client/resources/sync_stubs"
11+
12+
# Function to check if generated stub files have changed
13+
check_stub_changes() {
14+
local target_path="$1"
15+
local changed_files=$(git status --porcelain "$target_path" | grep -E '\.pyi$' || true)
16+
17+
if [ -n "$changed_files" ]; then
18+
echo "ERROR: Generated python stubs are not up-to-date. Please commit the changed files:"
19+
echo "$changed_files"
20+
exit 1
21+
fi
22+
}
23+
24+
# Function to generate Python stubs
25+
generate_python_stubs() {
26+
echo "Generating Python stubs..."
27+
cd "$PYTHON_DIR"
28+
29+
if [[ ! -d "$PYTHON_DIR/venv" ]]; then
30+
echo "Running bootstrap script..."
31+
bash ./scripts/dev bootstrap
32+
fi
33+
34+
bash ./scripts/dev gen-stubs
35+
check_stub_changes "$STUBS_DIR"
36+
}
37+
38+
# Function to generate bindings stubs
39+
generate_bindings_stubs() {
40+
echo "Generating bindings stubs..."
41+
cd "$BINDINGS_DIR"
42+
cargo run --bin stub_gen
43+
44+
# The stub file is generated in the bindings directory
45+
local stub_file="$BINDINGS_DIR/sift_stream_bindings.pyi"
46+
check_stub_changes "$stub_file"
47+
}
48+
49+
# Check for changes in relevant files
50+
python_changed_files=($(git diff --name-only --diff-filter=ACM | grep '^python/lib/sift_client/' || true))
51+
bindings_changed_files=($(git diff --name-only --diff-filter=ACM | grep '^rust/crates/sift_stream_bindings/src/' || true))
52+
53+
# Generate stubs if needed
54+
if [[ -n "$python_changed_files" ]]; then
55+
generate_python_stubs
56+
fi
57+
58+
if [[ -n "$bindings_changed_files" ]]; then
59+
generate_bindings_stubs
60+
fi
61+
62+
echo "All stubs are up-to-date."

python/lib/sift_client/_tests/_internal/low_level_wrappers/test_base.py

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@ async def test_basic_pagination_single_page(self):
2828
results = await LowLevelClientBase._handle_pagination(mock_func)
2929

3030
assert results == [1, 2, 3]
31-
mock_func.assert_called_once_with(
32-
page_size=None,
33-
page_token="",
34-
order_by=None
35-
)
31+
mock_func.assert_called_once_with(page_size=None, page_token="", order_by=None)
3632

3733
@pytest.mark.asyncio
3834
async def test_pagination_multiple_pages(self):
@@ -42,7 +38,7 @@ async def test_pagination_multiple_pages(self):
4238
mock_func.side_effect = [
4339
([1, 2, 3], "token1"), # First page
4440
([4, 5, 6], "token2"), # Second page
45-
([7, 8, 9], ""), # Last page (empty token)
41+
([7, 8, 9], ""), # Last page (empty token)
4642
]
4743

4844
results = await LowLevelClientBase._handle_pagination(mock_func)
@@ -61,50 +57,33 @@ async def test_pagination_with_page_size(self):
6157
"""Test pagination with specified page size."""
6258
mock_func = AsyncMock(return_value=([1, 2], ""))
6359

64-
results = await LowLevelClientBase._handle_pagination(
65-
mock_func,
66-
page_size=2
67-
)
60+
results = await LowLevelClientBase._handle_pagination(mock_func, page_size=2)
6861

6962
assert results == [1, 2]
70-
mock_func.assert_called_once_with(
71-
page_size=2,
72-
page_token="",
73-
order_by=None
74-
)
63+
mock_func.assert_called_once_with(page_size=2, page_token="", order_by=None)
7564

7665
@pytest.mark.asyncio
7766
async def test_pagination_with_order_by(self):
7867
"""Test pagination with order_by parameter."""
7968
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
8069

81-
results = await LowLevelClientBase._handle_pagination(
82-
mock_func,
83-
order_by="name asc"
84-
)
70+
results = await LowLevelClientBase._handle_pagination(mock_func, order_by="name asc")
8571

8672
assert results == [1, 2, 3]
87-
mock_func.assert_called_once_with(
88-
page_size=None,
89-
page_token="",
90-
order_by="name asc"
91-
)
73+
mock_func.assert_called_once_with(page_size=None, page_token="", order_by="name asc")
9274

9375
@pytest.mark.asyncio
9476
async def test_pagination_with_initial_page_token(self):
9577
"""Test pagination starting with a specific page token."""
9678
mock_func = AsyncMock(return_value=([4, 5, 6], ""))
9779

9880
results = await LowLevelClientBase._handle_pagination(
99-
mock_func,
100-
page_token="start_token"
81+
mock_func, page_token="start_token"
10182
)
10283

10384
assert results == [4, 5, 6]
10485
mock_func.assert_called_once_with(
105-
page_size=None,
106-
page_token="start_token",
107-
order_by=None
86+
page_size=None, page_token="start_token", order_by=None
10887
)
10988

11089
@pytest.mark.asyncio
@@ -113,29 +92,23 @@ async def test_pagination_with_kwargs(self):
11392
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
11493
kwargs = {"filter": "active", "include_archived": False}
11594

116-
results = await LowLevelClientBase._handle_pagination(
117-
mock_func,
118-
kwargs=kwargs
119-
)
95+
results = await LowLevelClientBase._handle_pagination(mock_func, kwargs=kwargs)
12096

12197
assert results == [1, 2, 3]
12298
mock_func.assert_called_once_with(
12399
page_size=None,
124100
page_token="",
125101
order_by=None,
126102
filter="active",
127-
include_archived=False
103+
include_archived=False,
128104
)
129105

130106
@pytest.mark.asyncio
131107
async def test_pagination_with_max_results_single_page(self):
132108
"""Test pagination with max_results that fits in a single page."""
133109
mock_func = AsyncMock(return_value=([1, 2, 3, 4, 5], ""))
134110

135-
results = await LowLevelClientBase._handle_pagination(
136-
mock_func,
137-
max_results=3
138-
)
111+
results = await LowLevelClientBase._handle_pagination(mock_func, max_results=3)
139112

140113
# Should return only the max results
141114
assert results == [1, 2, 3]
@@ -150,10 +123,7 @@ async def test_pagination_with_max_results_multiple_pages(self):
150123
([4, 5, 6], "token2"), # Second page (6 total items, exceeds max_results=5)
151124
]
152125

153-
results = await LowLevelClientBase._handle_pagination(
154-
mock_func,
155-
max_results=5
156-
)
126+
results = await LowLevelClientBase._handle_pagination(mock_func, max_results=5)
157127

158128
# Should include 2 pages and return the full first page but limited 2nd page
159129
assert results == [1, 2, 3, 4, 5]
@@ -165,13 +135,10 @@ async def test_pagination_with_max_results_exact_match(self):
165135
mock_func = AsyncMock()
166136
mock_func.side_effect = [
167137
([1, 2, 3], "token1"), # First page
168-
([4, 5], ""), # Second page, total = 5
138+
([4, 5], ""), # Second page, total = 5
169139
]
170140

171-
results = await LowLevelClientBase._handle_pagination(
172-
mock_func,
173-
max_results=5
174-
)
141+
results = await LowLevelClientBase._handle_pagination(mock_func, max_results=5)
175142

176143
assert results == [1, 2, 3, 4, 5]
177144
assert mock_func.call_count == 2
@@ -186,18 +153,13 @@ async def test_pagination_empty_results(self):
186153
assert results == []
187154
mock_func.assert_called_once()
188155

189-
190156
@pytest.mark.asyncio
191157
async def test_pagination_max_results_zero(self):
192158
"""Test pagination with max_results=0."""
193159
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
194160

195-
results = await LowLevelClientBase._handle_pagination(
196-
mock_func,
197-
max_results=0
198-
)
161+
results = await LowLevelClientBase._handle_pagination(mock_func, max_results=0)
199162

200163
# Should return empty list without calling the function
201164
assert results == []
202165
mock_func.assert_not_called()
203-

python/lib/sift_client/_tests/integrated/calculated_channels.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,18 @@ async def main():
5656

5757
created_channels = []
5858
for i in range(num_channels):
59-
new_chan = CalculatedChannelCreate(name=f"test_channel_{unique_name_suffix}_{i}",
60-
description=f"Test calculated channel {i} - initial description",
61-
expression="$1 / $2", # $1 = mainmotor.velocity, $2 = voltage
62-
expression_channel_references=[
63-
ChannelReference(channel_reference="$1", channel_identifier="mainmotor.velocity"),
64-
ChannelReference(channel_reference="$2", channel_identifier="voltage"),
65-
],
66-
units="velocity/voltage",
67-
asset_ids=[asset_id],
68-
user_notes=f"Created for testing update fields - channel {i}",)
59+
new_chan = CalculatedChannelCreate(
60+
name=f"test_channel_{unique_name_suffix}_{i}",
61+
description=f"Test calculated channel {i} - initial description",
62+
expression="$1 / $2", # $1 = mainmotor.velocity, $2 = voltage
63+
expression_channel_references=[
64+
ChannelReference(channel_reference="$1", channel_identifier="mainmotor.velocity"),
65+
ChannelReference(channel_reference="$2", channel_identifier="voltage"),
66+
],
67+
units="velocity/voltage",
68+
asset_ids=[asset_id],
69+
user_notes=f"Created for testing update fields - channel {i}",
70+
)
6971
calculated_channel = client.calculated_channels.create(new_chan)
7072
created_channels.append(calculated_channel)
7173
print(

python/lib/sift_client/_tests/integrated/ingestion.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ async def main():
4444
print(f" Deleting run: {run.name}")
4545
client.runs.archive(run=run)
4646

47-
run = client.runs.create(dict(
48-
name=f"test-run-{datetime.now(tz=timezone.utc).timestamp()}",
49-
description="A test run created via the API",
50-
tags=["api-created", "test"],)
47+
run = client.runs.create(
48+
dict(
49+
name=f"test-run-{datetime.now(tz=timezone.utc).timestamp()}",
50+
description="A test run created via the API",
51+
tags=["api-created", "test"],
52+
)
5153
)
5254

5355
regular_flow = Flow(

python/lib/sift_client/_tests/resources/test_assets.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ def sift_client() -> SiftClient:
3131
rest_url=rest_url,
3232
)
3333

34+
3435
def test_client_binding(sift_client):
3536
assert sift_client.assets
3637
assert isinstance(sift_client.assets, AssetsAPI)
3738
assert sift_client.async_.assets
3839
assert isinstance(sift_client.async_.assets, AssetsAPIAsync)
3940

4041

41-
4242
@pytest.fixture
4343
def assets_api_async(sift_client: SiftClient):
4444
"""Get the async assets API instance."""
@@ -50,6 +50,7 @@ def assets_api_sync(sift_client: SiftClient):
5050
"""Get the synchronous assets API instance."""
5151
return sift_client.assets
5252

53+
5354
@pytest.fixture
5455
def test_asset(assets_api_sync):
5556
assets = assets_api_sync.list_(limit=1)
@@ -211,5 +212,3 @@ def test_basic_list(self, assets_api_sync):
211212
assert isinstance(assets, list)
212213
assert assets
213214
assert isinstance(assets[0], Asset)
214-
215-

python/lib/sift_client/_tests/resources/test_ping.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
pytestmark = pytest.mark.integration
1717

18+
1819
@pytest.fixture(scope="session")
1920
def sift_client() -> SiftClient:
2021
"""Create a SiftClient instance for testing."""
@@ -28,6 +29,7 @@ def sift_client() -> SiftClient:
2829
rest_url=rest_url,
2930
)
3031

32+
3133
def test_client_binding(sift_client):
3234
assert sift_client.ping
3335
assert isinstance(sift_client.ping, PingAPI)
@@ -40,11 +42,13 @@ def ping_api_async(sift_client: SiftClient):
4042
"""Get the ping async API instance."""
4143
return sift_client.async_.ping
4244

45+
4346
@pytest.fixture
4447
def ping_api_sync(sift_client: SiftClient):
4548
"""Get the synchronous ping API instance."""
4649
return sift_client.ping
4750

51+
4852
class TestPingAPIAsync:
4953
"""Test suite for the Ping API functionality."""
5054

0 commit comments

Comments
 (0)