Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support str and datetime on expires parameter on the set_cookie method #1908

Merged
merged 23 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6b98839
Adds new cookie expire type. Adds function that returns the string if…
Oct 8, 2022
563cb05
Adding tests for datetime expiration and cookie expire format
Oct 8, 2022
c66ca24
Implements PR change suggestions
Oct 13, 2022
8af9d46
Updates test that used str input back to int input
Oct 13, 2022
f9881ba
Merge branch 'master' into issue-1878-fix-cookie-typing
oskipa Oct 13, 2022
5bf86d5
Updates date formatting comments to point to RFCs. Removes expires fr…
Oct 20, 2022
1303802
Deletes extra test
Oct 20, 2022
432358a
Merge branch 'master' into issue-1878-fix-cookie-typing
Oct 21, 2022
d7859be
Merge branch 'master' into issue-1878-fix-cookie-typing
oskipa Nov 1, 2022
af7d3cc
Merge branch 'master' into issue-1878-fix-cookie-typing
oskipa Nov 2, 2022
caed475
Merge branch 'master' into issue-1878-fix-cookie-typing
oskipa Nov 7, 2022
b8e7931
Merge branch 'master' into issue-1878-fix-cookie-typing
Kludex Dec 12, 2022
0433643
simplify code
Kludex Dec 17, 2022
09447b7
Merge branch 'master' into issue-1878-fix-cookie-typing
Kludex Feb 4, 2023
6db2870
refactor: improve tests
Kludex Feb 4, 2023
7eacc85
readd expires on delete cookie method
Kludex Feb 4, 2023
d6b28a7
docs: add note about datetime support on expires
Kludex Feb 4, 2023
d5199ac
tests: use time-machine on test suite
Kludex Feb 4, 2023
2fda4aa
Merge branch 'master' into issue-1878-fix-cookie-typing
Kludex Feb 4, 2023
da8faba
Use monkeypatch instead of time-machine
Kludex Feb 6, 2023
65ccf47
Merge branch 'master' into issue-1878-fix-cookie-typing
Kludex Feb 6, 2023
727f9dc
Merge branch 'master' into issue-1878-fix-cookie-typing
Kludex Feb 6, 2023
b2b6f91
Merge branch 'master' into issue-1878-fix-cookie-typing
Kludex Feb 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/responses.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Signature: `Response.set_cookie(key, value, max_age=None, expires=None, path="/"
* `key` - A string that will be the cookie's key.
* `value` - A string that will be the cookie's value.
* `max_age` - An integer that defines the lifetime of the cookie in seconds. A negative integer or a value of `0` will discard the cookie immediately. `Optional`
* `expires` - An integer that defines the number of seconds until the cookie expires. `Optional`
* `expires` - Either an integer that defines the number of seconds until the cookie expires, or a datetime. `Optional`
* `path` - A string that specifies the subset of routes to which the cookie will apply. `Optional`
* `domain` - A string that specifies the domain for which the cookie is valid. `Optional`
* `secure` - A bool indicating that the cookie will only be sent to the server if request is made using SSL and the HTTPS protocol. `Optional`
Expand Down
10 changes: 7 additions & 3 deletions starlette/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import stat
import sys
import typing
from email.utils import formatdate
from datetime import datetime
from email.utils import format_datetime, formatdate
from functools import partial
from mimetypes import guess_type as mimetypes_guess_type
from urllib.parse import quote
Expand Down Expand Up @@ -105,7 +106,7 @@ def set_cookie(
key: str,
value: str = "",
max_age: typing.Optional[int] = None,
expires: typing.Optional[int] = None,
expires: typing.Optional[typing.Union[datetime, str, int]] = None,
path: str = "/",
domain: typing.Optional[str] = None,
secure: bool = False,
Expand All @@ -117,7 +118,10 @@ def set_cookie(
if max_age is not None:
cookie[key]["max-age"] = max_age
if expires is not None:
cookie[key]["expires"] = expires
if isinstance(expires, datetime):
cookie[key]["expires"] = format_datetime(expires, usegmt=True)
else:
cookie[key]["expires"] = expires
if path is not None:
cookie[key]["path"] = path
if domain is not None:
Expand Down
40 changes: 39 additions & 1 deletion tests/test_responses.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import datetime as dt
import os
import time
from http.cookies import SimpleCookie

import anyio
import pytest
Expand Down Expand Up @@ -288,7 +291,11 @@ def test_file_response_with_inline_disposition(tmpdir, test_client_factory):
assert response.headers["content-disposition"] == expected_disposition


def test_set_cookie(test_client_factory):
def test_set_cookie(test_client_factory, monkeypatch):
# Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
mocked_now = dt.datetime(2100, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
monkeypatch.setattr(time, "time", lambda: mocked_now.timestamp())

async def app(scope, receive, send):
response = Response("Hello, world!", media_type="text/plain")
response.set_cookie(
Expand All @@ -307,6 +314,37 @@ async def app(scope, receive, send):
client = test_client_factory(app)
response = client.get("/")
assert response.text == "Hello, world!"
assert (
response.headers["set-cookie"]
== "mycookie=myvalue; Domain=localhost; expires=Fri, 22 Jan 2100 12:00:10 GMT; "
florimondmanca marked this conversation as resolved.
Show resolved Hide resolved
"HttpOnly; Max-Age=10; Path=/; SameSite=none; Secure"
)


@pytest.mark.parametrize(
"expires",
[
pytest.param(
dt.datetime(2100, 1, 22, 12, 0, 10, tzinfo=dt.timezone.utc), id="datetime"
),
pytest.param("Fri, 22 Jan 2100 12:00:10 GMT", id="str"),
pytest.param(10, id="int"),
],
)
def test_expires_on_set_cookie(test_client_factory, monkeypatch, expires):
# Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
mocked_now = dt.datetime(2100, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
monkeypatch.setattr(time, "time", lambda: mocked_now.timestamp())

async def app(scope, receive, send):
response = Response("Hello, world!", media_type="text/plain")
response.set_cookie("mycookie", "myvalue", expires=expires)
await response(scope, receive, send)

client = test_client_factory(app)
response = client.get("/")
cookie: SimpleCookie = SimpleCookie(response.headers.get("set-cookie"))
assert cookie["mycookie"]["expires"] == "Fri, 22 Jan 2100 12:00:10 GMT"


def test_delete_cookie(test_client_factory):
Expand Down