Hone logo
Hone
Problems

Python Session Management: Building a Simple Web Session System

Web applications often need to maintain state for individual users across multiple requests. This is typically achieved through session management, allowing users to log in, store preferences, or keep track of items in a shopping cart. This challenge asks you to build a basic session management system in Python.

Problem Description

Your task is to create a Python class, SessionManager, that can handle the creation, retrieval, and expiration of user sessions. A session should be identified by a unique session ID. Each session will store key-value pairs representing user data. You also need to implement a mechanism to automatically clean up expired sessions.

Key Requirements:

  1. Session Creation: The SessionManager should be able to generate a new, unique session ID and create a new, empty session associated with that ID.
  2. Session Storage: Store session data (key-value pairs) for each active session.
  3. Session Retrieval: Given a session ID, retrieve the associated session data. If the session ID is invalid or expired, indicate that.
  4. Session Updates: Allow modification of session data (adding, updating, or deleting key-value pairs).
  5. Session Expiration: Sessions should have a configurable expiration time. Expired sessions should no longer be accessible.
  6. Automatic Cleanup: Implement a mechanism to periodically scan for and remove expired sessions to prevent memory leaks.

Expected Behavior:

  • When a new session is created, a unique ID is returned.
  • When data is stored, it should be accessible later using the same session ID and key.
  • Accessing an expired or non-existent session should result in a clear indication of failure (e.g., returning None or raising a specific exception).
  • The cleanup process should effectively remove sessions that have passed their expiration time.

Edge Cases to Consider:

  • What happens if a session ID is provided that doesn't exist?
  • What happens if a session ID is provided that has expired?
  • How do you ensure session IDs are truly unique?
  • What if the expiration time is set to zero or a negative value?

Examples

Example 1: Basic Session Operations

from datetime import timedelta

# Assume SessionManager is initialized with a default expiration of 30 minutes
session_manager = SessionManager(default_expiration=timedelta(minutes=30))

# 1. Create a new session
session_id = session_manager.create_session()
print(f"Created session ID: {session_id}")
# Expected Output: (a unique string like 'abc123xyz')

# 2. Store data in the session
session_manager.set_session_data(session_id, "username", "Alice")
session_manager.set_session_data(session_id, "user_id", 123)
print(f"Stored data for session {session_id}")

# 3. Retrieve data from the session
username = session_manager.get_session_data(session_id, "username")
user_id = session_manager.get_session_data(session_id, "user_id")
print(f"Retrieved username: {username}, user_id: {user_id}")
# Expected Output: Retrieved username: Alice, user_id: 123

# 4. Update data
session_manager.set_session_data(session_id, "username", "Alice Smith")
print(f"Updated username: {session_manager.get_session_data(session_id, 'username')}")
# Expected Output: Updated username: Alice Smith

# 5. Delete data
session_manager.delete_session_data(session_id, "user_id")
print(f"User ID after deletion: {session_manager.get_session_data(session_id, 'user_id')}")
# Expected Output: User ID after deletion: None

Example 2: Handling Non-Existent and Expired Sessions

from datetime import timedelta, datetime, timezone

session_manager = SessionManager(default_expiration=timedelta(seconds=5))

# Create a session
session_id = session_manager.create_session()

# Try to access data from a non-existent session
non_existent_id = "invalid_session_id"
print(f"Data from non-existent session: {session_manager.get_session_data(non_existent_id, 'some_key')}")
# Expected Output: Data from non-existent session: None

# Wait for the session to expire
import time
time.sleep(6)

# Try to access data from the expired session
print(f"Data from expired session: {session_manager.get_session_data(session_id, 'username')}")
# Expected Output: Data from expired session: None

# Attempt to set data on an expired session should ideally fail or be ignored
session_manager.set_session_data(session_id, "new_key", "new_value")
print(f"Data from expired session after setting: {session_manager.get_session_data(session_id, 'new_key')}")
# Expected Output: Data from expired session after setting: None

Example 3: Session Cleanup

from datetime import timedelta
import threading

# Initialize with a short expiration and a short cleanup interval
session_manager = SessionManager(default_expiration=timedelta(seconds=2), cleanup_interval=timedelta(seconds=1))
session_manager.start_cleanup_thread() # Assume this starts the background thread

session_id_1 = session_manager.create_session()
session_id_2 = session_manager.create_session()

print(f"Created sessions: {session_id_1}, {session_id_2}")

time.sleep(3) # Wait for sessions to expire

print("Checking for sessions after expiration...")
# Manually trigger cleanup or rely on the interval
session_manager.cleanup_expired_sessions()

print(f"Session {session_id_1} exists: {session_manager.get_session_data(session_id_1, 'any_key') is not None}")
print(f"Session {session_id_2} exists: {session_manager.get_session_data(session_id_2, 'any_key') is not None}")
# Expected Output:
# Session <session_id_1> exists: False
# Session <session_id_2> exists: False

session_manager.stop_cleanup_thread() # Clean up the thread when done

Constraints

  • Session IDs must be unique strings. A UUID-based approach is recommended.
  • Session data can consist of any JSON-serializable Python data types (strings, numbers, booleans, lists, dictionaries, None).
  • The SessionManager should be thread-safe to handle concurrent access from multiple requests if used in a multi-threaded environment.
  • The cleanup process should not block the main thread.
  • The cleanup_interval should be a timedelta object.
  • The default_expiration should be a timedelta object.

Notes

  • Consider how you will generate unique session IDs. Python's uuid module is a good choice.
  • You will need a way to store sessions. A dictionary where keys are session IDs and values are session objects (containing data and expiration timestamp) is a common approach.
  • For thread safety, you will likely need to use locks.
  • The cleanup mechanism could be implemented using a threading.Timer or a background threading.Thread that periodically checks for expired sessions.
  • Think about how you will store the timestamp of when a session was last accessed or created to determine expiration.
  • The problem statement mentions "automatic cleanup". This implies a background process. You should provide a way to start and stop this process.
Loading editor...
python