Skip to content

Commit ace8c60

Browse files
feat(python): add ruff linter and formatter
Add ruff for linting and formatting across all Python SDK packages.
1 parent a1fa913 commit ace8c60

File tree

41 files changed

+684
-514
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+684
-514
lines changed

.github/workflows/ci.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ jobs:
6464
- name: Test
6565
run: pnpm --filter @grafana/sigil-sdk-js run test:ci
6666

67+
python-lint:
68+
name: Python Lint
69+
runs-on: ubuntu-latest
70+
steps:
71+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
72+
with:
73+
persist-credentials: false
74+
75+
- name: ruff check
76+
run: uvx ruff check python/ python-providers/ python-frameworks/
77+
78+
- name: ruff format
79+
run: uvx ruff format --check python/ python-providers/ python-frameworks/
80+
6781
python:
6882
name: Python SDK
6983
runs-on: ubuntu-latest

mise.toml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ while IFS= read -r moddir; do
2020
done < <(find . -name go.mod -not -path './node_modules/*' -exec dirname {} \\; | sort)
2121
"""
2222

23+
[tasks."format:py"]
24+
description = "Format Python SDK code"
25+
run = "uvx ruff format python/ python-providers/ python-frameworks/"
26+
2327
[tasks."format:cs"]
2428
description = "Format .NET SDK code"
2529
run = "dotnet format dotnet/Sigil.DotNet.sln"
2630

2731
[tasks.format]
2832
description = "Format all code"
29-
depends = ["format:go", "format:cs"]
33+
depends = ["format:go", "format:py", "format:cs"]
3034

3135
# --- Linting ---
3236

@@ -45,13 +49,22 @@ while IFS= read -r moddir; do
4549
done < <(find . -name go.mod -not -path './node_modules/*' -exec dirname {} \\; | sort)
4650
"""
4751

52+
[tasks."lint:py"]
53+
description = "Lint and format-check Python SDK code"
54+
run = """
55+
#!/usr/bin/env bash
56+
set -euo pipefail
57+
uvx ruff check python/ python-providers/ python-frameworks/
58+
uvx ruff format --check python/ python-providers/ python-frameworks/
59+
"""
60+
4861
[tasks."lint:cs"]
4962
description = "Verify .NET SDK formatting and analyzer checks"
5063
run = "dotnet format dotnet/Sigil.DotNet.sln --verify-no-changes"
5164

5265
[tasks.lint]
5366
description = "Run all linting"
54-
depends = ["lint:go", "lint:cs"]
67+
depends = ["lint:go", "lint:py", "lint:cs"]
5568

5669
# --- Type checking ---
5770

python-frameworks/google-adk/sigil_sdk_google_adk/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
try: # pragma: no cover - imported from google-adk at runtime
1515
from google.adk.plugins import BasePlugin
1616
except Exception: # pragma: no cover - lightweight fallback for local unit tests
17+
1718
class BasePlugin: # type: ignore[no-redef]
1819
"""Fallback BasePlugin shape used when google-adk isn't installed."""
1920

