diff --git a/README.md b/README.md index d938e13..d3d518e 100644 --- a/README.md +++ b/README.md @@ -11,18 +11,18 @@ and swap the HTTP client while inheriting everything else. `requirements.txt`: ``` -aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v0.1.10 +aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v0.1.11 ``` Direct: ```bash -pip install "aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v0.1.10" +pip install "aioweb @ git+ssh://git@git.rethinkstudios.io/rethink-public/aioweb.git@v0.1.11" ``` Requires `aiohttp` and `yarl` (pulled transitively). -Drop the `@v0.1.10` suffix from the line above to install the latest unpinned. +Drop the `@v0.1.11` suffix from the line above to install the latest unpinned. ## Usage @@ -97,10 +97,15 @@ s.overwrite_domain("internal.local", "127.0.0.1") # host-substring rewrite print(s.preview("POST", url, json={"a": 1}).as_curl()) # equivalent cURL command ``` -`as_curl()` renders `params` (merged into the url's query string) and `timeout` (as -`--max-time`) as well as headers/body/proxy, so the emitted command is faithful to what -`request()` actually sends. Pass `debug=True` to `request_with_retries` to log the cURL -preview and request flow. +`preview()` is synchronous and never touches the backend session — it works before any +event loop is running (e.g. building a preview at import/setup time), not just inside +`asyncio.run()`. Its default cookie lookup only reads cookies from an already-built +session; if the session hasn't been built yet there are no cookies to read, so it +returns none by default (pass `cookies=` explicitly to preview cookies for a session +that hasn't sent a request yet). `as_curl()` renders `params` (merged into the url's +query string) and `timeout` (as `--max-time`) as well as headers/body/proxy, so the +emitted command is faithful to what `request()` actually sends. Pass `debug=True` to +`request_with_retries` to log the cURL preview and request flow. ## Custom backends @@ -144,6 +149,23 @@ Two changes can't be shimmed without re-introducing the bugs they fix: ## Changelog +### v0.1.11 + +- **`preview()` no longer requires a running event loop.** Its default cookie + lookup (`cookies=` not passed) previously called `_cookies_for_url()`, which + reached the backend session and built it if absent — under aiohttp>=3.14 that + needs a running loop, so `preview()` raised `RuntimeError('no running event + loop')` when called before the loop starts, defeating its own pre-loop + build/inspect use case (v0.1.7). Now the default cookie lookup is skipped + entirely when the session hasn't been built yet (nothing could have been set + on a session that doesn't exist); `cookies={}` and in-loop calls are unaffected. +- **`set_cookie(domain=None)` now honors `path`.** The `domain=None` branch + returned right after `update_cookies()`, before the line that sets the + morsel's `path` — so a shared cookie (`set_cookie(name, value, path="/api")`, + no `domain=`) always stored the `SimpleCookie` default `path="/"` instead. The + morsel's `path` is now set before the `domain=None` early return. The + domain-bound branch was already correct and is unchanged. + ### v0.1.10 - Docs-only pass: compressed module/method docstrings and comments that restated diff --git a/pyproject.toml b/pyproject.toml index de71344..039d282 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "aioweb" -version = "0.1.10" +version = "0.1.11" description = "Async HTTP session wrapper over aiohttp — proxies, header overwrites, retries, previews. Config-free, installable." requires-python = ">=3.10" dependencies = [ diff --git a/src/aioweb/session.py b/src/aioweb/session.py index ae38c51..afc242e 100644 --- a/src/aioweb/session.py +++ b/src/aioweb/session.py @@ -227,12 +227,12 @@ class ExtendedSession: """ cookie = SimpleCookie() cookie[name] = value + cookie[name]["path"] = path if domain is None: self.session.cookie_jar.update_cookies(cookie) return if "://" not in domain: domain = "http://" + domain - cookie[name]["path"] = path self.session.cookie_jar.update_cookies(cookie, response_url=URL(domain)) def clear_cookies(self): @@ -247,11 +247,15 @@ class ExtendedSession: # preview def preview(self, method, url, **kwargs): - """build a RequestPreview for a request without sending it + """build a RequestPreview for a request without sending it - synchronous, no loop required url is domain-rewritten before cookies are resolved, matching the real request - cookie binding is host-based, so resolving against the pre-rewrite - host could miss or misattribute cookies. + host could miss or misattribute cookies. the default cookie lookup never + touches the backend session: building it needs a running loop (aiohttp>=3.14), + which would defeat preview()'s own pre-loop inspection use case, and an unbuilt + session has no cookies to report anyway - pass cookies= explicitly to preview + cookies that would come from a not-yet-built session. """ url = self._apply_domain_overwrites(url) proxy = self._get_proxy(url, kwargs.pop("proxies", None)) @@ -262,7 +266,7 @@ class ExtendedSession: timeout_total = timeout if isinstance(timeout, (int, float)) else None cookies = kwargs.pop("cookies", None) - if cookies is None: + if cookies is None and self._session is not None: cookies = self._cookies_for_url(url) if cookies: cookie_header = "; ".join(f"{k}={v}" for k, v in cookies.items())