From 18646f313c3d1e742828728caa13af0ab9c90b04 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Fri, 3 Jul 2026 00:14:44 -0400 Subject: [PATCH] docs: compress prose/module docstrings, em-dash->hyphen (de-bloat wave 1) Signed-off-by: disqualifier --- README.md | 6 +++--- pyproject.toml | 2 +- src/commons/__init__.py | 21 +++------------------ src/commons/addr/__init__.py | 15 ++++++--------- src/commons/addr/geo.py | 22 +++++++--------------- src/commons/addr/ip.py | 12 +++++------- src/commons/masking.py | 14 +++++--------- src/commons/paths.py | 10 +--------- src/commons/retry.py | 24 +++--------------------- src/commons/timing.py | 18 ++++++------------ 10 files changed, 40 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index b44e960..55f418e 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,15 @@ Small sync helpers shared across projects. Base is stdlib only — **no dependen ## 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: -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 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 diff --git a/pyproject.toml b/pyproject.toml index 80364bb..fec0281 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] 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" requires-python = ">=3.10" dependencies = [] diff --git a/src/commons/__init__.py b/src/commons/__init__.py index 003e83d..b451640 100644 --- a/src/commons/__init__.py +++ b/src/commons/__init__.py @@ -1,22 +1,7 @@ -"""commons — small sync helpers shared across projects. - -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. +"""commons - small sync helpers shared across projects: timing, paths, masking, addr, retry. 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 — -`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. +`commons[addr]` extra). see each submodule's docstring for its api. """ from . import addr, masking, paths, timing from .masking import credit, cvv, mask_proxy, mask_url, phantom, provider @@ -68,4 +53,4 @@ __all__ = [ "aretry", ] -__version__ = "0.3.2" +__version__ = "0.3.3" diff --git a/src/commons/addr/__init__.py b/src/commons/addr/__init__.py index f61ef80..80b31a8 100644 --- a/src/commons/addr/__init__.py +++ b/src/commons/addr/__init__.py @@ -1,13 +1,10 @@ -"""addr — ip/address tooling for commons. +"""addr - ip/address tooling for commons. -two concerns: -- `ip` — pure stdlib ipaddress utilities (validation, membership, cidr shape, - conversions). ships in the base install, no dependencies. -- `geo` — async network lookups (public ip, ip->geo, reverse geocode). needs - aiohttp, gated behind the `commons[addr]` extra; importing this package is fine - 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. +`ip`: pure stdlib ipaddress utilities, ships in the base install, no dependencies. +`geo`: async network lookups (aiohttp), gated behind the `commons[addr]` extra; +importing this package is fine without it, but calling a geo function raises until +it is installed. the geo.ipify api_key is always injected by the caller, never +hardcoded. """ from . import geo, ip from .geo import fetch_ip, fetch_location, ip_location diff --git a/src/commons/addr/geo.py b/src/commons/addr/geo.py index fab1ddf..e1dabb2 100644 --- a/src/commons/addr/geo.py +++ b/src/commons/addr/geo.py @@ -1,15 +1,10 @@ """async ip/geo network lookups (aiohttp, gated behind the [addr] extra). 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 -ClientSession (`session=`) or create and close one internally. - -url-building and json->dict parsing are pulled into pure helpers (`_*_url`, -`_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. +raises a clear RuntimeError. every lookup returns None on any request/parse +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 +is hardcoded here. """ import logging import unicodedata @@ -59,11 +54,8 @@ def _parse_ipify(data: dict) -> Optional[str]: def _state_slug(state) -> str: - """lowercase ascii-folded state slug with underscores (e.g. 'New York' -> 'new_york') - - 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. - """ + """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""" folded = unicodedata.normalize("NFKD", str(state)).encode("ascii", "ignore").decode("ascii") 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 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: raise ValueError("ip_location requires an api_key (inject it; never hardcode)") diff --git a/src/commons/addr/ip.py b/src/commons/addr/ip.py index 4b5abfd..2aabe08 100644 --- a/src/commons/addr/ip.py +++ b/src/commons/addr/ip.py @@ -1,12 +1,10 @@ """pure ip/address utilities over stdlib ipaddress (no network, no deps). -every function accepts strings. validation/membership helpers -(`is_valid`/`version`/`in_network`/`in_any`) never raise on bad input — they return -a falsy value. functions that require a valid address or cidr (`to_int`/`set_bits`/ -the network-shape helpers) let `ValueError` 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"). +validation/membership helpers (`is_valid`/`version`/`in_network`/`in_any`) never +raise on bad input - they return a falsy value. functions that require a valid +address or cidr (`to_int`/`set_bits`/the network-shape helpers) let `ValueError` +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"). """ import ipaddress from typing import List, Optional diff --git a/src/commons/masking.py b/src/commons/masking.py index 47a5613..0cf1ea6 100644 --- a/src/commons/masking.py +++ b/src/commons/masking.py @@ -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 -(e.g. "•••• •••• •••• 1234"). they are not a security control: the underlying -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`. +DISPLAY only, not a security control: the underlying value is unchanged and still +needs proper handling (encryption at rest, etc.). """ from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit @@ -35,7 +31,7 @@ def cvv(value: str) -> str: def phantom(value: str) -> str: """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. """ if len(value) <= 10: @@ -46,7 +42,7 @@ def phantom(value: str) -> str: def provider(card_number: str) -> str: """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). """ n = _digits(card_number) diff --git a/src/commons/paths.py b/src/commons/paths.py index ea19fca..7176c72 100644 --- a/src/commons/paths.py +++ b/src/commons/paths.py @@ -1,12 +1,4 @@ -"""nested access by dotted path. - -`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. -""" +"""nested dict/list access by dotted path (deep_get / deep_set).""" from typing import Any _MISSING = object() diff --git a/src/commons/retry.py b/src/commons/retry.py index 4d852a5..8da42b9 100644 --- a/src/commons/retry.py +++ b/src/commons/retry.py @@ -1,25 +1,7 @@ -"""retry with exponential backoff — sync and async, one backoff engine. - -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(): - ... +"""retry with exponential backoff, sync (`retry`) and async (`aretry`), call or decorator form. 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 -on a non-retryable error (e.g. a 400 vs a 429). each retry is logged (emit-only), -never printed. +swallowed). see README for usage examples. """ 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) 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. """ for n in range(max(0, attempts - 1)): diff --git a/src/commons/timing.py b/src/commons/timing.py index fe486ed..35e5b69 100644 --- a/src/commons/timing.py +++ b/src/commons/timing.py @@ -1,22 +1,16 @@ """time helpers built on unix timestamps with timezone-aware datetime support. -one engine, two ergonomics: -- bare module functions (now/add/ahead/ago/is_expired/to_dt/...) operate on unix - ints for quick, stateless use. all delta math routes through `_delta_seconds`. -- `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). +one engine, two ergonomics: bare module functions for stateless unix-int math +(all delta math routes through `_delta_seconds`, the single source of truth), and +`Clock`, which holds a timezone + fast flag and delegates to the same functions. """ import time as _time from datetime import datetime, timezone from typing import Optional, Union from zoneinfo import ZoneInfo -# default fast flag for bare module calls. when True, every unit (day/hour/minute) -# 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. +# default fast flag for bare calls (test-only: collapses each unit to 1s); set +# `commons.timing.FAST_MODE` from test setup, off by default, not a config import. FAST_MODE = False UTC = timezone.utc @@ -42,7 +36,7 @@ def now() -> 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 - 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: return days + hours + minutes + seconds