# -*- test-case-name: treq.test.test_treq_integration -*-
"""
Convenience helpers for :mod:`http.cookiejar`
"""
from collections.abc import Iterable
from typing import Union, Optional
from http.cookiejar import Cookie, CookieJar
from hyperlink import EncodedURL
[docs]
class IndexableCookieJar(CookieJar):
"""
A :py:class:`IndexableCookieJar` is Treq's subclass of the standard library
:py:class:`http.cookiejar.CookieJar`, which, like the one from `requests`_,
allows for indexing to retrieve cookie values. This is for convenience and
for compatibility with `requests` users expectations.
.. _requests: https://requests.readthedocs.io/en/latest/
.. note::
In general, you should not need to import or instantiate a
:py:class:`IndexableCookieJar` directly; anywhere that treq requires cookies, a
:py:class:`http.cookiejar.CookieJar` or ``dict`` of ``str`` to ``str``
should be acceptable; but :py:meth:`treq.response._Response.cookies`
returns one that is also indexable.
"""
def __getitem__(self, name: str) -> str:
"""
Retrieve the value of the named cookie.
:param name: The name of the cookie to retrieve.
"""
for cookie in self:
if cookie.name == name and cookie.value is not None:
return cookie.value
raise KeyError(name)
[docs]
def scoped_cookie(origin: Union[str, EncodedURL], name: str, value: str) -> Cookie:
"""
Create a cookie scoped to a given URL's origin.
You can insert the result directly into a `CookieJar`, like::
jar = CookieJar()
jar.set_cookie(scoped_cookie("https://example.tld", "flavor", "chocolate"))
await treq.get("https://domain.example", cookies=jar)
:param origin:
A URL that specifies the domain and port number of the cookie.
If the protocol is HTTP*S* the cookie is marked ``Secure``, meaning
it will not be attached to HTTP requests. Otherwise the cookie will be
attached to both HTTP and HTTPS requests
:param name: Name of the cookie.
:param value: Value of the cookie.
.. note::
This does not scope the cookies to any particular path, only the
host, port, and scheme of the given URL.
"""
if isinstance(origin, EncodedURL):
url_object = origin
else:
url_object = EncodedURL.from_text(origin)
secure = url_object.scheme == "https"
port_specified = not (
(url_object.scheme == "https" and url_object.port == 443)
or (url_object.scheme == "http" and url_object.port == 80)
)
port = str(url_object.port) if port_specified else None
domain = url_object.host
netscape_domain = domain if "." in domain else domain + ".local"
return Cookie(
# Scoping
domain=netscape_domain,
port=port,
secure=secure,
port_specified=port_specified,
# Contents
name=name,
value=value,
# Constant/always-the-same stuff
version=0,
path="/",
expires=None,
discard=False,
comment=None,
comment_url=None,
rfc2109=False,
path_specified=False,
domain_specified=False,
domain_initial_dot=False,
rest={},
)
[docs]
def search(
jar: CookieJar, *, domain: str, name: Optional[str] = None
) -> Iterable[Cookie]:
"""
Raid the cookie jar for matching cookies.
This is O(n) on the number of cookies in the jar.
:param jar: The `CookieJar` (or subclass thereof) to search.
:param domain:
Domain, as in the URL, to match. ``.local`` is appended to
a bare hostname. Subdomains are not matched (i.e., searching
for ``foo.bar.tld`` won't return a cookie set for ``bar.tld``).
:param name: Cookie name to match (exactly)
"""
netscape_domain = domain if "." in domain else domain + ".local"
for c in jar:
if c.domain != netscape_domain:
continue
if name is not None and c.name != name:
continue
yield c