Summary
The sync and async clients share request-building/error semantics via datamaxi/_dispatch.py, but the response-shaping layer is hand-copied. Param-building + DataFrame shaping is duplicated ~verbatim between sync and async resources, so every schema/shape tweak must be applied twice and can silently drift.
Examples
datamaxi/resources/premium.py:85-173 vs datamaxi/aio/premium.py:41-128 — ~90 near-identical lines.
- Same pattern for candle/ticker DataFrame logic.
- Retry logic also diverges: sync uses urllib3
Retry (GET-only, exponential backoff, honors Retry-After), async hand-rolls a loop (aio/_core.py:72-80) with linear backoff, all methods, ignoring Retry-After — a real behavior difference, not just duplication.
Fix direction
Extract the transport-agnostic response-shaping (param assembly, envelope handling, DataFrame conversion decision) into a shared helper the way _dispatch already shares request-building — leaving only the await/transport-specific glue in each client. Align the async retry policy with the sync one (or share a single policy description).
Impact
Highest maintainability risk after the two quick wins (py.typed, dispatch fallback bug). Watch for import-cycle risk when extracting; use a registry/helper module if needed.
Surfaced during a pythonic-idiom review of the SDK.
Summary
The sync and async clients share request-building/error semantics via
datamaxi/_dispatch.py, but the response-shaping layer is hand-copied. Param-building + DataFrame shaping is duplicated ~verbatim between sync and async resources, so every schema/shape tweak must be applied twice and can silently drift.Examples
datamaxi/resources/premium.py:85-173vsdatamaxi/aio/premium.py:41-128— ~90 near-identical lines.Retry(GET-only, exponential backoff, honorsRetry-After), async hand-rolls a loop (aio/_core.py:72-80) with linear backoff, all methods, ignoringRetry-After— a real behavior difference, not just duplication.Fix direction
Extract the transport-agnostic response-shaping (param assembly, envelope handling, DataFrame conversion decision) into a shared helper the way
_dispatchalready shares request-building — leaving only theawait/transport-specific glue in each client. Align the async retry policy with the sync one (or share a single policy description).Impact
Highest maintainability risk after the two quick wins (py.typed, dispatch fallback bug). Watch for import-cycle risk when extracting; use a registry/helper module if needed.
Surfaced during a pythonic-idiom review of the SDK.