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:12:54 -04:00
parent 147341d38a
commit 84127a93ff
5 changed files with 40 additions and 76 deletions
+8 -3
View File
@@ -9,15 +9,15 @@ edits. **Credentials are always injected — never hardcoded.**
## Install ## Install
``` ```
aioproxies @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioproxies.git@v0.3.0 aioproxies @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioproxies.git@v0.3.1
# network helpers (current_ip / reset) need the extra: # network helpers (current_ip / reset) need the extra:
aioproxies[net] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioproxies.git@v0.3.0 aioproxies[net] @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioproxies.git@v0.3.1
``` ```
The core has no dependencies. The `net` extra adds `aiohttp` for `current_ip` / The core has no dependencies. The `net` extra adds `aiohttp` for `current_ip` /
`reset`. `reset`.
Drop the `@v0.3.0` suffix from the line above to install the latest unpinned. Drop the `@v0.3.1` suffix from the line above to install the latest unpinned.
## Formatting ## Formatting
@@ -209,6 +209,11 @@ await reset("https://provider/reset-url") # rotate upstream ip
## Changelog ## Changelog
### v0.3.1
- **Docs-only de-bloat pass.** Compressed module/internal docstrings and comments,
replaced mojibake em-dashes with plain hyphens in source. No behavior change.
### v0.3.0 ### v0.3.0
- **Stored-shape change: int unix deadlines.** `burn(proxy, seconds)` and the - **Stored-shape change: int unix deadlines.** `burn(proxy, seconds)` and the
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "aioproxies" name = "aioproxies"
version = "0.3.0" version = "0.3.1"
description = "proxy parsing, formatting, health, and pool management for aiohttp/aioweb, camoufox, and socks5" description = "proxy parsing, formatting, health, and pool management for aiohttp/aioweb, camoufox, and socks5"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [] dependencies = []
+2 -7
View File
@@ -1,9 +1,4 @@
"""aioproxies proxy parsing, formatting, and source management. """aioproxies - proxy parsing, formatting, and source management. see README for usage."""
renders proxies for aiohttp/aioweb, camoufox, and socks5; manages session
templates (with caller-supplied fields like country/ttl), rotating lists, or a
static proxy. credentials are always injected, never hardcoded.
"""
from .manager import AioProxies, ProxiesExhaustedError, ProxyManager, aioproxies from .manager import AioProxies, ProxiesExhaustedError, ProxyManager, aioproxies
from .proxy import Proxy, canonical_key, parse, to_proxy from .proxy import Proxy, canonical_key, parse, to_proxy
@@ -18,4 +13,4 @@ __all__ = [
"to_proxy", "to_proxy",
] ]
__version__ = "0.3.0" __version__ = "0.3.1"
+18 -43
View File
@@ -1,31 +1,19 @@
"""proxy source management: session templates, rotation, static. """proxy source management: session templates, rotation, static.
`AioProxies` is constructed with exactly one source and hands out `Proxy` objects. `AioProxies` takes exactly one source (template / proxies / static) and hands out
no module-level globals (rotation state is per-instance); a missing proxy file `Proxy` objects via `next()`/`get()`. no module-level globals; a missing proxy file
raises, never `sys.exit`. raises, never `sys.exit`. see README for the full source/method reference.
sources: proxy health (rotating list source only) is keyed by each proxy's canonical key
- template: a format string. `{session}` is filled with a fresh id on each (`host:port:user:pass`, or `host:port` auth-less; port is normalized so `:080` and
`next()`; other placeholders (`{country}`, `{ttl}`, ...) come from `:80` collapse to one key). on template/static sources health/pool methods are
`next(**fields)`. a bare `{}` is treated as the session slot unless escaped as no-ops that log a warning and return, so generic caller code can call them
`{{}}` (back-compat with simple templates). regardless of source.
- proxies: a list of specs cycled round-robin (deduped by canonical key)
- static: one fixed proxy
proxy health (rotating list source only) — burn/timeout, usage counters, reuse per-proxy `timeout` (the availability field) is shared by cooldown and timed
cooldown, pool edits (replace/add/remove) — keyed by each proxy's canonical key burns: `None`/`0` fine, `-1` dead/permanent, a future unix ts = timed out until
(`host:port:user:pass`, or `host:port` auth-less; port is normalized so `:080` then; durations only ever exist as call arguments, converted to `now + seconds`
and `:80` collapse to one key). on template/static sources these are no-ops that and discarded, never stored raw.
log a warning and return, so generic caller code can call them regardless of
source.
per-proxy state (keyed by canonical key): `uses` is a pure counter, incremented on
every handout, never drives selection, survives burns. `timeout` is the
availability state — `None`/`0` fine, `-1` dead/permanent (manual restore only), a
future unix ts = timed out until then. this core is sync: no timers, lazy expiry
checked only against `time.time()` at call time. cooldown and timed burns share
this field; durations only ever exist as call arguments, converted to `now +
seconds` and discarded — a raw duration is never stored.
""" """
import logging import logging
import random import random
@@ -92,20 +80,14 @@ class AioProxies:
@staticmethod @staticmethod
def _normalize_template(template: str) -> str: def _normalize_template(template: str) -> str:
"""fill a bare `{}` session slot, escape-aware """fill a bare `{}` session slot with `{session}`; leave escaped `{{}}` untouched"""
a lone `{}` is treated as `{session}` (back-compat with simple templates).
an escaped literal `{{}}` — str.format's own convention for a literal `{}`
in the output — is left untouched so it survives to render as `{}`, not
`{session}`.
"""
return _BARE_SESSION_SLOT.sub("{session}", template) return _BARE_SESSION_SLOT.sub("{session}", template)
@classmethod @classmethod
def from_file(cls, path: str, **kwargs) -> "AioProxies": def from_file(cls, path: str, **kwargs) -> "AioProxies":
"""build a rotating manager from a newline-delimited proxy file """build a rotating manager from a newline-delimited proxy file
raises FileNotFoundError if the path is missing never exits the process. raises FileNotFoundError if the path is missing - never exits the process.
""" """
with open(path, "r", encoding="utf-8") as fh: with open(path, "r", encoding="utf-8") as fh:
lines = [ln.strip() for ln in fh if ln.strip()] lines = [ln.strip() for ln in fh if ln.strip()]
@@ -151,7 +133,7 @@ class AioProxies:
if self.template is not None: if self.template is not None:
if "session" in fields: if "session" in fields:
# `session` is auto-filled with a fresh id; a caller-supplied one would # `session` is auto-filled with a fresh id; a caller-supplied one would
# collide in str.format with an opaque TypeError reject it clearly # collide in str.format with an opaque TypeError - reject it clearly
raise ValueError("'session' is filled automatically; do not pass it to next()") raise ValueError("'session' is filled automatically; do not pass it to next()")
try: try:
filled = self.template.format(session=self.session_id(), **fields) filled = self.template.format(session=self.session_id(), **fields)
@@ -161,7 +143,7 @@ class AioProxies:
) from exc ) from exc
except ValueError as exc: except ValueError as exc:
# a malformed template (e.g. an unmatched '{') makes str.format raise a # a malformed template (e.g. an unmatched '{') makes str.format raise a
# bare ValueError; re-raise naming the cause so it isn't cryptic # bare ValueError - re-raise naming the cause so it isn't cryptic
raise ValueError(f"malformed proxy template {self.template!r}: {exc}") from exc raise ValueError(f"malformed proxy template {self.template!r}: {exc}") from exc
return parse(filled) return parse(filled)
return self._next_from_list() return self._next_from_list()
@@ -200,14 +182,7 @@ class AioProxies:
return proxy return proxy
def _has_burned(self) -> bool: def _has_burned(self) -> bool:
"""whether the empty 'fine' tier reflects a genuine burn(), not just cooldown """whether any proxy carries a genuine burn (vs just cooldown resting), for log level choice"""
tracked directly via each state's `burned` flag (set by burn(), cleared by
restore()) so a real burn is never masked by the manager's own cooldown
resting on the same `timeout` field, regardless of whether cooldown is on.
used to pick warning (real trouble) vs debug (normal cooldown) on the
soonest-recovering path.
"""
return any(state["burned"] for state in self._state.values()) return any(state["burned"] for state in self._state.values())
def _soonest_recovering(self) -> Optional[int]: def _soonest_recovering(self) -> Optional[int]:
@@ -382,6 +357,6 @@ class AioProxies:
self._index %= len(self._proxies) self._index %= len(self._proxies)
# name aliases same class, call it whichever reads best at your call site # name aliases - same class, call it whichever reads best at your call site
ProxyManager = AioProxies ProxyManager = AioProxies
aioproxies = AioProxies aioproxies = AioProxies
+11 -22
View File
@@ -1,15 +1,12 @@
"""proxy parsing and formatting (pure, no network IO). """proxy parsing and formatting (pure, no network IO).
a `Proxy` holds host/port/optional-auth and renders the shapes different clients `Proxy` holds host/port/optional-auth and renders aiohttp/aioweb, camoufox, socks5,
want: an aiohttp/aioweb proxies dict, a camoufox proxy dict, a socks5 dict, or a and url shapes; auth-less (IP-authenticated) proxies are first-class throughout.
plain url. `parse` accepts the common "host:port" and "host:port:user:pass" string `parse` accepts "host:port" and "host:port:user:pass" - the user field may itself
forms (the user field may itself contain commas, e.g. session-param proxies; the contain commas and the password may itself contain colons.
password field may itself contain colons). auth-less (IP-authenticated) proxies
with no creds are first-class — every render shape handles them via `has_auth`.
`key()` produces a stable canonical identity (`host:port:user:pass`, or `key()` / `canonical_key()` produce a stable canonical identity so burn/remove/stats
`host:port` when auth-less) so burn/remove/stats recognize the same proxy from any recognize the same proxy from any input shape (spec string, dict, or url).
input shape. `canonical_key()` extends that to dict/url forms.
""" """
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, Optional, Union from typing import Dict, Optional, Union
@@ -117,14 +114,7 @@ def parse(spec: Union[str, Proxy]) -> Proxy:
def canonical_key(spec: Union[str, Proxy, Dict[str, str]]) -> str: def canonical_key(spec: Union[str, Proxy, Dict[str, str]]) -> str:
"""canonical identity key for any supported proxy shape """`to_proxy(spec).key()` - the canonical identity key for any supported proxy shape"""
accepts a spec string, a Proxy, a url string (`http://user:pass@host:port`,
`socks5://...`), an aiohttp dict (`{"http": url, "https": url}`), or a
camoufox/socks5 dict (`{"server": ..., "username": ..., "password": ...}`).
all forms collapse to the same `host:port:user:pass` (or `host:port` auth-less)
key, so burn/remove/stats recognize one proxy regardless of how it was passed.
"""
return to_proxy(spec).key() return to_proxy(spec).key()
@@ -132,8 +122,8 @@ def to_proxy(spec: Union[str, Proxy, Dict[str, str]]) -> Proxy:
"""normalize any supported proxy shape into a Proxy """normalize any supported proxy shape into a Proxy
accepts a spec string, a Proxy, a url string, an aiohttp dict, or a accepts a spec string, a Proxy, a url string, an aiohttp dict, or a
camoufox/socks5 dict — the same shapes `canonical_key` accepts. used both for camoufox/socks5 dict. used both for keying and for adding proxies to a pool
keying and for adding proxies to a pool from any shape. from any shape.
""" """
if isinstance(spec, Proxy): if isinstance(spec, Proxy):
return spec return spec
@@ -150,9 +140,8 @@ def _proxy_from_dict(spec: Dict[str, str]) -> Proxy:
"""normalize an aiohttp / camoufox / socks5 dict into a Proxy""" """normalize an aiohttp / camoufox / socks5 dict into a Proxy"""
if "server" in spec: if "server" in spec:
proxy = _proxy_from_url(spec["server"]) proxy = _proxy_from_url(spec["server"])
# prefer explicit dict auth; otherwise fall back to auth embedded in the server # prefer explicit dict auth; fall back to auth embedded in the server URL so
# URL (http://user:pass@host:port) so it isn't silently dropped which would # it isn't silently dropped, which would key the proxy auth-less
# key the proxy auth-less and collide with a genuinely auth-less one
user = spec.get("username") or proxy.user user = spec.get("username") or proxy.user
password = spec.get("password") or proxy.password password = spec.get("password") or proxy.password
if user and password: if user and password: