Commit Graph

5 Commits

Author SHA1 Message Date
449f790571 fix: guard geo parse helpers against non-dict JSON; de-dup timeout build (v0.2.3)
- _parse_ipify/_parse_reverse return None on a truthy non-dict body instead of raising
  AttributeError, honoring the documented 'None on any parse failure' contract (L9)
- build the geo request ClientTimeout once instead of twice (nit)
- drop the stale 'live proxy region- contract' wording from _state_slug's docstring (nit).

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-29 17:57:54 -04:00
a5b91bed0d fix: state slug uses underscore + ascii-fold to match the proxy region- contract
addr.geo.fetch_location produced a hyphen slug with no unicode fold (new-york / québec), but the live proxy region- token the original utils.py fed expects underscore + ascii-fold (new_york / quebec). a multi-word state silently routed to an unrecognized region with no error. added _state_slug (NFKD ascii-fold, lowercase, spaces->underscore) and routed _parse_reverse through it. bump to v0.2.2.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-29 03:25:14 -04:00
c6e3dd1b54 fix: floor retry/aretry attempts at 1 (v0.2.1)
retry(fn, attempts=0) (or negative) silently returned None without ever calling fn,
looking like success. floor attempts at max(1, attempts) so the callable always runs
at least once; a failing call now fails loud after one try instead of no-op None.

verified: attempts=0/-5 -> 1 call (sync + async); failing fn raises after 1 try;
31/31 retry regression intact.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-28 16:11:34 -04:00
0939917172 feat: retry/backoff module (commons v0.2.0)
add commons.retry: exponential-backoff retry as sync `retry` and async `aretry`,
each usable as a call form or a decorator. backoff is min(backoff*factor**n,
max_backoff) with optional full jitter; the schedule is a pure generator so it
tests without real sleeps (sleep + rand injectable). `on=` narrows retryable
exception types, `give_up(exc)` stops early on a non-retryable error, and after
attempts are exhausted the LAST exception is re-raised (fail loud, never swallowed).

de-dups retry logic written 3x divergently (aiowebhooks 429/5xx, aioproxies
burn/rotate, aiomail reconnect).

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-27 21:33:55 -04:00
7437e1feb0 add package: pyproject + src
commons: stdlib-only sync helpers shared across projects.
- timing: unix-ts deltas + tz datetime conversions (one _delta_seconds
  engine), a configurable Clock, fast-mode for tests.
- paths: deep_get / deep_set by dotted path (numeric segments index lists).
- masking: display masking for cards/cvv/tokens (provider covers
  VISA/MC/AMEX/UPAY/DISC/JCB/DNRS).
- addr: ip/address tooling — pure stdlib ipaddress utils in base, async
  geo lookups (ipify/geo.ipify/nominatim) behind the [addr] aiohttp extra;
  geo.ipify api_key is injected, never hardcoded.
config-free, emit-only logging, base is zero-deps. src/ layout, hatchling.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-24 22:18:37 -04:00