Cookies(cookies=<CookieJar>) aliases the input instead of copying it #3786
|
AI disclosure: this was investigated and written with the assistance of Claude (Anthropic). I independently reproduced it against a fresh clone of Summary
def __init__(self, cookies: CookieTypes | None = None) -> None:
if cookies is None or isinstance(cookies, dict):
self.jar = CookieJar()
...
elif isinstance(cookies, list):
self.jar = CookieJar()
...
elif isinstance(cookies, Cookies):
self.jar = CookieJar()
...
else:
self.jar = cookies # <-- direct alias, not a copySo constructing a Reproductionfrom http.cookiejar import CookieJar
import httpx
jar = CookieJar()
client = httpx.Client(cookies=jar)
print(client.cookies.jar is jar) # True -- should be a copy, like every other input type
client.cookies.set("foo", "bar")
print(list(jar)) # the CALLER's own jar now contains "foo" -- silent mutationReal output from running this against today's No network needed to reproduce — plain construction is enough. Why I think it's worth a lookMost likely to bite code that keeps its own Happy to put together a PR (fresh |
Replies: 2 comments
|
This is actually intentional and follows standard Python HTTP networking conventions (such as Why
|
|
You're right, and this should be retracted — thank you for the thorough, correct pushback. I independently re-verified the mechanism before replying, not just taking the argument on faith. So the asymmetry isn't an oversight: Retracting the "this is a bug" framing and the proposed fix. Real, working alternative for anyone who does want an isolated copy: |
This is actually intentional and follows standard Python HTTP networking conventions (such as
urllib.request.HTTPCookieProcessor(cookiejar)).Why
CookieJaris aliased rather than clonedPassing an explicit
http.cookiejar.CookieJarinstance (or subclass) is the primary mechanism in Python for stateful persistence and custom policy:Persistent Jars (
MozillaCookieJar/LWPCookieJar):When users pass a file-backed jar:
If
httpx.Cookies…