From a7bde85101a7ecd4fcd2ec685d96d05440334ef3 Mon Sep 17 00:00:00 2001 From: abhi Date: Fri, 7 Feb 2025 14:04:11 +0000 Subject: [PATCH 01/10] concurrent in-store changes --- hug/middleware.py | 18 +++++++++++++++--- hug/store.py | 20 ++++++++++++++++++-- tests/test_middleware.py | 2 +- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/hug/middleware.py b/hug/middleware.py index ea522794..ecd751b3 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -18,6 +18,7 @@ """ from __future__ import absolute_import +from concurrent.futures import ThreadPoolExecutor import logging import re @@ -50,6 +51,7 @@ class SessionMiddleware(object): "cookie_path", "cookie_secure", "cookie_http_only", + "session_data_executor" ) def __init__( @@ -63,6 +65,7 @@ def __init__( cookie_path=None, cookie_secure=True, cookie_http_only=True, + max_workers = 10 ): self.store = store self.context_name = context_name @@ -73,10 +76,16 @@ def __init__( self.cookie_path = cookie_path self.cookie_secure = cookie_secure self.cookie_http_only = cookie_http_only + self.session_data_executor = ThreadPoolExecutor(max_workers==max_workers) def generate_sid(self): """Generate a UUID4 string.""" return str(uuid.uuid4()) + + def get_session_data(self, sid): + if self.store.exists(sid): + return self.store.get(sid) + return {} def process_request(self, request, response): """Get session ID from cookie, load corresponding session data from coupled store and inject session data into @@ -85,8 +94,9 @@ def process_request(self, request, response): sid = request.cookies.get(self.cookie_name, None) data = {} if sid is not None: - if self.store.exists(sid): - data = self.store.get(sid) + future = self.session_data_executor.submit(self.get_session_data, sid) + data = future.result() + request.context.update({self.context_name: data}) def process_response(self, request, response, resource, req_succeeded): @@ -95,7 +105,9 @@ def process_response(self, request, response, resource, req_succeeded): if sid is None or not self.store.exists(sid): sid = self.generate_sid() - self.store.set(sid, request.context.get(self.context_name, {})) + # Session state might change for multiple users/windows, we will be able to update the store parallely + self.session_data_executor.submit(self.store.set, sid, request.context.get(self.context_name, {})) + response.set_cookie( self.cookie_name, sid, diff --git a/hug/store.py b/hug/store.py index 0f28b346..4f596ce9 100644 --- a/hug/store.py +++ b/hug/store.py @@ -20,18 +20,21 @@ """ from hug.exceptions import StoreKeyNotFound +from concurrent.futures import ThreadPoolExecutor class InMemoryStore: """ Naive store class which can be used for the session middleware and unit tests. - It is not thread-safe and no data will survive the lifecycle of the hug process. + ~~It is not thread-safe and no data will survive the lifecycle of the hug process.~~ + It is thread-safe and data will survive the lifecycle of the hug process. Regard this as a blueprint for more useful and probably more complex store implementations, for example stores which make use of databases like Redis, PostgreSQL or others. """ - def __init__(self): + def __init__(self, max_workers=10): self._data = {} + self._executor = ThreadPoolExecutor(max_workers=max_workers) def get(self, key): """Get data for given store key. Raise hug.exceptions.StoreKeyNotFound if key does not exist.""" @@ -53,3 +56,16 @@ def delete(self, key): """Delete data for given store key.""" if key in self._data: del self._data[key] + + # Add async methods to be followed by milldleware to ensure parallelism + def async_get(self, key): + return self._executor.submit(self.get, key) + + def async_set(self, key, data): + return self._executor.submit(self.set, key, data) + + def async_delete(self, key): + return self._executor.submit(self.delete, key) + + def async_exists(self, key): + return self._executor.submit(self.exists, key) diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 219e49fd..1295973b 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -46,7 +46,7 @@ def get_cookies(response): # Add middleware session_store = InMemoryStore() - middleware = SessionMiddleware(session_store, cookie_name="test-sid") + middleware = SessionMiddleware(session_store, cookie_name="test-sid", max_workers=4) __hug__.http.add_middleware(middleware) # Get cookies from response From 0ec11a185b5bbb1219ae8f750e84c25b99b3df14 Mon Sep 17 00:00:00 2001 From: abhi Date: Mon, 10 Feb 2025 13:34:55 +0000 Subject: [PATCH 02/10] Multi db session support --- benchmarks/http/requirements.txt | 22 +++++----- hug/middleware.py | 2 +- hug/store_wrapper.py | 27 ++++++++++++ hug/{store.py => stores/inmemory_store.py} | 30 +++++++------ hug/stores/mongo_store.py | 40 +++++++++++++++++ hug/stores/redis_store.py | 50 ++++++++++++++++++++++ hug/stores/sql_store.py | 47 ++++++++++++++++++++ tests/test_middleware.py | 2 +- tests/test_store.py | 2 +- 9 files changed, 197 insertions(+), 25 deletions(-) create mode 100644 hug/store_wrapper.py rename hug/{store.py => stores/inmemory_store.py} (80%) create mode 100644 hug/stores/mongo_store.py create mode 100644 hug/stores/redis_store.py create mode 100644 hug/stores/sql_store.py diff --git a/benchmarks/http/requirements.txt b/benchmarks/http/requirements.txt index 6fee467c..dd66bdea 100644 --- a/benchmarks/http/requirements.txt +++ b/benchmarks/http/requirements.txt @@ -1,10 +1,12 @@ -Cython==0.24 -bobo -bottle -cherrypy -falcon -flask -muffin -pyramid -hug -gunicorn +Cython>=0.24,<0.26 +bobo==0.8.0 +bottle==0.14 +cherrypy==18.6.1 +falcon==3.1.0 +flask==2.1.3 +muffin==0.86.0 +pyramid==2.0 +hug==2.6.1 +gunicorn==20.1.0 +redis==4.5.1 +pymongo==4.3.3 \ No newline at end of file diff --git a/hug/middleware.py b/hug/middleware.py index ecd751b3..bde48be6 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -42,7 +42,7 @@ class SessionMiddleware(object): """ __slots__ = ( - "store", + "store_wrapper", "context_name", "cookie_name", "cookie_expires", diff --git a/hug/store_wrapper.py b/hug/store_wrapper.py new file mode 100644 index 00000000..77ddcba9 --- /dev/null +++ b/hug/store_wrapper.py @@ -0,0 +1,27 @@ +from hug.stores.inmemory_store import InMemoryStore +from hug.stores.inmemory_store import RedisStore +from hug.stores.inmemory_store import MongoDBStore +from hug.stores.inmemory_store import SQLStore + +class StoreWrapper: + def __init__(self, store_type='inmemory', **kwargs): + if store_type == 'redis': + self.store = RedisStore(**kwargs) + elif store_type == 'mongodb': + self.store = MongoDBStore(**kwargs) + elif store_type == 'sql': + self.store = SQLStore(**kwargs) + else: + self.store = InMemoryStore(**kwargs) + + def get(self, key): + return self.store.get(key) + + def set(self, key, data): + self.store.set(key, data) + + def exists(self, key): + return self.store.exists(key) + + def delete(self, key): + self.store.delete(key) \ No newline at end of file diff --git a/hug/store.py b/hug/stores/inmemory_store.py similarity index 80% rename from hug/store.py rename to hug/stores/inmemory_store.py index 4f596ce9..c9140cd3 100644 --- a/hug/store.py +++ b/hug/stores/inmemory_store.py @@ -21,6 +21,7 @@ """ from hug.exceptions import StoreKeyNotFound from concurrent.futures import ThreadPoolExecutor +import threading class InMemoryStore: @@ -34,28 +35,33 @@ class InMemoryStore: def __init__(self, max_workers=10): self._data = {} + self._lock = threading.Lock() self._executor = ThreadPoolExecutor(max_workers=max_workers) def get(self, key): """Get data for given store key. Raise hug.exceptions.StoreKeyNotFound if key does not exist.""" - try: - data = self._data[key] - except KeyError: - raise StoreKeyNotFound(key) - return data + with self._lock: + try: + data = self._data[key] + except KeyError: + raise StoreKeyNotFound(key) + return data def exists(self, key): - """Return whether key exists or not.""" - return key in self._data + with self._lock: + """Return whether key exists or not.""" + return key in self._data def set(self, key, data): - """Set data object for given store key.""" - self._data[key] = data + with self._lock: + """Set data object for given store key.""" + self._data[key] = data def delete(self, key): - """Delete data for given store key.""" - if key in self._data: - del self._data[key] + with self._lock: + """Delete data for given store key.""" + if key in self._data: + del self._data[key] # Add async methods to be followed by milldleware to ensure parallelism def async_get(self, key): diff --git a/hug/stores/mongo_store.py b/hug/stores/mongo_store.py new file mode 100644 index 00000000..b914a855 --- /dev/null +++ b/hug/stores/mongo_store.py @@ -0,0 +1,40 @@ +from pymongo import MongoClient +import logging +import uuid + +class MongoDBStore: + def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") + self._client = MongoClient(uri) + self._collection = self._client[db_name][collection_name] + self._collection.create_index("createdAt", expireAfterSeconds=ttl) + + def get(self, key): + try: + return self._collection.find_one({"_id": key}) or {} + except Exception as e: + self._logger.exception(f"MongoDB exception: {e}") + return {} + + def set(self, key, data): + try: + data['createdAt'] = uuid.uuid1().time + self._collection.update_one({"_id": key}, {"$set": data}, upsert=True) + except Exception as e: + self._logger.exception(f"MongoDB exception: {e}") + raise + + def exists(self, key): + try: + return self._collection.count_documents({"_id": key}, limit=1) > 0 + except Exception as e: + self._logger.exception(f"MongoDB exception: {e}") + raise + + + def delete(self, key): + try: + self._collection.delete_one({"_id": key}) + except Exception as e: + self._logger.exception(f"MongoDB exception: {e}") + raise \ No newline at end of file diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py new file mode 100644 index 00000000..5fe5b61e --- /dev/null +++ b/hug/stores/redis_store.py @@ -0,0 +1,50 @@ +import redis +import logging + +class RedisStore: + def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") + self._client = redis.StrictRedis(host=host, port=port, db=db, decode_responses=True) + self._ttl = ttl + + def get(self, key): + try: + return self._client.hgetall(key) + except redis.RedisError as e: + self._logger.error(f"Redis Error: {e}") + return {} + except Exception as e: + self._logger.exception(f"Redis Exception: {e}") + return {} + + def set(self, key, data): + try: + self._client.hmset(key, data) + self._client.expire(key, self._ttl) + except redis.RedisError as e: + self._logger.error(f"Redis Error: {e}") + raise + except Exception as e: + self._logger.exception(f"Redis Exception: {e}") + raise + + def exists(self, key): + try: + return self._client.exists(key) + except redis.RedisError as e: + self._logger.error(f"Redis Error: {e}") + raise + except Exception as e: + self._logger.exception(f"Redis Exception: {e}") + raise + + def delete(self, key): + try: + self._client.delete(key) + except redis.RedisError as e: + self._logger.exception(f"Redis Error: {e}") + raise + except Exception as e: + self._logger.exception(f"Redis Exception: {e}") + raise + \ No newline at end of file diff --git a/hug/stores/sql_store.py b/hug/stores/sql_store.py new file mode 100644 index 00000000..cd36e647 --- /dev/null +++ b/hug/stores/sql_store.py @@ -0,0 +1,47 @@ +import sqlite3 +import json +import logging + +class SQLStore: + def __init__(self, db_path=':memory:', logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") + self._conn = sqlite3.connect(db_path, check_same_thread=False) + self._cursor = self._conn.cursor() + self._cursor.execute('''CREATE TABLE IF NOT EXISTS sessions ( + id TEXT PRIMARY KEY, + data TEXT + )''') + self._conn.commit() + + def get(self, key): + try: + self._cursor.execute("SELECT data FROM sessions WHERE id = ?", (key,)) + row = self._cursor.fetchone() + return json.loads(row[0]) if row else {} + except Exception as e: + self._logger.exception(f"SQL Exception: {e}") + return {} + + def set(self, key, data): + try: + self._cursor.execute("REPLACE INTO sessions (id, data) VALUES (?, ?)", (key, json.dumps(data))) # Serialize JSON + self._conn.commit() + except Exception as e: + self._logger.exception(f"SQL Exception: {e}") + raise + + def exists(self, key): + try: + self._cursor.execute("SELECT 1 FROM sessions WHERE id = ?", (key,)) + return self._cursor.fetchone() is not None + except Exception as e: + self._logger.exception(f"SQL Exception: {e}") + raise + + def delete(self, key): + try: + self._cursor.execute("DELETE FROM sessions WHERE id = ?", (key,)) + self._conn.commit() + except Exception as e: + self._logger.exception(f"SQL Exception: {e}") + raise \ No newline at end of file diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 1295973b..a65b6661 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -24,7 +24,7 @@ import hug from hug.exceptions import SessionNotFound from hug.middleware import CORSMiddleware, LogMiddleware, SessionMiddleware -from hug.store import InMemoryStore +from hug.stores.inmemory_store import InMemoryStore api = hug.API(__name__) diff --git a/tests/test_store.py b/tests/test_store.py index 1ae04fd5..88e14ee6 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -22,7 +22,7 @@ import pytest from hug.exceptions import StoreKeyNotFound -from hug.store import InMemoryStore +from hug.stores.inmemory_store import InMemoryStore stores_to_test = [InMemoryStore()] From 0edaca68a75fcf54384f1983a637e726b40be89f Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 18:09:10 +0000 Subject: [PATCH 03/10] bug fix --- hug/middleware.py | 10 +++++----- hug/{store_wrapper.py => store.py} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename hug/{store_wrapper.py => store.py} (100%) diff --git a/hug/middleware.py b/hug/middleware.py index bde48be6..093c25bb 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -24,7 +24,7 @@ import re import uuid from datetime import datetime - +from hug.store import StoreWrapper class SessionMiddleware(object): """Simple session middleware. @@ -42,7 +42,7 @@ class SessionMiddleware(object): """ __slots__ = ( - "store_wrapper", + "store", "context_name", "cookie_name", "cookie_expires", @@ -56,7 +56,7 @@ class SessionMiddleware(object): def __init__( self, - store, + store_type="inmemory", context_name="session", cookie_name="sid", cookie_expires=None, @@ -67,7 +67,7 @@ def __init__( cookie_http_only=True, max_workers = 10 ): - self.store = store + self.store = StoreWrapper(store_type) self.context_name = context_name self.cookie_name = cookie_name self.cookie_expires = cookie_expires @@ -76,7 +76,7 @@ def __init__( self.cookie_path = cookie_path self.cookie_secure = cookie_secure self.cookie_http_only = cookie_http_only - self.session_data_executor = ThreadPoolExecutor(max_workers==max_workers) + self.session_data_executor = ThreadPoolExecutor(max_workers=max_workers) def generate_sid(self): """Generate a UUID4 string.""" diff --git a/hug/store_wrapper.py b/hug/store.py similarity index 100% rename from hug/store_wrapper.py rename to hug/store.py From 8a83638dfb87c6d35a038f074c78b0a99159657f Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 18:21:29 +0000 Subject: [PATCH 04/10] fixed imports --- hug/store.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hug/store.py b/hug/store.py index 77ddcba9..91d8558e 100644 --- a/hug/store.py +++ b/hug/store.py @@ -1,7 +1,7 @@ from hug.stores.inmemory_store import InMemoryStore -from hug.stores.inmemory_store import RedisStore -from hug.stores.inmemory_store import MongoDBStore -from hug.stores.inmemory_store import SQLStore +from hug.stores.redis_store import RedisStore +from hug.stores.mongo_store import MongoDBStore +from hug.stores.sql_store import SQLStore class StoreWrapper: def __init__(self, store_type='inmemory', **kwargs): From e1cf570b40c9486d9efdac6be36110f299bb5719 Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 18:34:16 +0000 Subject: [PATCH 05/10] exception handling fix --- hug/stores/redis_store.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py index 5fe5b61e..14fb525d 100644 --- a/hug/stores/redis_store.py +++ b/hug/stores/redis_store.py @@ -11,7 +11,7 @@ def get(self, key): try: return self._client.hgetall(key) except redis.RedisError as e: - self._logger.error(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") return {} except Exception as e: self._logger.exception(f"Redis Exception: {e}") @@ -22,7 +22,7 @@ def set(self, key, data): self._client.hmset(key, data) self._client.expire(key, self._ttl) except redis.RedisError as e: - self._logger.error(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") raise except Exception as e: self._logger.exception(f"Redis Exception: {e}") @@ -32,7 +32,7 @@ def exists(self, key): try: return self._client.exists(key) except redis.RedisError as e: - self._logger.error(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") raise except Exception as e: self._logger.exception(f"Redis Exception: {e}") From ca58f41b7fb081d06f72bd7d49abf4ed12029cbc Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 19:07:38 +0000 Subject: [PATCH 06/10] logger init --- hug/logger_mixin.py | 11 +++++++++++ hug/stores/mongo_store.py | 6 +++--- hug/stores/redis_store.py | 7 ++++--- hug/stores/sql_store.py | 6 +++--- 4 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 hug/logger_mixin.py diff --git a/hug/logger_mixin.py b/hug/logger_mixin.py new file mode 100644 index 00000000..df16f790 --- /dev/null +++ b/hug/logger_mixin.py @@ -0,0 +1,11 @@ +import logging + +class LoggerMixin: + def __init__(self, logger_name="hug"): + self._logger = logging.getLogger(logger_name) + if not self._logger.handlers: + handler = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + self._logger.addHandler(handler) + self._logger.setLevel(logging.DEBUG) \ No newline at end of file diff --git a/hug/stores/mongo_store.py b/hug/stores/mongo_store.py index b914a855..14623901 100644 --- a/hug/stores/mongo_store.py +++ b/hug/stores/mongo_store.py @@ -1,10 +1,10 @@ from pymongo import MongoClient -import logging +from hug.logger_mixin import LoggerMixin import uuid class MongoDBStore: - def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger=None): - self._logger = logger if logger is not None else logging.getLogger("hug") + def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger_name="hug"): + super().__init__(logger_name) self._client = MongoClient(uri) self._collection = self._client[db_name][collection_name] self._collection.create_index("createdAt", expireAfterSeconds=ttl) diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py index 14fb525d..d9da40e1 100644 --- a/hug/stores/redis_store.py +++ b/hug/stores/redis_store.py @@ -1,9 +1,10 @@ import redis -import logging +from hug.logger_mixin import LoggerMixin class RedisStore: - def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger=None): - self._logger = logger if logger is not None else logging.getLogger("hug") + def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger_name="hug"): + super().__init__(logger_name) + self._client = redis.StrictRedis(host=host, port=port, db=db, decode_responses=True) self._ttl = ttl diff --git a/hug/stores/sql_store.py b/hug/stores/sql_store.py index cd36e647..54a18332 100644 --- a/hug/stores/sql_store.py +++ b/hug/stores/sql_store.py @@ -1,10 +1,10 @@ import sqlite3 import json -import logging +from hug.logger_mixin import LoggerMixin class SQLStore: - def __init__(self, db_path=':memory:', logger=None): - self._logger = logger if logger is not None else logging.getLogger("hug") + def __init__(self, db_path=':memory:', logger_name="hug"): + super().__init__(logger_name) self._conn = sqlite3.connect(db_path, check_same_thread=False) self._cursor = self._conn.cursor() self._cursor.execute('''CREATE TABLE IF NOT EXISTS sessions ( From 72daeaeb396f02f72eeda703ecc01e34e5983708 Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 19:22:32 +0000 Subject: [PATCH 07/10] testing print --- hug/stores/mongo_store.py | 14 +++++++------- hug/stores/redis_store.py | 23 +++++++++++------------ hug/stores/sql_store.py | 14 +++++++------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/hug/stores/mongo_store.py b/hug/stores/mongo_store.py index 14623901..0518ddb4 100644 --- a/hug/stores/mongo_store.py +++ b/hug/stores/mongo_store.py @@ -1,10 +1,10 @@ from pymongo import MongoClient -from hug.logger_mixin import LoggerMixin +import logging import uuid class MongoDBStore: - def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger_name="hug"): - super().__init__(logger_name) + def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") self._client = MongoClient(uri) self._collection = self._client[db_name][collection_name] self._collection.create_index("createdAt", expireAfterSeconds=ttl) @@ -13,7 +13,7 @@ def get(self, key): try: return self._collection.find_one({"_id": key}) or {} except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + print(f"MongoDB exception: {e}") return {} def set(self, key, data): @@ -21,14 +21,14 @@ def set(self, key, data): data['createdAt'] = uuid.uuid1().time self._collection.update_one({"_id": key}, {"$set": data}, upsert=True) except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + print(f"MongoDB exception: {e}") raise def exists(self, key): try: return self._collection.count_documents({"_id": key}, limit=1) > 0 except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + print(f"MongoDB exception: {e}") raise @@ -36,5 +36,5 @@ def delete(self, key): try: self._collection.delete_one({"_id": key}) except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + print(f"MongoDB exception: {e}") raise \ No newline at end of file diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py index d9da40e1..b6c8d29d 100644 --- a/hug/stores/redis_store.py +++ b/hug/stores/redis_store.py @@ -1,10 +1,9 @@ import redis -from hug.logger_mixin import LoggerMixin +import logging class RedisStore: - def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger_name="hug"): - super().__init__(logger_name) - + def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") self._client = redis.StrictRedis(host=host, port=port, db=db, decode_responses=True) self._ttl = ttl @@ -12,10 +11,10 @@ def get(self, key): try: return self._client.hgetall(key) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + print(f"Redis Error: {e}") return {} except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + print(f"Redis Exception: {e}") return {} def set(self, key, data): @@ -23,29 +22,29 @@ def set(self, key, data): self._client.hmset(key, data) self._client.expire(key, self._ttl) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + print(f"Redis Error: {e}") raise except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + print(f"Redis Exception: {e}") raise def exists(self, key): try: return self._client.exists(key) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + print(f"Redis Error: {e}") raise except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + print(f"Redis Exception: {e}") raise def delete(self, key): try: self._client.delete(key) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + print(f"Redis Error: {e}") raise except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + print(f"Redis Exception: {e}") raise \ No newline at end of file diff --git a/hug/stores/sql_store.py b/hug/stores/sql_store.py index 54a18332..28bffd67 100644 --- a/hug/stores/sql_store.py +++ b/hug/stores/sql_store.py @@ -1,10 +1,10 @@ import sqlite3 import json -from hug.logger_mixin import LoggerMixin +import logging class SQLStore: - def __init__(self, db_path=':memory:', logger_name="hug"): - super().__init__(logger_name) + def __init__(self, db_path=':memory:', logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") self._conn = sqlite3.connect(db_path, check_same_thread=False) self._cursor = self._conn.cursor() self._cursor.execute('''CREATE TABLE IF NOT EXISTS sessions ( @@ -19,7 +19,7 @@ def get(self, key): row = self._cursor.fetchone() return json.loads(row[0]) if row else {} except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + print(f"SQL Exception: {e}") return {} def set(self, key, data): @@ -27,7 +27,7 @@ def set(self, key, data): self._cursor.execute("REPLACE INTO sessions (id, data) VALUES (?, ?)", (key, json.dumps(data))) # Serialize JSON self._conn.commit() except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + print(f"SQL Exception: {e}") raise def exists(self, key): @@ -35,7 +35,7 @@ def exists(self, key): self._cursor.execute("SELECT 1 FROM sessions WHERE id = ?", (key,)) return self._cursor.fetchone() is not None except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + print(f"SQL Exception: {e}") raise def delete(self, key): @@ -43,5 +43,5 @@ def delete(self, key): self._cursor.execute("DELETE FROM sessions WHERE id = ?", (key,)) self._conn.commit() except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + print(f"SQL Exception: {e}") raise \ No newline at end of file From 3b0ea5efd53f33ddef522db0b650d36f4577c23e Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 19:25:26 +0000 Subject: [PATCH 08/10] Revert "testing print" This reverts commit 72daeaeb396f02f72eeda703ecc01e34e5983708. --- hug/stores/mongo_store.py | 14 +++++++------- hug/stores/redis_store.py | 23 ++++++++++++----------- hug/stores/sql_store.py | 14 +++++++------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/hug/stores/mongo_store.py b/hug/stores/mongo_store.py index 0518ddb4..14623901 100644 --- a/hug/stores/mongo_store.py +++ b/hug/stores/mongo_store.py @@ -1,10 +1,10 @@ from pymongo import MongoClient -import logging +from hug.logger_mixin import LoggerMixin import uuid class MongoDBStore: - def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger=None): - self._logger = logger if logger is not None else logging.getLogger("hug") + def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger_name="hug"): + super().__init__(logger_name) self._client = MongoClient(uri) self._collection = self._client[db_name][collection_name] self._collection.create_index("createdAt", expireAfterSeconds=ttl) @@ -13,7 +13,7 @@ def get(self, key): try: return self._collection.find_one({"_id": key}) or {} except Exception as e: - print(f"MongoDB exception: {e}") + self._logger.exception(f"MongoDB exception: {e}") return {} def set(self, key, data): @@ -21,14 +21,14 @@ def set(self, key, data): data['createdAt'] = uuid.uuid1().time self._collection.update_one({"_id": key}, {"$set": data}, upsert=True) except Exception as e: - print(f"MongoDB exception: {e}") + self._logger.exception(f"MongoDB exception: {e}") raise def exists(self, key): try: return self._collection.count_documents({"_id": key}, limit=1) > 0 except Exception as e: - print(f"MongoDB exception: {e}") + self._logger.exception(f"MongoDB exception: {e}") raise @@ -36,5 +36,5 @@ def delete(self, key): try: self._collection.delete_one({"_id": key}) except Exception as e: - print(f"MongoDB exception: {e}") + self._logger.exception(f"MongoDB exception: {e}") raise \ No newline at end of file diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py index b6c8d29d..d9da40e1 100644 --- a/hug/stores/redis_store.py +++ b/hug/stores/redis_store.py @@ -1,9 +1,10 @@ import redis -import logging +from hug.logger_mixin import LoggerMixin class RedisStore: - def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger=None): - self._logger = logger if logger is not None else logging.getLogger("hug") + def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger_name="hug"): + super().__init__(logger_name) + self._client = redis.StrictRedis(host=host, port=port, db=db, decode_responses=True) self._ttl = ttl @@ -11,10 +12,10 @@ def get(self, key): try: return self._client.hgetall(key) except redis.RedisError as e: - print(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") return {} except Exception as e: - print(f"Redis Exception: {e}") + self._logger.exception(f"Redis Exception: {e}") return {} def set(self, key, data): @@ -22,29 +23,29 @@ def set(self, key, data): self._client.hmset(key, data) self._client.expire(key, self._ttl) except redis.RedisError as e: - print(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") raise except Exception as e: - print(f"Redis Exception: {e}") + self._logger.exception(f"Redis Exception: {e}") raise def exists(self, key): try: return self._client.exists(key) except redis.RedisError as e: - print(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") raise except Exception as e: - print(f"Redis Exception: {e}") + self._logger.exception(f"Redis Exception: {e}") raise def delete(self, key): try: self._client.delete(key) except redis.RedisError as e: - print(f"Redis Error: {e}") + self._logger.exception(f"Redis Error: {e}") raise except Exception as e: - print(f"Redis Exception: {e}") + self._logger.exception(f"Redis Exception: {e}") raise \ No newline at end of file diff --git a/hug/stores/sql_store.py b/hug/stores/sql_store.py index 28bffd67..54a18332 100644 --- a/hug/stores/sql_store.py +++ b/hug/stores/sql_store.py @@ -1,10 +1,10 @@ import sqlite3 import json -import logging +from hug.logger_mixin import LoggerMixin class SQLStore: - def __init__(self, db_path=':memory:', logger=None): - self._logger = logger if logger is not None else logging.getLogger("hug") + def __init__(self, db_path=':memory:', logger_name="hug"): + super().__init__(logger_name) self._conn = sqlite3.connect(db_path, check_same_thread=False) self._cursor = self._conn.cursor() self._cursor.execute('''CREATE TABLE IF NOT EXISTS sessions ( @@ -19,7 +19,7 @@ def get(self, key): row = self._cursor.fetchone() return json.loads(row[0]) if row else {} except Exception as e: - print(f"SQL Exception: {e}") + self._logger.exception(f"SQL Exception: {e}") return {} def set(self, key, data): @@ -27,7 +27,7 @@ def set(self, key, data): self._cursor.execute("REPLACE INTO sessions (id, data) VALUES (?, ?)", (key, json.dumps(data))) # Serialize JSON self._conn.commit() except Exception as e: - print(f"SQL Exception: {e}") + self._logger.exception(f"SQL Exception: {e}") raise def exists(self, key): @@ -35,7 +35,7 @@ def exists(self, key): self._cursor.execute("SELECT 1 FROM sessions WHERE id = ?", (key,)) return self._cursor.fetchone() is not None except Exception as e: - print(f"SQL Exception: {e}") + self._logger.exception(f"SQL Exception: {e}") raise def delete(self, key): @@ -43,5 +43,5 @@ def delete(self, key): self._cursor.execute("DELETE FROM sessions WHERE id = ?", (key,)) self._conn.commit() except Exception as e: - print(f"SQL Exception: {e}") + self._logger.exception(f"SQL Exception: {e}") raise \ No newline at end of file From 2ff0c3fb7b3619473b4c2655c5d7440240e7ab64 Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 19:26:10 +0000 Subject: [PATCH 09/10] Revert "logger init" This reverts commit ca58f41b7fb081d06f72bd7d49abf4ed12029cbc. --- hug/logger_mixin.py | 11 ----------- hug/stores/mongo_store.py | 6 +++--- hug/stores/redis_store.py | 7 +++---- hug/stores/sql_store.py | 6 +++--- 4 files changed, 9 insertions(+), 21 deletions(-) delete mode 100644 hug/logger_mixin.py diff --git a/hug/logger_mixin.py b/hug/logger_mixin.py deleted file mode 100644 index df16f790..00000000 --- a/hug/logger_mixin.py +++ /dev/null @@ -1,11 +0,0 @@ -import logging - -class LoggerMixin: - def __init__(self, logger_name="hug"): - self._logger = logging.getLogger(logger_name) - if not self._logger.handlers: - handler = logging.StreamHandler() - formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') - handler.setFormatter(formatter) - self._logger.addHandler(handler) - self._logger.setLevel(logging.DEBUG) \ No newline at end of file diff --git a/hug/stores/mongo_store.py b/hug/stores/mongo_store.py index 14623901..b914a855 100644 --- a/hug/stores/mongo_store.py +++ b/hug/stores/mongo_store.py @@ -1,10 +1,10 @@ from pymongo import MongoClient -from hug.logger_mixin import LoggerMixin +import logging import uuid class MongoDBStore: - def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger_name="hug"): - super().__init__(logger_name) + def __init__(self, uri='mongodb://localhost:27017/', db_name='session_db', collection_name='sessions', ttl=3600, logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") self._client = MongoClient(uri) self._collection = self._client[db_name][collection_name] self._collection.create_index("createdAt", expireAfterSeconds=ttl) diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py index d9da40e1..14fb525d 100644 --- a/hug/stores/redis_store.py +++ b/hug/stores/redis_store.py @@ -1,10 +1,9 @@ import redis -from hug.logger_mixin import LoggerMixin +import logging class RedisStore: - def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger_name="hug"): - super().__init__(logger_name) - + def __init__(self, host='localhost', port=6379, db=0, ttl=3600, logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") self._client = redis.StrictRedis(host=host, port=port, db=db, decode_responses=True) self._ttl = ttl diff --git a/hug/stores/sql_store.py b/hug/stores/sql_store.py index 54a18332..cd36e647 100644 --- a/hug/stores/sql_store.py +++ b/hug/stores/sql_store.py @@ -1,10 +1,10 @@ import sqlite3 import json -from hug.logger_mixin import LoggerMixin +import logging class SQLStore: - def __init__(self, db_path=':memory:', logger_name="hug"): - super().__init__(logger_name) + def __init__(self, db_path=':memory:', logger=None): + self._logger = logger if logger is not None else logging.getLogger("hug") self._conn = sqlite3.connect(db_path, check_same_thread=False) self._cursor = self._conn.cursor() self._cursor.execute('''CREATE TABLE IF NOT EXISTS sessions ( From c4c9a431695eb49f6566a86cae55af6bee954208 Mon Sep 17 00:00:00 2001 From: abhi Date: Wed, 12 Feb 2025 19:34:11 +0000 Subject: [PATCH 10/10] fixed -string issue --- hug/stores/mongo_store.py | 8 ++++---- hug/stores/redis_store.py | 16 ++++++++-------- hug/stores/sql_store.py | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hug/stores/mongo_store.py b/hug/stores/mongo_store.py index b914a855..027cc77c 100644 --- a/hug/stores/mongo_store.py +++ b/hug/stores/mongo_store.py @@ -13,7 +13,7 @@ def get(self, key): try: return self._collection.find_one({"_id": key}) or {} except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + self._logger.exception("MongoDB exception: {}".format(str(e))) return {} def set(self, key, data): @@ -21,14 +21,14 @@ def set(self, key, data): data['createdAt'] = uuid.uuid1().time self._collection.update_one({"_id": key}, {"$set": data}, upsert=True) except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + self._logger.exception("MongoDB exception: {}".format(str(e))) raise def exists(self, key): try: return self._collection.count_documents({"_id": key}, limit=1) > 0 except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + self._logger.exception("MongoDB exception: {}".format(str(e))) raise @@ -36,5 +36,5 @@ def delete(self, key): try: self._collection.delete_one({"_id": key}) except Exception as e: - self._logger.exception(f"MongoDB exception: {e}") + self._logger.exception("MongoDB exception: {}".format(str(e))) raise \ No newline at end of file diff --git a/hug/stores/redis_store.py b/hug/stores/redis_store.py index 14fb525d..363c1192 100644 --- a/hug/stores/redis_store.py +++ b/hug/stores/redis_store.py @@ -11,10 +11,10 @@ def get(self, key): try: return self._client.hgetall(key) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + self._logger.exception("Redis Error: {}".format(str(e))) return {} except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + self._logger.exception("Redis Exception: {}".format(str(e))) return {} def set(self, key, data): @@ -22,29 +22,29 @@ def set(self, key, data): self._client.hmset(key, data) self._client.expire(key, self._ttl) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + self._logger.exception("Redis Error: {}".format(str(e))) raise except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + self._logger.exception("Redis Exception: {}".format(str(e))) raise def exists(self, key): try: return self._client.exists(key) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + self._logger.exception("Redis Error: {}".format(str(e))) raise except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + self._logger.exception("Redis Exception: {}".format(str(e))) raise def delete(self, key): try: self._client.delete(key) except redis.RedisError as e: - self._logger.exception(f"Redis Error: {e}") + self._logger.exception("Redis Error: {}".format(str(e))) raise except Exception as e: - self._logger.exception(f"Redis Exception: {e}") + self._logger.exception("Redis Exception: {}".format(str(e))) raise \ No newline at end of file diff --git a/hug/stores/sql_store.py b/hug/stores/sql_store.py index cd36e647..1e11c36c 100644 --- a/hug/stores/sql_store.py +++ b/hug/stores/sql_store.py @@ -19,7 +19,7 @@ def get(self, key): row = self._cursor.fetchone() return json.loads(row[0]) if row else {} except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + self._logger.exception("SQL Exception: {}".format(str(e))) return {} def set(self, key, data): @@ -27,7 +27,7 @@ def set(self, key, data): self._cursor.execute("REPLACE INTO sessions (id, data) VALUES (?, ?)", (key, json.dumps(data))) # Serialize JSON self._conn.commit() except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + self._logger.exception("SQL Exception: {}".format(str(e))) raise def exists(self, key): @@ -35,7 +35,7 @@ def exists(self, key): self._cursor.execute("SELECT 1 FROM sessions WHERE id = ?", (key,)) return self._cursor.fetchone() is not None except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + self._logger.exception("SQL Exception: {}".format(str(e))) raise def delete(self, key): @@ -43,5 +43,5 @@ def delete(self, key): self._cursor.execute("DELETE FROM sessions WHERE id = ?", (key,)) self._conn.commit() except Exception as e: - self._logger.exception(f"SQL Exception: {e}") + self._logger.exception("SQL Exception: {}".format(str(e))) raise \ No newline at end of file