| 87 | return self._headers |
| 88 | |
| 89 | def set_cookie( |
| 90 | self, |
| 91 | key: str, |
| 92 | value: str = "", |
| 93 | max_age: int | None = None, |
| 94 | expires: datetime | str | int | None = None, |
| 95 | path: str | None = "/", |
| 96 | domain: str | None = None, |
| 97 | secure: bool = False, |
| 98 | httponly: bool = False, |
| 99 | samesite: Literal["lax", "strict", "none"] | None = "lax", |
| 100 | partitioned: bool = False, |
| 101 | ) -> None: |
| 102 | cookie: http.cookies.BaseCookie[str] = http.cookies.SimpleCookie() |
| 103 | cookie[key] = value |
| 104 | if max_age is not None: |
| 105 | cookie[key]["max-age"] = max_age |
| 106 | if expires is not None: |
| 107 | if isinstance(expires, datetime): |
| 108 | cookie[key]["expires"] = format_datetime(expires, usegmt=True) |
| 109 | else: |
| 110 | cookie[key]["expires"] = expires |
| 111 | if path is not None: |
| 112 | cookie[key]["path"] = path |
| 113 | if domain is not None: |
| 114 | cookie[key]["domain"] = domain |
| 115 | if secure: |
| 116 | cookie[key]["secure"] = True |
| 117 | if httponly: |
| 118 | cookie[key]["httponly"] = True |
| 119 | if samesite is not None: |
| 120 | assert samesite.lower() in [ |
| 121 | "strict", |
| 122 | "lax", |
| 123 | "none", |
| 124 | ], "samesite must be either 'strict', 'lax' or 'none'" |
| 125 | cookie[key]["samesite"] = samesite |
| 126 | if partitioned: |
| 127 | if sys.version_info < (3, 14): |
| 128 | raise ValueError("Partitioned cookies are only supported in Python 3.14 and above.") # pragma: no cover |
| 129 | cookie[key]["partitioned"] = True # pragma: no cover |
| 130 | |
| 131 | cookie_val = cookie.output(header="").strip() |
| 132 | self.raw_headers.append((b"set-cookie", cookie_val.encode("latin-1"))) |
| 133 | |
| 134 | def delete_cookie( |
| 135 | self, |