Skip to content

Commit 489b8d4

Browse files
fix: resolve all ruff lint and format errors for CI
- Fix type comparisons using `is` instead of `==` (E721) - Combine nested if statements in conversation.py and sanitizer.py (SIM102) - Remove unused variables in sandbox.py (F841) - Add per-file-ignores in pyproject.toml for test patterns: - ARG001: Unused function arguments (common in test stubs) - ARG005: Unused lambda arguments (common in mock side_effects) - SIM117: Nested with statements (clearer for multiple mocks) - Run ruff format on all source and test files All 616 tests pass with 99.26% coverage. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f138bb5 commit 489b8d4

20 files changed

+201
-271
lines changed

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ ignore = [
110110
"E501", # line too long (handled by formatter)
111111
]
112112

113+
[tool.ruff.lint.per-file-ignores]
114+
"tests/**/*.py" = [
115+
"ARG001", # Unused function arguments common in test stubs
116+
"ARG005", # Unused lambda arguments common in mock side_effects
117+
"SIM117", # Nested with statements clearer for multiple mocks
118+
]
119+
113120
[tool.ruff.lint.isort]
114121
known-first-party = ["agent_airlock"]
115122

src/agent_airlock/__init__.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ def run_code(code: str) -> str:
2828
reset_context,
2929
set_current_context,
3030
)
31+
from .conversation import (
32+
ConversationConstraints,
33+
ConversationState,
34+
ConversationTracker,
35+
ToolCall,
36+
get_conversation_tracker,
37+
reset_conversation_tracker,
38+
)
3139
from .core import Airlock, SandboxExecutionError, SandboxUnavailableError, airlock
3240
from .policy import (
3341
BUSINESS_HOURS_POLICY,
@@ -60,14 +68,6 @@ def run_code(code: str) -> str:
6068
is_async_generator_function,
6169
is_generator_function,
6270
)
63-
from .conversation import (
64-
ConversationConstraints,
65-
ConversationState,
66-
ConversationTracker,
67-
ToolCall,
68-
get_conversation_tracker,
69-
reset_conversation_tracker,
70-
)
7171
from .validator import GhostArgumentError
7272

7373
__version__ = "0.1.5"

src/agent_airlock/conversation.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,7 @@ def _maybe_cleanup(self) -> None:
161161
self._last_cleanup = now
162162
cutoff = datetime.now(timezone.utc).timestamp() - self._ttl_seconds
163163
expired = [
164-
sid
165-
for sid, state in self._sessions.items()
166-
if state.created_at.timestamp() < cutoff
164+
sid for sid, state in self._sessions.items() if state.created_at.timestamp() < cutoff
167165
]
168166

169167
for sid in expired:
@@ -264,7 +262,10 @@ def should_block(
264262

265263
# Check if conversation is blocked
266264
if state.is_blocked:
267-
return True, f"Conversation blocked until {state.blocked_until}: {state.block_reason}"
265+
return (
266+
True,
267+
f"Conversation blocked until {state.blocked_until}: {state.block_reason}",
268+
)
268269

269270
# Check cooldown_after constraints
270271
for trigger_tool, cooldown_seconds in constraints.cooldown_after.items():
@@ -287,8 +288,7 @@ def should_block(
287288
if elapsed < cooldown:
288289
remaining = int(cooldown - elapsed)
289290
return True, (
290-
f"Tool '{tool_name}' cooldown: "
291-
f"{remaining}s remaining of {cooldown}s"
291+
f"Tool '{tool_name}' cooldown: {remaining}s remaining of {cooldown}s"
292292
)
293293

294294
# Check max calls per tool
@@ -297,8 +297,7 @@ def should_block(
297297
current_calls = len(state.get_successful_calls_for_tool(tool_name))
298298
if current_calls >= max_calls:
299299
return True, (
300-
f"Tool '{tool_name}' limit reached: "
301-
f"{current_calls}/{max_calls} calls used"
300+
f"Tool '{tool_name}' limit reached: {current_calls}/{max_calls} calls used"
302301
)
303302

304303
# Check required_before constraints
@@ -308,17 +307,18 @@ def should_block(
308307
missing = set(required_tools) - called_tools
309308
if missing:
310309
return True, (
311-
f"Tool '{tool_name}' requires prior calls to: "
312-
f"{', '.join(sorted(missing))}"
310+
f"Tool '{tool_name}' requires prior calls to: {', '.join(sorted(missing))}"
313311
)
314312

315313
# Check max total calls
316-
if constraints.max_total_calls is not None:
317-
if state.success_count >= constraints.max_total_calls:
318-
return True, (
319-
f"Conversation call limit reached: "
320-
f"{state.success_count}/{constraints.max_total_calls}"
321-
)
314+
if (
315+
constraints.max_total_calls is not None
316+
and state.success_count >= constraints.max_total_calls
317+
):
318+
return True, (
319+
f"Conversation call limit reached: "
320+
f"{state.success_count}/{constraints.max_total_calls}"
321+
)
322322

323323
# Check blocked ratio
324324
if constraints.max_blocked_ratio is not None and state.call_count > 0:

src/agent_airlock/core.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -448,9 +448,7 @@ async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> R | dict[str, Any]
448448
token = set_current_context(context)
449449
try:
450450
if self.sandbox:
451-
result = await self._execute_in_sandbox_async(
452-
func, *args, **cleaned_kwargs
453-
)
451+
result = await self._execute_in_sandbox_async(func, *args, **cleaned_kwargs)
454452
else:
455453
# Await the async validated function
456454
# Type ignore: validated_func preserves async nature of func

src/agent_airlock/sandbox.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,6 @@ def execute_in_sandbox(
430430

431431
# Execute in sandbox
432432
pool = get_sandbox_pool(config)
433-
stdout_lines: list[str] = []
434-
stderr_lines: list[str] = []
435433

436434
try:
437435
with pool.sandbox() as sandbox:

src/agent_airlock/sanitizer.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -616,13 +616,15 @@ def sanitize_with_workspace_config(
616616
data_type = SensitiveDataType(detection["type"])
617617

618618
# Apply workspace-specific filtering
619-
if data_type == SensitiveDataType.EMAIL:
620-
if not workspace_config.should_mask_email(detection["value"]):
621-
continue
619+
if data_type == SensitiveDataType.EMAIL and not workspace_config.should_mask_email(
620+
detection["value"]
621+
):
622+
continue
622623

623-
if data_type == SensitiveDataType.PHONE:
624-
if not workspace_config.should_mask_phone(detection["value"]):
625-
continue
624+
if data_type == SensitiveDataType.PHONE and not workspace_config.should_mask_phone(
625+
detection["value"]
626+
):
627+
continue
626628

627629
filtered_detections.append(detection)
628630

@@ -650,10 +652,12 @@ def sanitize_with_workspace_config(
650652
# Handle custom patterns
651653
if type_str.startswith("custom:"):
652654
pattern_name = type_str[7:]
653-
strategy = workspace_config.custom_strategies.get(
654-
pattern_name, MaskingStrategy.FULL
655+
strategy = workspace_config.custom_strategies.get(pattern_name, MaskingStrategy.FULL)
656+
masked = (
657+
f"[{pattern_name.upper()}]"
658+
if strategy == MaskingStrategy.TYPE_ONLY
659+
else "[REDACTED]"
655660
)
656-
masked = f"[{pattern_name.upper()}]" if strategy == MaskingStrategy.TYPE_ONLY else "[REDACTED]"
657661
else:
658662
data_type = SensitiveDataType(type_str)
659663
strategy = mask_config.get(data_type, MaskingStrategy.FULL)
@@ -664,21 +668,15 @@ def sanitize_with_workspace_config(
664668
masked_value = _mask_value(password_value, data_type, strategy)
665669
masked = full_match.replace(password_value, masked_value)
666670
result_text = (
667-
result_text[: detection["start"]]
668-
+ masked
669-
+ result_text[detection["end"] :]
671+
result_text[: detection["start"]] + masked + result_text[detection["end"] :]
670672
)
671673
detection["masked_as"] = "[REDACTED]"
672674
detections.append(detection)
673675
continue
674676
else:
675677
masked = _mask_value(detection["value"], data_type, strategy)
676678

677-
result_text = (
678-
result_text[: detection["start"]]
679-
+ masked
680-
+ result_text[detection["end"] :]
681-
)
679+
result_text = result_text[: detection["start"]] + masked + result_text[detection["end"] :]
682680
detection["masked_as"] = masked
683681
detections.append(detection)
684682

src/agent_airlock/streaming.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,5 @@ def sync_wrapper(*args: Any, **kwargs: Any) -> Generator[Any, None, None]:
361361

362362
else:
363363
raise TypeError(
364-
f"create_streaming_wrapper requires a generator function, "
365-
f"got {type(func).__name__}"
364+
f"create_streaming_wrapper requires a generator function, got {type(func).__name__}"
366365
)

tests/test_azure_compatibility.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
import json
1515
from typing import Any, Literal, get_type_hints
1616

17-
import pytest
18-
1917
from agent_airlock import Airlock, AirlockConfig, SecurityPolicy
2018

2119

@@ -58,13 +56,13 @@ def get_weather(
5856
prop: dict[str, Any] = {}
5957

6058
# Map Python types to JSON Schema types
61-
if hint == str or str(hint) == "str":
59+
if hint is str or str(hint) == "str":
6260
prop["type"] = "string"
63-
elif hint == int or str(hint) == "int":
61+
elif hint is int or str(hint) == "int":
6462
prop["type"] = "integer"
65-
elif hint == float or str(hint) == "float":
63+
elif hint is float or str(hint) == "float":
6664
prop["type"] = "number"
67-
elif hint == bool or str(hint) == "bool":
65+
elif hint is bool or str(hint) == "bool":
6866
prop["type"] = "boolean"
6967
elif hasattr(hint, "__origin__") and hint.__origin__ is Literal:
7068
prop["type"] = "string"
@@ -387,13 +385,13 @@ def _python_type_to_json(hint: Any) -> str:
387385
"""Convert Python type hint to JSON schema type."""
388386
if hint is None:
389387
return "string"
390-
if hint == str or str(hint) == "str":
388+
if hint is str or str(hint) == "str":
391389
return "string"
392-
if hint == int or str(hint) == "int":
390+
if hint is int or str(hint) == "int":
393391
return "integer"
394-
if hint == float or str(hint) == "float":
392+
if hint is float or str(hint) == "float":
395393
return "number"
396-
if hint == bool or str(hint) == "bool":
394+
if hint is bool or str(hint) == "bool":
397395
return "boolean"
398396
return "string"
399397

@@ -420,7 +418,9 @@ def rate_limited_tool(x: int) -> int:
420418
result = rate_limited_tool(x=2)
421419
assert isinstance(result, dict)
422420
assert result.get("status") == "blocked"
423-
assert "rate" in result.get("reason", "").lower() or "limit" in result.get("error", "").lower()
421+
assert (
422+
"rate" in result.get("reason", "").lower() or "limit" in result.get("error", "").lower()
423+
)
424424

425425

426426
class TestAzureGovCloud:

0 commit comments

Comments
 (0)