Items discovered during the enterprise-grade review (2026-04-02). Agents: Security, Architecture, Code Quality, Refactor/Dead Code.
- C1
models.pysplit intomodels/subpackage:_monitor_models.py,_incident_models.py,_maintenance_models.py,_statuspage_models.py,_outage_models.py. Re-exported viamodels/__init__.py— zero public API breakage. - C2
_requesthelpers extracted:_compute_sleep_time(response, delay)and_should_retry(status_code, attempt)(client.py). - C3
_handle_response_errorhelpers extracted:_parse_error_body(response)and_parse_retry_after(response)(client.py). - C4
_remap_legacy_fieldsandMonitor.__init__no longer mutate input — both operate on{**data}copies.MonitorCreateuses@model_validator(mode="before")for clean remapping. - C5
Outagemodel defined inmodels/_outage_models.pywithextra="ignore",frozen=True.list_outages()now returnslist[Outage]. - C6 Shadow
from datetime import datetime as dtinsideMaintenance.is_active()removed. Uses module-leveldatetimeimport directly.
- H1
_requestreturn type corrected todict[str, Any] | list[dict[str, Any]]. - H2
_parse_list(raw, model_cls, label)extracted to_utils.py; used by all 5 mixin list methods (~65 lines eliminated). - H3
_unwrap_list(response, key)extracted to_utils.py; used by all 5 mixins. - H4
_ClientProtocol(Protocol)defined in_protocols.py; all mixin classes inherit from it, eliminating 5# type: ignore[empty-body]stubs. - H5 Internal symbols (
EndpointConfig,ENDPOINTS,get_endpoint_url,get_version_for_endpoint,API_PATHS,HYPERPING_API_BASE) removed from__all__. Deprecated symbolsHYPERPING_API_BASEandAPI_PATHSemitDeprecationWarningvia__getattr__. - H6
update_monitorandupdate_maintenancedocument the race condition in docstrings;raise_on_conflict: bool = Falseparameter added as ETag placeholder. - H7
get_monitor_reportdocuments O(n) fetch cost in docstring; notes themonitor_uuid=query param to check for. - H8
_validate_id(value, name)helper in_utils.pycalls before every f-string URL build in all 5 mixins. - H9 Bare
except Exceptionnarrowed toexcept (ValueError, httpx.DecodingError)in_parse_error_body. - H10
response_bodyrisk documented. ForHyperpingAuthError,response_body=Noneto prevent token leakage through observability stacks. - H11 GitHub Actions workflows need pinning to full 40-char commit SHAs (supply chain risk). TODO comments added in both workflow files. Requires manual SHA lookup per release tag.
- H8 (see above)
- H9 (see above)
- H10 (see above)
- M1 Async client (
AsyncHyperpingClient) — shipped in PR #13 (feature/sdk-py-03-async-client). - M2 Pagination (
pageparam,hasNextPageauto-pagination viacollect_all_pages/collect_all_pages_async) — shipped in PR #12 (merged) and PR #13. - M3 Per-endpoint circuit breaker (
per_endpoint_circuit_breaker: bool = Falseoption) onHyperpingClientandAsyncHyperpingClient. Per-path state viacircuit_breaker_state_for(path). Default off; existing single-shared-breaker behaviour preserved. - M4
MonitorCreatenow has@model_validator(mode="after")that raisesValueErrorif DNS fields are set on non-DNS monitors. - M5
MonitorListResponseis in__all__— retained but documented as not returned by any client method. Will be used once pagination lands. - M6
APIErrorResponseremoved from__all__(documented as intentionally internal in comment). - M7
DEFAULT_RETRY_CONFIG/DEFAULT_CIRCUIT_BREAKER_CONFIG— added explicit# intentionally internalcomment in_circuit_breaker.py. - M8
ping()docstring clarified: explicitly states it fetches the monitor list and suggests using a dedicated/healthendpoint if available.
- M9
periodparam inget_all_reports/get_monitor_reporttypedLiteral[...]+ValueErrorguard. - M10
add_subscribervalidates email format with_EMAIL_REbefore sending to API; raisesValueErroron mismatch. - M11
urlfield URL scheme validation deferred: HTTP monitors use URLs but DNS/ICMP/port monitors use hostnames/IPs, requiring protocol-aware cross-field validation. Deferred — implement alongside M4 long-term discriminated-union work. - M12 DateTime coercion for
start_date,end_date,datefields — deferred, breaking change requires semver bump. The fragile.replace("Z", "+00:00")workaround remains; a future 0.2.0 migration guide should cover this. - M13
CircuitBreaker.statereturn type changed toCircuitState(wasstr). - M14
CircuitBreaker.stateandfailure_countreads now hold_lock.
- M14 (see above)
- M15 Debug log sanitizes
jsonandparamsdicts before logging;_SENSITIVE_LOG_KEYSredacts known sensitive field names.
- M16
CircuitBreaker+ configs extracted to_circuit_breaker.py; re-exported fromclient.pyfor backward compat. - M17 All test files migrated from
HYPERPING_API_BASE + API_PATHS[...]toAPI_BASE + Endpoint.*. - M18
_incidents_mixin.pyuses canonicalIncidentUpdateTypeandAddIncidentUpdateRequest(legacy aliases removed). - M19
_MONITOR_WRITABLE_FIELDSmoved to module-levelfrozensetconstant in_monitors_mixin.py. - M20
params if params else Nonesimplified toparams or Nonein_incidents_mixin.pyand_maintenance_mixin.py.
- M21
update_incidenttests added:test_update_incident_changes_titleandtest_update_incident_not_foundintest_incidents.py. - M22
update_monitor,pause_monitor,resume_monitortargeted tests added totest_monitors.py. - M23
get_all_reports/get_monitor_reporttests added includingoutages.detailsnested list parsing. - M24
conftest.pyclientfixture converted toyield-based; callsclient.close()after each test. - M25 Request models (
MonitorCreate,IncidentCreate, etc.) validated as mutable intest_sdk_surface.py. Deliberate exception to immutability rule — request models that accept legacy field remapping via__init__cannot easily be frozen. Annotated in test. Revisit if/when__init__remapping is replaced by@model_validator.
- L1
LocalizedText.get(lang, default="")accessor method added (alongside C1 split). - L2 f-string logging replaced with
%-style args in all mixin and client files. - L3
IncidentStatus/IncidentUpdateCreatelegacy aliases now emitDeprecationWarningviamodels/__init__.__getattr__; removal planned for v0.3.0. - L4
ci.ymlnow haspermissions: { contents: read }at job level. - L5
uv auditstep added to bothci.ymlandpublish.yml(pip-auditfallback for compatibility). - L6 Dependency bounds narrowed:
httpx>=0.27,<1.0andpydantic>=2.0,<3.0. - L7 Circuit breaker error message now references
recovery_timeout(was incorrectly referencingretry_config.initial_delay).
The following items require either a semver bump, a separate PR, or manual work:
M1 Async client— shippedM2 Pagination— shippedM3 Per-endpoint circuit breaker option— shipped- M11 URL validation for HTTP-protocol monitors (cross-field, needs discriminated union work)
- M12 DateTime coercion (breaking change — v0.2.0)
- H11 Pin all GitHub Actions
uses:to 40-char commit SHAs (requires per-tag SHA lookup) - M25 Frozen request models (revisit after
@model_validatorremapping is complete)