|
1 | 1 | import email |
| 2 | +from datetime import datetime |
2 | 3 | from email.message import Message |
3 | | -from typing import List, Tuple, cast |
| 4 | +from typing import Dict, List, NamedTuple, Optional, Tuple, Type, TypeVar, Union, cast |
4 | 5 | from urllib.parse import parse_qsl |
5 | 6 |
|
| 7 | +try: |
| 8 | + from typing import Literal # type: ignore[attr-defined] |
| 9 | +except ImportError: # pragma: no cover |
| 10 | + from typing_extensions import Literal |
| 11 | + |
6 | 12 | import httpx |
7 | 13 |
|
8 | 14 |
|
@@ -71,3 +77,62 @@ def decode_data(request: httpx.Request) -> Tuple[MultiItems, MultiItems]: |
71 | 77 | files = MultiItems() |
72 | 78 |
|
73 | 79 | return data, files |
| 80 | + |
| 81 | + |
| 82 | +Self = TypeVar("Self", bound="SetCookie") |
| 83 | + |
| 84 | + |
| 85 | +class SetCookie( |
| 86 | + NamedTuple( |
| 87 | + "SetCookie", |
| 88 | + [ |
| 89 | + ("header_name", Literal["Set-Cookie"]), |
| 90 | + ("header_value", str), |
| 91 | + ], |
| 92 | + ) |
| 93 | +): |
| 94 | + def __new__( |
| 95 | + cls: Type[Self], |
| 96 | + name: str, |
| 97 | + value: str, |
| 98 | + *, |
| 99 | + path: Optional[str] = None, |
| 100 | + domain: Optional[str] = None, |
| 101 | + expires: Optional[Union[str, datetime]] = None, |
| 102 | + max_age: Optional[int] = None, |
| 103 | + http_only: bool = False, |
| 104 | + same_site: Optional[Literal["Strict", "Lax", "None"]] = None, |
| 105 | + secure: bool = False, |
| 106 | + partitioned: bool = False, |
| 107 | + ) -> Self: |
| 108 | + """ |
| 109 | + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#syntax |
| 110 | + """ |
| 111 | + attrs: Dict[str, Union[str, bool]] = {name: value} |
| 112 | + if path is not None: |
| 113 | + attrs["Path"] = path |
| 114 | + if domain is not None: |
| 115 | + attrs["Domain"] = domain |
| 116 | + if expires is not None: |
| 117 | + if isinstance(expires, datetime): # pragma: no branch |
| 118 | + expires = expires.strftime("%a, %d %b %Y %H:%M:%S GMT") |
| 119 | + attrs["Expires"] = expires |
| 120 | + if max_age is not None: |
| 121 | + attrs["Max-Age"] = str(max_age) |
| 122 | + if http_only: |
| 123 | + attrs["HttpOnly"] = True |
| 124 | + if same_site is not None: |
| 125 | + attrs["SameSite"] = same_site |
| 126 | + if same_site == "None": # pragma: no branch |
| 127 | + secure = True |
| 128 | + if secure: |
| 129 | + attrs["Secure"] = True |
| 130 | + if partitioned: |
| 131 | + attrs["Partitioned"] = True |
| 132 | + |
| 133 | + string = "; ".join( |
| 134 | + _name if _value is True else f"{_name}={_value}" |
| 135 | + for _name, _value in attrs.items() |
| 136 | + ) |
| 137 | + self = super().__new__(cls, "Set-Cookie", string) |
| 138 | + return self |
0 commit comments