@@ -52,6 +53,7 @@ async def after_tool_callback(
5253
del tool, tool_args, tool_context, result
5354
return None
5455

56+
5557
_adk_callback_fields = (
5658
"before_model_callback",
5759
"after_model_callback",
@@ -170,7 +172,9 @@ async def before_tool_callback(self, tool: Any, args: dict[str, Any], tool_conte
170172
)
171173
return None
172174

173-
async def after_tool_callback(self, tool: Any, args: dict[str, Any], tool_context: Any, result: dict[str, Any]) -> None:
175+
async def after_tool_callback(
176+
self, tool: Any, args: dict[str, Any], tool_context: Any, result: dict[str, Any]
177+
) -> None:
174178
del tool, args
175179
invocation_key = self._invocation_key(tool_context)
176180
function_call_id = _as_string(_read(tool_context, "function_call_id"))
@@ -184,7 +188,9 @@ async def after_tool_callback(self, tool: Any, args: dict[str, Any], tool_contex
184188
await _invoke_handler(self._sigil_handler, "on_tool_end", result, run_id=run_id)
185189
return None
186190

187-
async def on_tool_error_callback(self, tool: Any, args: dict[str, Any], tool_context: Any, error: Exception) -> None:
191+
async def on_tool_error_callback(
192+
self, tool: Any, args: dict[str, Any], tool_context: Any, error: Exception
193+
) -> None:
188194
del tool, args
189195
invocation_key = self._invocation_key(tool_context)
190196
function_call_id = _as_string(_read(tool_context, "function_call_id"))
@@ -454,7 +460,7 @@ def with_sigil_google_adk_plugins(
454460
plugins = _as_list(getattr(target, "plugins", None))
455461
if not _contains_sigil_plugin(plugins):
456462
plugins.append(plugin)
457-
setattr(target, "plugins", plugins)
463+
target.plugins = plugins
458464
return target
459465

460466

python-frameworks/google-adk/tests/test_sigil_sdk_google_adk.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
from sigil_sdk_google_adk import (
1616
SigilAsyncGoogleAdkHandler,
1717
SigilGoogleAdkCallbacks,
18-
SigilGoogleAdkPlugin,
1918
SigilGoogleAdkHandler,
20-
with_sigil_google_adk_callbacks,
19+
SigilGoogleAdkPlugin,
2120
create_sigil_google_adk_handler,
2221
create_sigil_google_adk_plugin,
22+
with_sigil_google_adk_callbacks,
2323
with_sigil_google_adk_plugins,
2424
)
2525

@@ -32,8 +32,7 @@ def export_generations(self, request):
3232
self.requests.append(request)
3333
return ExportGenerationsResponse(
3434
results=[
35-
ExportGenerationResult(generation_id=generation.id, accepted=True)
36-
for generation in request.generations
35+
ExportGenerationResult(generation_id=generation.id, accepted=True) for generation in request.generations
3736
]
3837
)
3938

@@ -205,7 +204,10 @@ def test_sigil_sdk_google_adk_generation_span_tracks_active_parent_span_and_expo
205204
run_id=run_id,
206205
parent_run_id=uuid4(),
207206
invocation_params={"model": "gpt-5"},
208-
metadata={"conversation_id": "framework-conversation-lineage-42", "thread_id": "framework-thread-lineage-42"},
207+
metadata={
208+
"conversation_id": "framework-conversation-lineage-42",
209+
"thread_id": "framework-thread-lineage-42",
210+
},
209211
)
210212
handler.on_llm_end(
211213
{"generations": [[{"text": "world"}]], "llm_output": {"model_name": "gpt-5", "finish_reason": "stop"}},

python-frameworks/langchain/sigil_sdk_langchain/handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
try:
1212
from langchain_core.callbacks import AsyncCallbackHandler, BaseCallbackHandler
1313
except ModuleNotFoundError: # pragma: no cover - handled by package dependency in normal installs
14+
1415
class BaseCallbackHandler: # type: ignore[no-redef]
1516
"""Fallback base class when langchain-core is unavailable."""
1617

python-frameworks/langchain/tests/test_langchain_handler.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ def export_generations(self, request):
2929
self.requests.append(request)
3030
return ExportGenerationsResponse(
3131
results=[
32-
ExportGenerationResult(generation_id=generation.id, accepted=True)
33-
for generation in request.generations
32+
ExportGenerationResult(generation_id=generation.id, accepted=True) for generation in request.generations
3433
]
3534
)
3635

@@ -61,7 +60,11 @@ def test_langchain_sync_lifecycle_sets_framework_tags_and_metadata() -> None:
6160
agent_version="v1",
6261
provider_resolver="auto",
6362
extra_tags={"env": "test", "sigil.framework.name": "override"},
64-
extra_metadata={"seed": 7, "sigil.framework.run_id": "override-run", "sigil.framework.thread_id": "override-thread"},
63+
extra_metadata={
64+
"seed": 7,
65+
"sigil.framework.run_id": "override-run",
66+
"sigil.framework.thread_id": "override-thread",
67+
},
6568
)
6669

6770
handler.on_chat_model_start(
@@ -422,7 +425,9 @@ def test_langchain_tool_chain_and_retriever_callbacks_emit_spans() -> None:
422425
spans = span_exporter.get_finished_spans()
423426
tool_span = next(span for span in spans if span.attributes.get("gen_ai.operation.name") == "execute_tool")
424427
chain_span = next(span for span in spans if span.attributes.get("gen_ai.operation.name") == "framework_chain")
425-
retriever_span = next(span for span in spans if span.attributes.get("gen_ai.operation.name") == "framework_retriever")
428+
retriever_span = next(
429+
span for span in spans if span.attributes.get("gen_ai.operation.name") == "framework_retriever"
430+
)
426431

427432
assert tool_span.attributes.get("gen_ai.tool.name") == "weather"
428433
assert tool_span.attributes.get("gen_ai.conversation.id") == "chain-thread-42"

python-frameworks/langgraph/sigil_sdk_langgraph/handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
try:
1212
from langchain_core.callbacks import AsyncCallbackHandler, BaseCallbackHandler
1313
except ModuleNotFoundError: # pragma: no cover - handled by package dependency in normal installs
14+
1415
class BaseCallbackHandler: # type: ignore[no-redef]
1516
"""Fallback base class when langchain-core is unavailable."""
1617

python-frameworks/langgraph/tests/test_langgraph_handler.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ def export_generations(self, request):
2727
self.requests.append(request)
2828
return ExportGenerationsResponse(
2929
results=[
30-
ExportGenerationResult(generation_id=generation.id, accepted=True)
31-
for generation in request.generations
30+
ExportGenerationResult(generation_id=generation.id, accepted=True) for generation in request.generations
3231
]
3332
)
3433

@@ -59,7 +58,11 @@ def test_langgraph_sync_lifecycle_sets_framework_tags_and_metadata() -> None:
5958
agent_version="v1",
6059
provider_resolver="auto",
6160
extra_tags={"env": "test", "sigil.framework.name": "override"},
62-
extra_metadata={"seed": 7, "sigil.framework.run_id": "override-run", "sigil.framework.thread_id": "override-thread"},
61+
extra_metadata={
62+
"seed": 7,
63+
"sigil.framework.run_id": "override-run",
64+
"sigil.framework.thread_id": "override-thread",
65+
},
6366
)
6467

6568
handler.on_chat_model_start(
@@ -299,7 +302,9 @@ def test_langgraph_tool_chain_and_retriever_callbacks_emit_spans() -> None:
299302
spans = span_exporter.get_finished_spans()
300303
tool_span = next(span for span in spans if span.attributes.get("gen_ai.operation.name") == "execute_tool")
301304
chain_span = next(span for span in spans if span.attributes.get("gen_ai.operation.name") == "framework_chain")
302-
retriever_span = next(span for span in spans if span.attributes.get("gen_ai.operation.name") == "framework_retriever")
305+
retriever_span = next(
306+
span for span in spans if span.attributes.get("gen_ai.operation.name") == "framework_retriever"
307+
)
303308

304309
assert tool_span.attributes.get("gen_ai.tool.name") == "weather"
305310
assert tool_span.attributes.get("gen_ai.conversation.id") == "graph-thread-42"

python-frameworks/litellm/sigil_sdk_litellm/handler.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626

2727
logger = logging.getLogger(__name__)
2828

29-
_CHAT_CALL_TYPES = frozenset({
30-
"completion",
31-
"acompletion",
32-
"text_completion",
33-
"atext_completion",
34-
})
29+
_CHAT_CALL_TYPES = frozenset(
30+
{
31+
"completion",
32+
"acompletion",
33+
"text_completion",
34+
"atext_completion",
35+
}
36+
)
3537

3638

3739
def _make_tool_call_part(*, call_id: str, name: str, arguments: str) -> Part:
@@ -247,12 +249,14 @@ def _map_tool_definitions(kwargs: dict[str, Any]) -> list[ToolDefinition]:
247249
description = function.get("description", "")
248250
parameters = function.get("parameters")
249251
schema_json = json.dumps(parameters).encode("utf-8") if parameters else b""
250-
out.append(ToolDefinition(
251-
name=name,
252-
description=description,
253-
type=tool_type,
254-
input_schema_json=schema_json,
255-
))
252+
out.append(
253+
ToolDefinition(
254+
name=name,
255+
description=description,
256+
type=tool_type,
257+
input_schema_json=schema_json,
258+
)
259+
)
256260
return out
257261

258262

@@ -336,10 +340,14 @@ def log_success_event(self, kwargs: dict, response_obj: Any, start_time: datetim
336340
def log_failure_event(self, kwargs: dict, response_obj: Any, start_time: datetime, end_time: datetime) -> None:
337341
self._log_event(kwargs, response_obj, start_time, end_time, is_failure=True)
338342

339-
async def async_log_success_event(self, kwargs: dict, response_obj: Any, start_time: datetime, end_time: datetime) -> None:
343+
async def async_log_success_event(
344+
self, kwargs: dict, response_obj: Any, start_time: datetime, end_time: datetime
345+
) -> None:
340346
self._log_event(kwargs, response_obj, start_time, end_time, is_failure=False)
341347

342-
async def async_log_failure_event(self, kwargs: dict, response_obj: Any, start_time: datetime, end_time: datetime) -> None:
348+
async def async_log_failure_event(
349+
self, kwargs: dict, response_obj: Any, start_time: datetime, end_time: datetime
350+
) -> None:
343351
self._log_event(kwargs, response_obj, start_time, end_time, is_failure=True)
344352

345353
def _log_event(

python-frameworks/litellm/tests/test_litellm_handler.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
GenerationMode,
1414
MessageRole,
1515
PartKind,
16-
ToolDefinition,
1716
)
1817
from sigil_sdk_litellm import SigilLiteLLMLogger, create_sigil_litellm_logger
1918

@@ -25,10 +24,7 @@ def __init__(self) -> None:
2524
def export_generations(self, request: Any) -> ExportGenerationsResponse:
2625
self.requests.append(request)
2726
return ExportGenerationsResponse(
28-
results=[
29-
ExportGenerationResult(generation_id=g.id, accepted=True)
30-
for g in request.generations
31-
]
27+
results=[ExportGenerationResult(generation_id=g.id, accepted=True) for g in request.generations]
3228
)
3329

3430
def shutdown(self) -> None:
@@ -695,7 +691,10 @@ def test_litellm_session_id_used_as_conversation_id() -> None:
695691
},
696692
}
697693
handler.log_success_event(
698-
kwargs=kwargs, response_obj=None, start_time=_START, end_time=_END,
694+
kwargs=kwargs,
695+
response_obj=None,
696+
start_time=_START,
697+
end_time=_END,
699698
)
700699
client.flush()
701700

@@ -720,7 +719,10 @@ def test_litellm_trace_id_used_as_conversation_id() -> None:
720719
},
721720
}
722721
handler.log_success_event(
723-
kwargs=kwargs, response_obj=None, start_time=_START, end_time=_END,
722+
kwargs=kwargs,
723+
response_obj=None,
724+
start_time=_START,
725+
end_time=_END,
724726
)
725727
client.flush()
726728

@@ -745,7 +747,10 @@ def test_metadata_conversation_id_takes_precedence_over_litellm_session() -> Non
745747
},
746748
}
747749
handler.log_success_event(
748-
kwargs=kwargs, response_obj=None, start_time=_START, end_time=_END,
750+
kwargs=kwargs,
751+
response_obj=None,
752+
start_time=_START,
753+
end_time=_END,
749754
)
750755
client.flush()
751756

@@ -987,7 +992,7 @@ def test_non_utc_timezone_converted_to_utc() -> None:
987992

988993
tz_plus5 = timezone(timedelta(hours=5))
989994
start = datetime(2024, 1, 1, 15, 0, 0, tzinfo=tz_plus5) # = 10:00 UTC
990-
end = datetime(2024, 1, 1, 15, 0, 1, tzinfo=tz_plus5) # = 10:00:01 UTC
995+
end = datetime(2024, 1, 1, 15, 0, 1, tzinfo=tz_plus5) # = 10:00:01 UTC
991996

992997
handler.log_success_event(
993998
kwargs=_make_kwargs(slo),

0 commit comments

Comments
 (0)