docs: compress prose/module docstrings, em-dash->hyphen (de-bloat wave 1)

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-03 00:14:44 -04:00
parent 4f835ff003
commit 18646f313c
10 changed files with 40 additions and 104 deletions
+3 -3
View File
@@ -12,15 +12,15 @@ Small sync helpers shared across projects. Base is stdlib only — **no dependen
## Install ## Install
``` ```
commons @ git+ssh://git@git.rethinkstudios.io/rethink-public/commons.git@v0.3.2 commons @ git+ssh://git@git.rethinkstudios.io/rethink-public/commons.git@v0.3.3
# async address/geo lookups (fetch_ip / ip_location / fetch_location) need the extra: # async address/geo lookups (fetch_ip / ip_location / fetch_location) need the extra:
commons[addr] @ git+ssh://git@git.rethinkstudios.io/rethink-public/commons.git@v0.3.2 commons[addr] @ git+ssh://git@git.rethinkstudios.io/rethink-public/commons.git@v0.3.3
``` ```
The base install pulls **nothing** (stdlib). Only `commons[addr]` adds `aiohttp`, and The base install pulls **nothing** (stdlib). Only `commons[addr]` adds `aiohttp`, and
only for the geo lookups — the pure `commons.addr.ip` utilities ship in base. only for the geo lookups — the pure `commons.addr.ip` utilities ship in base.
Drop the `@v0.3.2` suffix from the line above to install the latest unpinned. Drop the `@v0.3.3` suffix from the line above to install the latest unpinned.
## timing ## timing
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "commons" name = "commons"
version = "0.3.2" version = "0.3.3"
description = "small stdlib-based sync helpers: time/timezone deltas, dotted-path dict access, display masking, ip/address tooling, and retry/backoff" description = "small stdlib-based sync helpers: time/timezone deltas, dotted-path dict access, display masking, ip/address tooling, and retry/backoff"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [] dependencies = []
+3 -18
View File
@@ -1,22 +1,7 @@
"""commons small sync helpers shared across projects. """commons - small sync helpers shared across projects: timing, paths, masking, addr, retry.
timing: unix-timestamp deltas + timezone-aware datetime conversions, bare
functions and a configurable Clock.
paths: nested dict/list access by dotted path (deep_get / deep_set); deep_set
mirrors deep_get's list indexing, so a get/set round-trip on the same
path never destroys a list.
masking: display masking for cards / cvv / tokens, plus url/proxy credential
redaction for logging (cosmetic, not a security control).
addr: ip/address tooling — pure stdlib ip utils (`commons.addr.ip`) in base,
async geo lookups (`commons.addr.geo`) behind the `commons[addr]` extra.
retry: exponential-backoff retry, sync (`retry`) and async (`aretry`), call or
decorator form; re-raises the last exception (fail loud), never swallows.
base is stdlib only, no dependencies (the addr geo lookups add aiohttp via the base is stdlib only, no dependencies (the addr geo lookups add aiohttp via the
extra). to toggle the timing test mode for bare calls, set it on the module — `commons[addr]` extra). see each submodule's docstring for its api.
`from commons import timing; timing.FAST_MODE = True` — or use `Clock(fast=True)`
for instance-scoped control. addr is exposed as a submodule (`from commons import
addr`); its ip helpers live under `commons.addr.ip` to keep top-level uncluttered.
""" """
from . import addr, masking, paths, timing from . import addr, masking, paths, timing
from .masking import credit, cvv, mask_proxy, mask_url, phantom, provider from .masking import credit, cvv, mask_proxy, mask_url, phantom, provider
@@ -68,4 +53,4 @@ __all__ = [
"aretry", "aretry",
] ]
__version__ = "0.3.2" __version__ = "0.3.3"
+6 -9
View File
@@ -1,13 +1,10 @@
"""addr ip/address tooling for commons. """addr - ip/address tooling for commons.
two concerns: `ip`: pure stdlib ipaddress utilities, ships in the base install, no dependencies.
- `ip` — pure stdlib ipaddress utilities (validation, membership, cidr shape, `geo`: async network lookups (aiohttp), gated behind the `commons[addr]` extra;
conversions). ships in the base install, no dependencies. importing this package is fine without it, but calling a geo function raises until
- `geo` — async network lookups (public ip, ip->geo, reverse geocode). needs it is installed. the geo.ipify api_key is always injected by the caller, never
aiohttp, gated behind the `commons[addr]` extra; importing this package is fine hardcoded.
without it, but calling a geo function raises until it is installed.
the geo.ipify api_key is always injected by the caller — nothing is hardcoded.
""" """
from . import geo, ip from . import geo, ip
from .geo import fetch_ip, fetch_location, ip_location from .geo import fetch_ip, fetch_location, ip_location
+7 -15
View File
@@ -1,15 +1,10 @@
"""async ip/geo network lookups (aiohttp, gated behind the [addr] extra). """async ip/geo network lookups (aiohttp, gated behind the [addr] extra).
importing this module without aiohttp is fine; only calling a lookup without it importing this module without aiohttp is fine; only calling a lookup without it
raises a clear RuntimeError. each call may reuse a caller-supplied aiohttp raises a clear RuntimeError. every lookup returns None on any request/parse
ClientSession (`session=`) or create and close one internally. failure (logged, never raised, never printed). the only secret is geo.ipify's
`api_key`, a REQUIRED keyword on `ip_location` - the caller injects it, nothing
url-building and json->dict parsing are pulled into pure helpers (`_*_url`, is hardcoded here.
`_parse_*`) so they unit-test without network. every lookup returns None on any
request/parse failure (logged, never raised, never printed).
security: the only secret is geo.ipify's `api_key`, which is a REQUIRED keyword on
`ip_location` — the caller injects it. nothing is hardcoded here.
""" """
import logging import logging
import unicodedata import unicodedata
@@ -59,11 +54,8 @@ def _parse_ipify(data: dict) -> Optional[str]:
def _state_slug(state) -> str: def _state_slug(state) -> str:
"""lowercase ascii-folded state slug with underscores (e.g. 'New York' -> 'new_york') """lowercase ascii-folded state slug with underscores (e.g. 'New York' -> 'new_york');
coerces to str first so a non-string `state` can't TypeError out of the parse contract"""
coerces to str first so a malformed non-string `state` doesn't raise TypeError out
of unicodedata.normalize and break the 'None on any parse failure' contract.
"""
folded = unicodedata.normalize("NFKD", str(state)).encode("ascii", "ignore").decode("ascii") folded = unicodedata.normalize("NFKD", str(state)).encode("ascii", "ignore").decode("ascii")
return folded.lower().replace(" ", "_") return folded.lower().replace(" ", "_")
@@ -121,7 +113,7 @@ async def ip_location(
"""look up geo data for an ip via geo.ipify; api_key is required and injected """look up geo data for an ip via geo.ipify; api_key is required and injected
returns the raw geo.ipify json dict, or None on failure. raises ValueError if returns the raw geo.ipify json dict, or None on failure. raises ValueError if
no api_key is supplied there is no default and nothing hardcoded. no api_key is supplied - there is no default and nothing hardcoded.
""" """
if not api_key: if not api_key:
raise ValueError("ip_location requires an api_key (inject it; never hardcode)") raise ValueError("ip_location requires an api_key (inject it; never hardcode)")
+5 -7
View File
@@ -1,12 +1,10 @@
"""pure ip/address utilities over stdlib ipaddress (no network, no deps). """pure ip/address utilities over stdlib ipaddress (no network, no deps).
every function accepts strings. validation/membership helpers validation/membership helpers (`is_valid`/`version`/`in_network`/`in_any`) never
(`is_valid`/`version`/`in_network`/`in_any`) never raise on bad input they return raise on bad input - they return a falsy value. functions that require a valid
a falsy value. functions that require a valid address or cidr (`to_int`/`set_bits`/ address or cidr (`to_int`/`set_bits`/the network-shape helpers) let `ValueError`
the network-shape helpers) let `ValueError` propagate so misuse is visible. propagate so misuse is visible. cidr parsing uses `ip_network(cidr, strict=False)`
throughout, so host bits set in the network string are tolerated (e.g. "10.0.0.5/24").
cidr parsing uses `ip_network(cidr, strict=False)` throughout, so host bits set in
the network string are tolerated (e.g. "10.0.0.5/24").
""" """
import ipaddress import ipaddress
from typing import List, Optional from typing import List, Optional
+5 -9
View File
@@ -1,11 +1,7 @@
"""display masking for sensitive-looking values. """display masking for sensitive-looking values (e.g. "•••• •••• •••• 1234").
these are DISPLAY helpers only — they format a value for showing in a UI or log DISPLAY only, not a security control: the underlying value is unchanged and still
(e.g. "•••• •••• •••• 1234"). they are not a security control: the underlying needs proper handling (encryption at rest, etc.).
value is unchanged and still needs proper handling (encryption at rest, etc.).
card helpers: `credit`, `cvv`, `provider`. token: `phantom`. url/proxy redaction
for logging connection strings: `mask_url`, `mask_proxy`.
""" """
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
@@ -35,7 +31,7 @@ def cvv(value: str) -> str:
def phantom(value: str) -> str: def phantom(value: str) -> str:
"""show a long token as first-six...last-four (e.g. a hash or id) """show a long token as first-six...last-four (e.g. a hash or id)
values of length <= 10 fully mask instead first6+last4 would otherwise values of length <= 10 fully mask instead - first6+last4 would otherwise
reveal (or double-reveal) every character while still looking masked. reveal (or double-reveal) every character while still looking masked.
""" """
if len(value) <= 10: if len(value) <= 10:
@@ -46,7 +42,7 @@ def phantom(value: str) -> str:
def provider(card_number: str) -> str: def provider(card_number: str) -> str:
"""short card brand from the number's prefix, or UNKW if undetermined """short card brand from the number's prefix, or UNKW if undetermined
a BIN-prefix heuristic for display/labeling not authoritative validation. a BIN-prefix heuristic for display/labeling - not authoritative validation.
tolerates spaces/dashes and short or non-numeric input (returns UNKW). tolerates spaces/dashes and short or non-numeric input (returns UNKW).
""" """
n = _digits(card_number) n = _digits(card_number)
+1 -9
View File
@@ -1,12 +1,4 @@
"""nested access by dotted path. """nested dict/list access by dotted path (deep_get / deep_set)."""
`deep_get(data, "in.this.old.notation")` walks dicts (and lists, when a segment is
a number) and returns a default instead of raising on a missing/!wrong path.
`deep_set` writes a nested value, creating intermediate dicts and indexing into
existing lists on numeric segments (mirroring deep_get), so a get/set round-trip
on the same path never corrupts a list. an out-of-range numeric segment raises
IndexError rather than silently mis-storing.
"""
from typing import Any from typing import Any
_MISSING = object() _MISSING = object()
+3 -21
View File
@@ -1,25 +1,7 @@
"""retry with exponential backoff sync and async, one backoff engine. """retry with exponential backoff, sync (`retry`) and async (`aretry`), call or decorator form.
de-duplicates retry logic that was written divergently across several libs (HTTP
429/5xx caps, proxy burn/rotate caps, IMAP reconnects). both a call form and a
decorator form share one implementation per flavor; the backoff schedule is a pure
generator so it tests without real sleeps.
from commons import retry, aretry
# call form
rows = retry(lambda: db_read(), attempts=5, on=(IOError,))
data = await aretry(lambda: fetch(url), attempts=3, on=(TimeoutError,))
# decorator form (same kwargs)
@aretry(attempts=4, backoff=0.5, on=(ConnectionError,))
async def pull():
...
after `attempts` are exhausted the LAST exception is re-raised (fail loud, never after `attempts` are exhausted the LAST exception is re-raised (fail loud, never
swallowed). `on` narrows which exceptions retry; `give_up(exc) -> bool` stops early swallowed). see README for usage examples.
on a non-retryable error (e.g. a 400 vs a 429). each retry is logged (emit-only),
never printed.
""" """
import asyncio import asyncio
@@ -38,7 +20,7 @@ def _delays(attempts: int, backoff: float, factor: float, max_backoff: float):
"""yield the wait before each retry: min(backoff * factor**n, max_backoff) """yield the wait before each retry: min(backoff * factor**n, max_backoff)
yields `attempts - 1` delays (one before each retry after the first try). the yields `attempts - 1` delays (one before each retry after the first try). the
raw, un-jittered schedule jitter is applied at call time so the schedule stays raw, un-jittered schedule - jitter is applied at call time so the schedule stays
pure and testable. pure and testable.
""" """
for n in range(max(0, attempts - 1)): for n in range(max(0, attempts - 1)):
+6 -12
View File
@@ -1,22 +1,16 @@
"""time helpers built on unix timestamps with timezone-aware datetime support. """time helpers built on unix timestamps with timezone-aware datetime support.
one engine, two ergonomics: one engine, two ergonomics: bare module functions for stateless unix-int math
- bare module functions (now/add/ahead/ago/is_expired/to_dt/...) operate on unix (all delta math routes through `_delta_seconds`, the single source of truth), and
ints for quick, stateless use. all delta math routes through `_delta_seconds`. `Clock`, which holds a timezone + fast flag and delegates to the same functions.
- `Clock` holds a timezone + fast flag and delegates to the same functions, so a
configured clock and the bare functions never diverge.
unix ints stay the storable value; datetimes are produced on demand in whatever
timezone you ask for (stdlib zoneinfo, no pytz).
""" """
import time as _time import time as _time
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Optional, Union from typing import Optional, Union
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
# default fast flag for bare module calls. when True, every unit (day/hour/minute) # default fast flag for bare calls (test-only: collapses each unit to 1s); set
# counts as one second so time-based flows run fast in tests. set # `commons.timing.FAST_MODE` from test setup, off by default, not a config import.
# `commons.timing.FAST_MODE` from test setup; off by default. not a config import.
FAST_MODE = False FAST_MODE = False
UTC = timezone.utc UTC = timezone.utc
@@ -42,7 +36,7 @@ def now() -> int:
def _delta_seconds(days: int, hours: int, minutes: int, seconds: int, fast: bool) -> int: def _delta_seconds(days: int, hours: int, minutes: int, seconds: int, fast: bool) -> int:
"""seconds for a unit combination; collapses to 1s/unit when fast is True """seconds for a unit combination; collapses to 1s/unit when fast is True
single source of truth for unit math every delta helper routes through here. single source of truth for unit math - every delta helper routes through here.
""" """
if fast: if fast:
return days + hours + minutes + seconds return days + hours + minutes + seconds