Skip to content

Commit

Permalink
update tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-oleshkevich committed Oct 7, 2024
1 parent 1c70618 commit 6fd4a04
Show file tree
Hide file tree
Showing 17 changed files with 580 additions and 629 deletions.
8 changes: 7 additions & 1 deletion examples/expiring.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Open http://localhost:8000 for demo page.
"""

import datetime
import json
from starlette.applications import Starlette
Expand Down Expand Up @@ -46,7 +47,12 @@ async def clean(request: Request) -> RedirectResponse:
Route("/clean", endpoint=clean),
]
middleware = [
Middleware(SessionMiddleware, store=CookieStore(secret_key="key"), cookie_https_only=False, lifetime=10),
Middleware(
SessionMiddleware,
store=CookieStore(secret_key="key"),
cookie_https_only=False,
lifetime=10,
),
Middleware(SessionAutoloadMiddleware),
]
app = Starlette(debug=True, routes=routes, middleware=middleware)
1 change: 1 addition & 0 deletions examples/fastapi_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
Access localhost:8000/set to set test session data, and
access localhost:8000/clean to clear session data
"""

import datetime
import typing
from fastapi import FastAPI
Expand Down
11 changes: 7 additions & 4 deletions examples/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Open http://localhost:8000 for demo page.
"""

from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.requests import Request
Expand Down Expand Up @@ -55,9 +56,7 @@ async def profile(request: Request) -> Response:
<form method="post" action="/logout">
<button type="submit">logout</button>
</form>
""".format(
username=username
)
""".format(username=username)
)


Expand All @@ -68,7 +67,11 @@ async def profile(request: Request) -> Response:
Route("/profile", endpoint=profile),
]
middleware = [
Middleware(SessionMiddleware, store=CookieStore(secret_key="secret"), cookie_https_only=False),
Middleware(
SessionMiddleware,
store=CookieStore(secret_key="secret"),
cookie_https_only=False,
),
Middleware(SessionAutoloadMiddleware),
]
app = Starlette(debug=True, routes=routes, middleware=middleware)
1 change: 1 addition & 0 deletions examples/redis_.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Open http://localhost:8000 for management panel.
"""

import datetime
import json
import os
Expand Down
1 change: 1 addition & 0 deletions examples/rolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Open http://localhost:8000 for demo page.
"""

import datetime
import json
from starlette.applications import Starlette
Expand Down
1 change: 1 addition & 0 deletions examples/session_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Open http://localhost:8000 for demo page.
"""

import datetime
import json
from starlette.applications import Starlette
Expand Down
963 changes: 436 additions & 527 deletions poetry.lock

Large diffs are not rendered by default.

71 changes: 37 additions & 34 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,13 @@ itsdangerous = "^2.0.1"
redis = {version = ">=4.2.0rc1", optional = true}

[tool.poetry.group.dev.dependencies]
pytest = "^7.2"
black = "^22.10.0"
pytest-asyncio = "^0.19.0"
requests = "^2.25.1"
pytest-cov = "^4.0"
flake8 = "^5.0.4"
mypy = "^0.990"
fastapi = "^0.79.0"
pytest = "^8.0"
pytest-asyncio = "^0.24.0"
pytest-cov = "5.0.0"
mypy = "1.11.2"
fastapi = "0.115.0"
redis = ">=4.2.0rc1"
types-redis = "^4.3.20"
httpx = "^0.27.2"

[tool.poetry.extras]
redis = ["redis"]
Expand All @@ -47,26 +44,10 @@ redis = ["redis"]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.black]
target-version = ["py310"]
line_length = 120

[tool.isort]
combine_as_imports = true
include_trailing_comma = true
known_standard_library = "dataclasses,typing_extensions"
known_first_party = "starsessions"
known_third_party = ["pkg_resources", "toml"]
known_future_library = "__future__"
line_length = 120
multi_line_output = 3
profile = "black"
use_parentheses = true

[tool.coverage.run]
branch = true
source = ["starsessions"]
omit = ["tests/*", ".venv/*", "*/__main__.py"]
source = ["starlette_dispatch"]
omit = ["tests/*", ".venv/*", ".git/*", "*/__main__.py", "examples"]

[tool.coverage.report]
exclude_lines = [
Expand All @@ -75,18 +56,40 @@ exclude_lines = [
]

[tool.mypy]
disallow_untyped_defs = true
ignore_missing_imports = true
files = ["starsessions", "tests", "examples"]
exclude = "/*venv/"
files = ["starsessions", "examples", "tests"]
pretty = true
strict = true
show_error_context = true
show_column_numbers = true
show_error_codes = true
warn_unused_configs = true

[tool.pytest.ini_options]
minversion = "7.2"
minversion = "8.0"
asyncio_mode = 'auto'
asyncio_default_fixture_loop_scope = 'session'
python_files = ["tests.py", "test_*.py", "*_tests.py"]
norecursedirs = [".git", ".venv"]
addopts = "--tb=short -s --no-cov-on-fail"
testpaths = ["tests"]
norecursedirs = [
"node_modules", "frontend", "storage", "dist", ".git",
"*/migrations/*", "*/static/*", "docs", ".venv"
]

[tool.ruff]
exclude = [
".egg",
".git",
".hg",
".mypy_cache",
".nox",
".tox",
".venv",
]
line-length = 120
indent-width = 4

[tool.ruff.lint]
fixable = ["ALL"]

[tool.ruff.format]
skip-magic-trailing-comma = false
6 changes: 5 additions & 1 deletion starsessions/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

from starsessions import SessionNotLoaded
from starsessions.serializers import JsonSerializer, Serializer
from starsessions.session import SessionHandler, get_session_remaining_seconds, load_session
from starsessions.session import (
SessionHandler,
get_session_remaining_seconds,
load_session,
)
from starsessions.stores import SessionStore


Expand Down
6 changes: 5 additions & 1 deletion starsessions/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ async def load(self) -> None:
)

# read and merge metadata
metadata = {"lifetime": self.lifetime, "created": time.time(), "last_access": time.time()}
metadata = {
"lifetime": self.lifetime,
"created": time.time(),
"last_access": time.time(),
}
metadata.update(data.pop("__metadata__", {}))
metadata.update({"last_access": time.time()}) # force update
self.metadata = metadata # type: ignore[assignment]
Expand Down
8 changes: 6 additions & 2 deletions tests/backends/test_cookie.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ async def test_cookie_read_write(cookie_store: SessionStore) -> None:


@pytest.mark.asyncio
async def test_cookie_read_data_of_session_only_cookie(cookie_store: SessionStore) -> None:
async def test_cookie_read_data_of_session_only_cookie(
cookie_store: SessionStore,
) -> None:
new_id = await cookie_store.write("session_id", b"some data", lifetime=0, ttl=0)
assert await cookie_store.read(new_id, lifetime=0) == b"some data"

Expand All @@ -26,6 +28,8 @@ async def test_cookie_remove(cookie_store: SessionStore) -> None:


@pytest.mark.asyncio
async def test_returns_empty_string_for_bad_signature(cookie_store: SessionStore) -> None:
async def test_returns_empty_string_for_bad_signature(
cookie_store: SessionStore,
) -> None:
# the session_id value is a signed session cookie value
assert await cookie_store.read("session_id", lifetime=10) == b""
19 changes: 8 additions & 11 deletions tests/backends/test_redis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import os
import typing

import pytest
import redis.asyncio
from pytest_asyncio.plugin import SubRequest

from starsessions import ImproperlyConfigured
from starsessions.stores.base import SessionStore
Expand All @@ -12,8 +13,11 @@ def redis_key_callable(session_id: str) -> str:
return f"this:is:a:redis:key:{session_id}"


@pytest.fixture(params=["prefix_", redis_key_callable], ids=["using string", "using redis_key_callable"])
def redis_store(request: SubRequest) -> SessionStore:
@pytest.fixture(
params=["prefix_", redis_key_callable],
ids=["using string", "using redis_key_callable"],
)
def redis_store(request: typing.Any) -> SessionStore:
redis_key = request.param
url = os.environ.get("REDIS_URL", "redis://localhost")
return RedisStore(url, prefix=redis_key)
Expand All @@ -35,14 +39,7 @@ async def test_redis_write_with_session_only_setup(redis_store: SessionStore) ->
async def test_redis_remove(redis_store: SessionStore) -> None:
await redis_store.write("session_id", b"data", lifetime=60, ttl=60)
await redis_store.remove("session_id")
assert await redis_store.exists("session_id") is False


@pytest.mark.asyncio
async def test_redis_exists(redis_store: SessionStore) -> None:
await redis_store.write("session_id", b"data", lifetime=60, ttl=60)
assert await redis_store.exists("session_id") is True
assert await redis_store.exists("other id") is False
assert await redis_store.read("session_id", lifetime=60) == b""


@pytest.mark.asyncio
Expand Down
35 changes: 20 additions & 15 deletions tests/test_autoloading.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from starlette.testclient import TestClient
from starlette.types import Receive, Scope, Send

from starsessions import SessionAutoloadMiddleware, SessionMiddleware, SessionNotLoaded, SessionStore
from starsessions import (
SessionAutoloadMiddleware,
SessionMiddleware,
SessionNotLoaded,
SessionStore,
)


@pytest.mark.asyncio
Expand All @@ -18,8 +23,8 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
await response(scope, receive, send)

app = SessionMiddleware(SessionAutoloadMiddleware(app), store=store)
client = TestClient(app)
assert client.get("/", cookies={"session": "session_id"}).json() == {"key": "value"}
client = TestClient(app, cookies={"session": "session_id"})
assert client.get("/").json() == {"key": "value"}


@pytest.mark.asyncio
Expand All @@ -32,15 +37,15 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
await response(scope, receive, send)

app = SessionMiddleware(SessionAutoloadMiddleware(app, paths=["/admin", "/app"]), store=store)
client = TestClient(app)
client = TestClient(app, cookies={"session": "session_id"})

with pytest.raises(SessionNotLoaded):
assert client.get("/", cookies={"session": "session_id"}).json() == {}
assert client.get("/").json() == {}

assert client.get("/app", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/admin", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/app/1/users", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/admin/settings", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/app").json() == {"key": "value"}
assert client.get("/admin").json() == {"key": "value"}
assert client.get("/app/1/users").json() == {"key": "value"}
assert client.get("/admin/settings").json() == {"key": "value"}


@pytest.mark.asyncio
Expand All @@ -56,11 +61,11 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
rx_admin = re.compile("/admin*")

app = SessionMiddleware(SessionAutoloadMiddleware(app, paths=[rx_admin, rx_app]), store=store)
client = TestClient(app)
client = TestClient(app, cookies={"session": "session_id"})
with pytest.raises(SessionNotLoaded):
assert client.get("/", cookies={"session": "session_id"}).json() == {}
assert client.get("/").json() == {}

assert client.get("/app", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/admin", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/app/1/users", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/admin/settings", cookies={"session": "session_id"}).json() == {"key": "value"}
assert client.get("/app").json() == {"key": "value"}
assert client.get("/admin").json() == {"key": "value"}
assert client.get("/app/1/users").json() == {"key": "value"}
assert client.get("/admin/settings").json() == {"key": "value"}
10 changes: 5 additions & 5 deletions tests/test_expiring_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
await response(scope, receive, send)

app = SessionMiddleware(app, store=store, lifetime=10, rolling=False)
client = TestClient(app)
client = TestClient(app, cookies={"session": "session_id"})

current_time = time.time()

# it must set max-age = 10
with mock.patch("time.time", lambda: current_time):
response = client.get("/", cookies={"session": "session_id"})
first_max_age = next(cookie for cookie in response.cookies if cookie.name == "session").expires
response = client.get("/")
first_max_age = next(cookie for cookie in response.cookies.jar if cookie.name == "session").expires

# it must set the same max-age as the first response
with mock.patch("time.time", lambda: current_time + 2):
response = client.get("/", cookies={"session": "session_id"})
second_max_age = next(cookie for cookie in response.cookies if cookie.name == "session").expires
response = client.get("/")
second_max_age = next(cookie for cookie in response.cookies.jar if cookie.name == "session").expires

# the expiry date must be the same for both responses
assert second_max_age == first_max_age
Loading

0 comments on commit 6fd4a04

Please sign in to comment.