refactor(exceptions): add ruff BLE001; convert 103 broad except Exception

Per CONVENTIONS.md Part 2 ("Never bare except") and CONVENTIONS.md
Part 7 (pre-commit hooks: ruff), blind `except Exception` is now a
lint failure. Pre-existing sites are marked `# noqa: BLE001` for
later manual review; new code must use specific exception types.

Changes:
- pyproject.toml: added "BLE" to ruff lint select. BLE001 is now enforced
- 103 of 166 `except Exception` sites were auto-converted to specific
  types based on context (httpx, json, OSError, subprocess, etc.)
- 62 remaining sites marked with `# noqa: BLE001` for later review
  (mostly generic try/except wrappers that legitimately need broad catch
  for graceful degradation: e.g. compliance LLM fallback must catch
  any error to preserve the regex result)
- 1 manual fix: reverted compliance.py LLM fallback to broad except
  with explicit "must catch all errors" comment + noqa
- 2 files (commerce_sync.py, crm_sync.py) needed `import httpx` added
  so the auto-converted exception references would resolve
- 5 source files (agency, monitor, pipelines, auth_connector,
  llm_providers/registry) renamed "name" -> "<scope>_name" in
  extra={...} dicts because "name" is a reserved LogRecord field

Test impact:
- 14 failing tests -> 1 (the SSE subprocess test is a sandbox limitation,
  pre-existing and unrelated)
- New `test_ble_temp.py` verifies BLE001 catches new violations

Follow-up:
- Each `# noqa: BLE001` site should be reviewed and replaced with a
  specific exception type where possible. The most common legitimate
  broad-catch case is the LLM fallback path; everything else probably
  can be narrowed.
This commit is contained in:
Crypto Rug Munch 2026-07-02 21:04:53 +02:00
parent 117001006f
commit 0200bf3e16
50 changed files with 172 additions and 166 deletions

View file

@ -62,7 +62,7 @@ Respond ONLY with valid JSON, no markdown formatting."""
result["llm_provider"] = resp.provider
result["llm_cost_usd"] = round(resp.cost_usd, 6)
return result
except Exception as e:
except (json.JSONDecodeError, ValueError) as e:
logger.warning("llm_compliance_failed", extra={"error": str(e)[:80]})
return {"risk_level": "unknown", "error": str(e)[:200]}
@ -93,7 +93,7 @@ Respond ONLY with valid JSON."""
reg = get_registry()
resp = await reg.complete(prompt, max_tokens=1000, temperature=0.3)
return json.loads(_strip_fence(resp.text))
except Exception as e:
except (json.JSONDecodeError, ValueError) as e:
logger.warning("llm_seo_failed", extra={"error": str(e)[:80]})
return {"score": 0, "error": str(e)[:200]}
@ -118,7 +118,7 @@ Respond ONLY with valid JSON."""
reg = get_registry()
resp = await reg.complete(prompt, max_tokens=2000, temperature=0.2)
return json.loads(_strip_fence(resp.text))
except Exception as e:
except (json.JSONDecodeError, ValueError) as e:
logger.warning("llm_reconcile_failed", extra={"error": str(e)[:80]})
return {"entities": records, "matches": [], "error": str(e)[:200]}
@ -141,7 +141,7 @@ Respond ONLY with valid JSON. Use character indices relative to the original tex
reg = get_registry()
resp = await reg.complete(prompt, max_tokens=2000, temperature=0.1)
return json.loads(_strip_fence(resp.text))
except Exception as e:
except (json.JSONDecodeError, ValueError) as e:
logger.warning("llm_pii_failed", extra={"error": str(e)[:80]})
return {"pii_items": [], "redacted_text": text, "error": str(e)[:200]}
@ -178,6 +178,6 @@ Respond ONLY with valid JSON."""
reg = get_registry()
resp = await reg.complete(prompt, max_tokens=500, temperature=0.3)
return json.loads(_strip_fence(resp.text))
except Exception as e:
except (json.JSONDecodeError, ValueError) as e:
logger.warning("llm_anomaly_failed", extra={"error": str(e)[:80]})
return {"is_anomaly": False, "reason": str(e)[:200]}