From 84127a93ff9f4acd23df6c98f4d83020dde32f32 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Fri, 3 Jul 2026 00:12:54 -0400 Subject: [PATCH] docs: compress prose/module docstrings, em-dash->hyphen (de-bloat wave 1) Signed-off-by: disqualifier --- README.md | 11 +++++-- pyproject.toml | 2 +- src/aioproxies/__init__.py | 9 ++---- src/aioproxies/manager.py | 61 +++++++++++--------------------------- src/aioproxies/proxy.py | 33 +++++++-------------- 5 files changed, 40 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index b2b5446..1097ba2 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,15 @@ edits. **Credentials are always injected — never hardcoded.** ## 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: -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` / `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 @@ -209,6 +209,11 @@ await reset("https://provider/reset-url") # rotate upstream ip ## 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 - **Stored-shape change: int unix deadlines.** `burn(proxy, seconds)` and the diff --git a/pyproject.toml b/pyproject.toml index 567e267..1c77481 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "aioproxies" -version = "0.3.0" +version = "0.3.1" description = "proxy parsing, formatting, health, and pool management for aiohttp/aioweb, camoufox, and socks5" requires-python = ">=3.10" dependencies = [] diff --git a/src/aioproxies/__init__.py b/src/aioproxies/__init__.py index 7409262..f6fa1a5 100644 --- a/src/aioproxies/__init__.py +++ b/src/aioproxies/__init__.py @@ -1,9 +1,4 @@ -"""aioproxies — proxy parsing, formatting, and source management. - -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. -""" +"""aioproxies - proxy parsing, formatting, and source management. see README for usage.""" from .manager import AioProxies, ProxiesExhaustedError, ProxyManager, aioproxies from .proxy import Proxy, canonical_key, parse, to_proxy @@ -18,4 +13,4 @@ __all__ = [ "to_proxy", ] -__version__ = "0.3.0" +__version__ = "0.3.1" diff --git a/src/aioproxies/manager.py b/src/aioproxies/manager.py index 0c44f2b..6701092 100644 --- a/src/aioproxies/manager.py +++ b/src/aioproxies/manager.py @@ -1,31 +1,19 @@ """proxy source management: session templates, rotation, static. -`AioProxies` is constructed with exactly one source and hands out `Proxy` objects. -no module-level globals (rotation state is per-instance); a missing proxy file -raises, never `sys.exit`. +`AioProxies` takes exactly one source (template / proxies / static) and hands out +`Proxy` objects via `next()`/`get()`. no module-level globals; a missing proxy file +raises, never `sys.exit`. see README for the full source/method reference. -sources: -- template: a format string. `{session}` is filled with a fresh id on each - `next()`; other placeholders (`{country}`, `{ttl}`, ...) come from - `next(**fields)`. a bare `{}` is treated as the session slot unless escaped as - `{{}}` (back-compat with simple templates). -- proxies: a list of specs cycled round-robin (deduped by canonical key) -- static: one fixed proxy +proxy health (rotating list source only) is keyed by each proxy's canonical key +(`host:port:user:pass`, or `host:port` auth-less; port is normalized so `:080` and +`:80` collapse to one key). on template/static sources health/pool methods are +no-ops that log a warning and return, so generic caller code can call them +regardless of source. -proxy health (rotating list source only) — burn/timeout, usage counters, reuse -cooldown, pool edits (replace/add/remove) — keyed by each proxy's canonical key -(`host:port:user:pass`, or `host:port` auth-less; port is normalized so `:080` -and `:80` collapse to one key). on template/static sources these are no-ops that -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. +per-proxy `timeout` (the availability field) is shared by cooldown and timed +burns: `None`/`0` fine, `-1` dead/permanent, a future unix ts = timed out until +then; durations only ever exist as call arguments, converted to `now + seconds` +and discarded, never stored raw. """ import logging import random @@ -92,20 +80,14 @@ class AioProxies: @staticmethod def _normalize_template(template: str) -> str: - """fill a bare `{}` session slot, escape-aware - - 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}`. - """ + """fill a bare `{}` session slot with `{session}`; leave escaped `{{}}` untouched""" return _BARE_SESSION_SLOT.sub("{session}", template) @classmethod def from_file(cls, path: str, **kwargs) -> "AioProxies": """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: lines = [ln.strip() for ln in fh if ln.strip()] @@ -151,7 +133,7 @@ class AioProxies: if self.template is not None: if "session" in fields: # `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()") try: filled = self.template.format(session=self.session_id(), **fields) @@ -161,7 +143,7 @@ class AioProxies: ) from exc except ValueError as exc: # 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 return parse(filled) return self._next_from_list() @@ -200,14 +182,7 @@ class AioProxies: return proxy def _has_burned(self) -> bool: - """whether the empty 'fine' tier reflects a genuine burn(), not just cooldown - - 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. - """ + """whether any proxy carries a genuine burn (vs just cooldown resting), for log level choice""" return any(state["burned"] for state in self._state.values()) def _soonest_recovering(self) -> Optional[int]: @@ -382,6 +357,6 @@ class AioProxies: 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 aioproxies = AioProxies diff --git a/src/aioproxies/proxy.py b/src/aioproxies/proxy.py index 3616dba..8b21ec6 100644 --- a/src/aioproxies/proxy.py +++ b/src/aioproxies/proxy.py @@ -1,15 +1,12 @@ """proxy parsing and formatting (pure, no network IO). -a `Proxy` holds host/port/optional-auth and renders the shapes different clients -want: an aiohttp/aioweb proxies dict, a camoufox proxy dict, a socks5 dict, or a -plain url. `parse` accepts the common "host:port" and "host:port:user:pass" string -forms (the user field may itself contain commas, e.g. session-param proxies; the -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`. +`Proxy` holds host/port/optional-auth and renders aiohttp/aioweb, camoufox, socks5, +and url shapes; auth-less (IP-authenticated) proxies are first-class throughout. +`parse` accepts "host:port" and "host:port:user:pass" - the user field may itself +contain commas and the password may itself contain colons. -`key()` produces a stable canonical identity (`host:port:user:pass`, or -`host:port` when auth-less) so burn/remove/stats recognize the same proxy from any -input shape. `canonical_key()` extends that to dict/url forms. +`key()` / `canonical_key()` produce a stable canonical identity so burn/remove/stats +recognize the same proxy from any input shape (spec string, dict, or url). """ from dataclasses import dataclass 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: - """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. - """ + """`to_proxy(spec).key()` - the canonical identity key for any supported proxy shape""" 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 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 - keying and for adding proxies to a pool from any shape. + camoufox/socks5 dict. used both for keying and for adding proxies to a pool + from any shape. """ if isinstance(spec, Proxy): return spec @@ -150,9 +140,8 @@ def _proxy_from_dict(spec: Dict[str, str]) -> Proxy: """normalize an aiohttp / camoufox / socks5 dict into a Proxy""" if "server" in spec: proxy = _proxy_from_url(spec["server"]) - # prefer explicit dict auth; otherwise fall back to auth embedded in the server - # URL (http://user:pass@host:port) so it isn't silently dropped — which would - # key the proxy auth-less and collide with a genuinely auth-less one + # prefer explicit dict auth; fall back to auth embedded in the server URL so + # it isn't silently dropped, which would key the proxy auth-less user = spec.get("username") or proxy.user password = spec.get("password") or proxy.password if user and password: