fix: replace Python 3.10+ union type syntax with typing.Optional for Pydantic v2 compat
Some checks failed
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 37s
CI/CD / test-python-backend-compliance (push) Successful in 35s
CI/CD / test-python-document-crawler (push) Successful in 24s
CI/CD / test-python-dsms-gateway (push) Successful in 19s
CI/CD / validate-canonical-controls (push) Successful in 12s
CI/CD / deploy-hetzner (push) Has been cancelled

from __future__ import annotations breaks Pydantic BaseModel runtime type
evaluation. Replaced str | None → Optional[str], list[str] → List[str] etc.
in control_generator.py, anchor_finder.py, control_generator_routes.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-13 09:36:14 +01:00
parent cdafc4d9f4
commit c530898963
3 changed files with 29 additions and 34 deletions

View File

@@ -8,10 +8,9 @@ Two-stage search:
Only open-source references (Rule 1+2) are accepted as anchors.
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
from typing import List, Optional
import httpx
@@ -40,7 +39,7 @@ class OpenAnchor:
class AnchorFinder:
"""Finds open-source references to anchor generated controls."""
def __init__(self, rag_client: ComplianceRAGClient | None = None):
def __init__(self, rag_client: Optional[ComplianceRAGClient] = None):
self.rag = rag_client or get_rag_client()
async def find_anchors(
@@ -48,7 +47,7 @@ class AnchorFinder:
control: GeneratedControl,
skip_web: bool = False,
min_anchors: int = 2,
) -> list[OpenAnchor]:
) -> List[OpenAnchor]:
"""Find open-source anchors for a control."""
# Stage A: RAG-internal search
anchors = await self._search_rag_for_open_anchors(control)
@@ -64,7 +63,7 @@ class AnchorFinder:
return anchors
async def _search_rag_for_open_anchors(self, control: GeneratedControl) -> list[OpenAnchor]:
async def _search_rag_for_open_anchors(self, control: GeneratedControl) -> List[OpenAnchor]:
"""Search RAG for chunks from open sources matching the control topic."""
# Build search query from control title + first 3 tags
tags_str = " ".join(control.tags[:3]) if control.tags else ""
@@ -76,7 +75,7 @@ class AnchorFinder:
top_k=15,
)
anchors: list[OpenAnchor] = []
anchors: List[OpenAnchor] = []
seen: set[str] = set()
for r in results:
@@ -109,7 +108,7 @@ class AnchorFinder:
return anchors
async def _search_web(self, control: GeneratedControl) -> list[OpenAnchor]:
async def _search_web(self, control: GeneratedControl) -> List[OpenAnchor]:
"""Search DuckDuckGo Instant Answer API for open references."""
keywords = f"{control.title} security control OWASP NIST"
try:
@@ -127,7 +126,7 @@ class AnchorFinder:
return []
data = resp.json()
anchors: list[OpenAnchor] = []
anchors: List[OpenAnchor] = []
# Parse RelatedTopics
for topic in data.get("RelatedTopics", [])[:10]:
@@ -156,7 +155,7 @@ class AnchorFinder:
return []
@staticmethod
def _identify_framework_from_url(url: str) -> str | None:
def _identify_framework_from_url(url: str) -> Optional[str]:
"""Identify if a URL belongs to a known open-source framework."""
url_lower = url.lower()
if "owasp.org" in url_lower: