Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ python:
- method: pip
path: .
sphinx:
configuration: docs/conf.py
builder: html
fail_on_warning: true
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Unreleased
----------

Added
~~~~~~~
- Redis backend now identifies itself to Redis servers using the ``lib_name`` and ``lib_version`` parameters. The format follows redis-py conventions: ``lib_name`` is set to ``redis-py(flask-session_v<version>)`` and ``lib_version`` is set to the redis-py version.


0.8.0 - 2024-03-26
------------------

Expand Down
69 changes: 69 additions & 0 deletions src/flask_session/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,72 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
return wrapper

return decorator


def get_flask_session_version() -> str:
"""Get the flask-session version string.

Returns:
flask-session version in the format 'major.minor.patch'
or 'unknown' if not available.
"""
try:
from importlib.metadata import version

return version("flask-session")
except Exception:
# importlib.metadata not available or package metadata not found
# Fallback to __version__ from __init__.py
try:
from flask_session import __version__

return __version__
except (ImportError, AttributeError):
return "unknown"


def get_redis_py_version() -> str:
"""Get the redis-py version string.

Returns:
redis-py version in the format 'major.minor.patch'
or 'unknown' if not available.
"""
try:
from importlib.metadata import version

return version("redis")
except Exception:
return "unknown"


def add_redis_version_info(kwargs):
"""Add version identification for redis-py client.

This function adds library identification to Redis connection kwargs,
allowing Redis operators to see which library is using the connection.

Follows the format: lib_name='redis-py(flask-session_v0.8.0)'
and lib_version='<redis-py version>'.

Only sets lib_name and lib_version if not already provided by user,
ensuring user-provided values are never overridden.

Args:
kwargs: Dictionary of keyword arguments to pass to Redis client.
Will be modified in-place to add 'lib_name' and 'lib_version'
if they are not already present.

Example:
>>> kwargs = {}
>>> add_redis_version_info(kwargs)
>>> kwargs['lib_name']
'redis-py(flask-session_v0.8.0)'
>>> kwargs['lib_version']
'5.0.8'
"""
if "lib_name" not in kwargs:
flask_session_ver = get_flask_session_version()
redis_py_ver = get_redis_py_version()
kwargs["lib_name"] = f"redis-py(flask-session_v{flask_session_ver})"
kwargs["lib_version"] = redis_py_ver
7 changes: 5 additions & 2 deletions src/flask_session/redis/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from flask import Flask
from redis import Redis

from .._utils import total_seconds
from .._utils import add_redis_version_info, total_seconds
from ..base import ServerSideSession, ServerSideSessionInterface
from ..defaults import Defaults

Expand Down Expand Up @@ -53,7 +53,10 @@ def __init__(
RuntimeWarning,
stacklevel=1,
)
client = Redis()
# Add version identification for redis-py client
kwargs = {}
add_redis_version_info(kwargs)
client = Redis(**kwargs)
self.client = client
super().__init__(
app, key_prefix, use_signer, permanent, sid_length, serialization_format
Expand Down
39 changes: 38 additions & 1 deletion tests/test_redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from contextlib import contextmanager

import flask
from flask_session.redis import RedisSession
from redis import Redis

from flask_session._utils import get_flask_session_version, get_redis_py_version
from flask_session.redis import RedisSession


class TestRedisSession:
"""This requires package: redis"""
Expand Down Expand Up @@ -40,3 +42,38 @@ def test_redis_default(self, app_utils):
json.loads(byte_string.decode("utf-8")) if byte_string else {}
)
assert stored_session.get("value") == "44"

def test_redis_adds_version_info(self, app_utils):
"""Test that RedisSessionInterface adds Flask-Session version info when creating default client."""
with self.setup_redis():
# Don't provide a Redis client, let it create one with version info
app = app_utils.create_app({"SESSION_TYPE": "redis"})

with app.test_request_context():
# Access the session interface to trigger client creation
session_interface = app.session_interface

# Check that lib_name and lib_version were set in connection pool
conn_kwargs = session_interface.client.connection_pool.connection_kwargs
flask_session_ver = get_flask_session_version()
expected_lib_name = f"redis-py(flask-session_v{flask_session_ver})"
assert conn_kwargs.get("lib_name") == expected_lib_name
assert conn_kwargs.get("lib_version") == get_redis_py_version()

def test_redis_respects_custom_version_info(self, app_utils):
"""Test that custom lib_name and lib_version are not overridden."""
with self.setup_redis():
# Create a Redis client with custom version info
custom_client = Redis(lib_name="MyCustomApp", lib_version="1.2.3")

app = app_utils.create_app(
{"SESSION_TYPE": "redis", "SESSION_REDIS": custom_client}
)

with app.test_request_context():
session_interface = app.session_interface

# Check that custom values were preserved
conn_kwargs = session_interface.client.connection_pool.connection_kwargs
assert conn_kwargs.get("lib_name") == "MyCustomApp"
assert conn_kwargs.get("lib_version") == "1.2.3"