Add QCEW CSV module, ECEC breakdown, response caching + typed errors
Item 1 — QCEW county/industry detail: - new bls_client/qcew.py: thin client over the QCEW Open Data CSV service (area/industry/size slices; no API key, no quota), with filter_rows helper - covers the county/industry detail the timeseries API can't reach Item 2 — ECEC benefit breakdown: - new series.ecec() builder from the authoritative cm.estimate decode table - real helpers: wages, total benefits, paid leave, supplemental pay, health insurance, retirement & savings, legally required + ecec_dashboard() - FIX: ecec_total_benefits was CMU1036... (education/health industries only); correct all-civilian total benefits is CMU1030000000000D Item 3 — caching + typed errors: - bls_client/cache.py FileCache (on-disk, TTL); BLSClient(cache=True), queries_used counter (cache hits don't spend quota) - bls_client/errors.py: BLSError / BLSQuotaError / BLSRequestError; quota exhaustion now raises a clear BLSQuotaError instead of a generic RuntimeError Tests: 119 offline (+24) incl. ECEC goldens, QCEW fixture parse, cache/quota behavior; live smoke extended to ECEC + QCEW. Helper sweep: 96/96 live. README: caching/QCEW usage, updated coverage + limitations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
23
bls_client/errors.py
Normal file
23
bls_client/errors.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Typed exceptions for the BLS client.
|
||||
|
||||
Lets callers distinguish "you're throttled" from "your request was bad" — the
|
||||
two failure modes the old generic RuntimeError conflated.
|
||||
"""
|
||||
|
||||
|
||||
class BLSError(RuntimeError):
|
||||
"""Base class for all BLS API errors."""
|
||||
|
||||
def __init__(self, message, *, status=None, messages=None):
|
||||
super().__init__(message)
|
||||
self.status = status # the API "status" field, e.g. REQUEST_FAILED
|
||||
self.messages = messages or [] # the API "message" list, verbatim
|
||||
|
||||
|
||||
class BLSRequestError(BLSError):
|
||||
"""The API rejected the request (malformed series ID, bad year range, etc.)."""
|
||||
|
||||
|
||||
class BLSQuotaError(BLSError):
|
||||
"""The daily query threshold was exceeded (500/day for a registered key,
|
||||
25/day unregistered). Resets at midnight Eastern."""
|
||||
Reference in New Issue
Block a user