style: apply ruff format to OCR modules
This commit is contained in:
+10
-6
@@ -295,11 +295,15 @@ class OcrClient:
|
|||||||
await self._record_quietly(self._quota.mark_progress())
|
await self._record_quietly(self._quota.mark_progress())
|
||||||
self._feed_outcome(source.name, ok=True)
|
self._feed_outcome(source.name, ok=True)
|
||||||
latency_ms = int((self._now() - started) * 1000)
|
latency_ms = int((self._now() - started) * 1000)
|
||||||
await self._emit(kind, image, source, call_id, started, session_id, parent_call_id, result)
|
await self._emit(
|
||||||
|
kind, image, source, call_id, started, session_id, parent_call_id, result
|
||||||
|
)
|
||||||
return _AttemptOutcome(result, source, call_id, latency_ms)
|
return _AttemptOutcome(result, source, call_id, latency_ms)
|
||||||
except (RequestRejectedError, ResultInvalidError) as exc:
|
except (RequestRejectedError, ResultInvalidError) as exc:
|
||||||
await self._gate_on_terminal(exc, entry)
|
await self._gate_on_terminal(exc, entry)
|
||||||
await self._emit(kind, image, source, call_id, started, session_id, parent_call_id, error=exc)
|
await self._emit(
|
||||||
|
kind, image, source, call_id, started, session_id, parent_call_id, error=exc
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
if entry.is_probe:
|
if entry.is_probe:
|
||||||
@@ -314,7 +318,9 @@ class OcrClient:
|
|||||||
reasons[source.name] = reason
|
reasons[source.name] = reason
|
||||||
await self._record_quietly(self._breaker.record_failure(entry, reason, dead))
|
await self._record_quietly(self._breaker.record_failure(entry, reason, dead))
|
||||||
self._feed_outcome(source.name, ok=False)
|
self._feed_outcome(source.name, ok=False)
|
||||||
await self._emit(kind, image, source, call_id, started, session_id, parent_call_id, error=exc)
|
await self._emit(
|
||||||
|
kind, image, source, call_id, started, session_id, parent_call_id, error=exc
|
||||||
|
)
|
||||||
return _FailedAttempt(exc, immediate=dead)
|
return _FailedAttempt(exc, immediate=dead)
|
||||||
finally:
|
finally:
|
||||||
await self._settle_and_release(permit)
|
await self._settle_and_release(permit)
|
||||||
@@ -474,9 +480,7 @@ class OcrClient:
|
|||||||
# 严禁静默用 MonkeyOcrTransport 打别家端点(默认值掩盖错误)
|
# 严禁静默用 MonkeyOcrTransport 打别家端点(默认值掩盖错误)
|
||||||
alien = sorted({s.provider for s in sources if s.provider != "monkey"})
|
alien = sorted({s.provider for s in sources if s.provider != "monkey"})
|
||||||
if alien:
|
if alien:
|
||||||
raise ValueError(
|
raise ValueError(f"OCR 装配仅支持 provider=monkey(D9 其余后端预留未实现): 发现 {alien}")
|
||||||
f"OCR 装配仅支持 provider=monkey(D9 其余后端预留未实现): 发现 {alien}"
|
|
||||||
)
|
|
||||||
return cls(
|
return cls(
|
||||||
scope=gw.scope,
|
scope=gw.scope,
|
||||||
sources=sources,
|
sources=sources,
|
||||||
|
|||||||
@@ -75,7 +75,11 @@ def _classify_status(
|
|||||||
exc: httpx.HTTPStatusError, source_name: str, operation: str
|
exc: httpx.HTTPStatusError, source_name: str, operation: str
|
||||||
) -> TransientError | SourceDeadError | RequestRejectedError:
|
) -> TransientError | SourceDeadError | RequestRejectedError:
|
||||||
status = exc.response.status_code
|
status = exc.response.status_code
|
||||||
ctx: dict[str, Any] = {"source_name": source_name, "status_code": status, "operation": operation}
|
ctx: dict[str, Any] = {
|
||||||
|
"source_name": source_name,
|
||||||
|
"status_code": status,
|
||||||
|
"operation": operation,
|
||||||
|
}
|
||||||
message = f"{source_name} OCR {operation} HTTP {status}"
|
message = f"{source_name} OCR {operation} HTTP {status}"
|
||||||
if status >= 500 or status == 429:
|
if status >= 500 or status == 429:
|
||||||
return TransientError(message, **ctx)
|
return TransientError(message, **ctx)
|
||||||
@@ -146,7 +150,9 @@ def _parse_pages(payload: object) -> tuple[list[OcrLayoutElement], list[tuple[fl
|
|||||||
return elements, page_sizes
|
return elements, page_sizes
|
||||||
|
|
||||||
|
|
||||||
def _parse_middle_json(zip_bytes: bytes) -> tuple[list[OcrLayoutElement], list[tuple[float, float]]]:
|
def _parse_middle_json(
|
||||||
|
zip_bytes: bytes,
|
||||||
|
) -> tuple[list[OcrLayoutElement], list[tuple[float, float]]]:
|
||||||
"""ZIP → (elements, page_sizes);一切形态异常归 ResultInvalid(坏图≠坏服务)。
|
"""ZIP → (elements, page_sizes);一切形态异常归 ResultInvalid(坏图≠坏服务)。
|
||||||
|
|
||||||
数值防御全量下沉自 CHS `_parse_table_result`(invokers.py:437-479),
|
数值防御全量下沉自 CHS `_parse_table_result`(invokers.py:437-479),
|
||||||
|
|||||||
@@ -249,14 +249,20 @@ class TestMiddleJsonDefense:
|
|||||||
self._expect_invalid(_zip_bytes(_middle_bytes([page])))
|
self._expect_invalid(_zip_bytes(_middle_bytes([page])))
|
||||||
|
|
||||||
def test_bbox_order_invalid(self):
|
def test_bbox_order_invalid(self):
|
||||||
self._expect_invalid(_zip_bytes(_middle_bytes([_page([_block("table", (218, 48, 41, 282))])])))
|
self._expect_invalid(
|
||||||
|
_zip_bytes(_middle_bytes([_page([_block("table", (218, 48, 41, 282))])]))
|
||||||
|
)
|
||||||
|
|
||||||
def test_bbox_int_degenerate(self):
|
def test_bbox_int_degenerate(self):
|
||||||
# float 合法但 int() 后宽度为零: 专为 CHS shim 的裁剪路径兜底
|
# float 合法但 int() 后宽度为零: 专为 CHS shim 的裁剪路径兜底
|
||||||
self._expect_invalid(_zip_bytes(_middle_bytes([_page([_block("table", (1.2, 1.2, 1.8, 5))])])))
|
self._expect_invalid(
|
||||||
|
_zip_bytes(_middle_bytes([_page([_block("table", (1.2, 1.2, 1.8, 5))])]))
|
||||||
|
)
|
||||||
|
|
||||||
def test_bbox_non_finite(self):
|
def test_bbox_non_finite(self):
|
||||||
self._expect_invalid(_zip_bytes(_middle_bytes([_page([_block("table", (1, 2, float("inf"), 4))])])))
|
self._expect_invalid(
|
||||||
|
_zip_bytes(_middle_bytes([_page([_block("table", (1, 2, float("inf"), 4))])]))
|
||||||
|
)
|
||||||
|
|
||||||
def test_type_missing(self):
|
def test_type_missing(self):
|
||||||
block = {"bbox": [1, 2, 30, 40], "index": 0}
|
block = {"bbox": [1, 2, 30, 40], "index": 0}
|
||||||
@@ -278,8 +284,13 @@ class TestMiddleJsonDefense:
|
|||||||
class TestErrorTranslation:
|
class TestErrorTranslation:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("status", "exc_type"),
|
("status", "exc_type"),
|
||||||
[(502, TransientError), (429, TransientError), (401, SourceDeadError),
|
[
|
||||||
(403, SourceDeadError), (404, RequestRejectedError)],
|
(502, TransientError),
|
||||||
|
(429, TransientError),
|
||||||
|
(401, SourceDeadError),
|
||||||
|
(403, SourceDeadError),
|
||||||
|
(404, RequestRejectedError),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
async def test_http_status(self, status, exc_type):
|
async def test_http_status(self, status, exc_type):
|
||||||
t = _transport_for(_routes(text_resp=httpx.Response(status)))
|
t = _transport_for(_routes(text_resp=httpx.Response(status)))
|
||||||
|
|||||||
@@ -234,9 +234,7 @@ class TestTerminalOutcomes:
|
|||||||
assert client._selector.outcomes == [] # 坏结果 ≠ 坏服务,不喂健康
|
assert client._selector.outcomes == [] # 坏结果 ≠ 坏服务,不喂健康
|
||||||
|
|
||||||
async def test_rejected_with_status_counts_no_attempt(self):
|
async def test_rejected_with_status_counts_no_attempt(self):
|
||||||
client, _, gate = _client(
|
client, _, gate = _client([_src()], [RequestRejectedError("parse failed", status_code=200)])
|
||||||
[_src()], [RequestRejectedError("parse failed", status_code=200)]
|
|
||||||
)
|
|
||||||
with pytest.raises(RequestRejectedError):
|
with pytest.raises(RequestRejectedError):
|
||||||
await client.parse_layout(b"jpg")
|
await client.parse_layout(b"jpg")
|
||||||
assert gate.successes == [("m1", False)]
|
assert gate.successes == [("m1", False)]
|
||||||
@@ -378,9 +376,7 @@ class TestAssembly:
|
|||||||
await client.aclose()
|
await client.aclose()
|
||||||
|
|
||||||
async def test_non_monkey_provider_rejected(self):
|
async def test_non_monkey_provider_rejected(self):
|
||||||
env = {
|
env = {k.replace("MONKEY", "GLM"): v for k, v in self._ENV.items()}
|
||||||
k.replace("MONKEY", "GLM"): v for k, v in self._ENV.items()
|
|
||||||
}
|
|
||||||
with pytest.raises(ValueError, match="monkey"):
|
with pytest.raises(ValueError, match="monkey"):
|
||||||
OcrClient.from_env("OCR", env=env)
|
OcrClient.from_env("OCR", env=env)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ def test_ocr_public_surface_exported():
|
|||||||
"""M3 OCR 公共 API 面(设计 §3;transport 结果与 Protocol 不出顶层)。"""
|
"""M3 OCR 公共 API 面(设计 §3;transport 结果与 Protocol 不出顶层)。"""
|
||||||
import polygateway
|
import polygateway
|
||||||
|
|
||||||
for name in ("OcrClient", "OcrSettings", "OcrTextResult", "OcrLayoutResult", "OcrLayoutElement"):
|
for name in (
|
||||||
|
"OcrClient",
|
||||||
|
"OcrSettings",
|
||||||
|
"OcrTextResult",
|
||||||
|
"OcrLayoutResult",
|
||||||
|
"OcrLayoutElement",
|
||||||
|
):
|
||||||
assert hasattr(polygateway, name), name
|
assert hasattr(polygateway, name), name
|
||||||
assert name in polygateway.__all__, name
|
assert name in polygateway.__all__, name
|
||||||
|
|||||||
Reference in New Issue
Block a user