Skip to content

Commit

Permalink
get rid of session manager
Browse files Browse the repository at this point in the history
  • Loading branch information
btschwertfeger committed Dec 28, 2024
1 parent 81489af commit 9dc6518
Showing 1 changed file with 104 additions and 117 deletions.
221 changes: 104 additions & 117 deletions kraken/base_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,109 +172,18 @@ def check_batch_status(self: ErrorHandler, data: dict) -> dict:
return data


class SessionManager:
"""
Manages the requests-based session for the sync Spot and Futures clients.
Kraken rejects requests that are older than a certain time without further
information. To avoid this, the session manager creates a new session
every 5 minutes.
"""

HEADERS: Final[dict] = {"User-Agent": "btschwertfeger/python-kraken-sdk"}

def __init__(
self: SessionManager,
*,
proxy: str | None = None,
max_session_age: int = 300,
) -> None:
"""
Initialize the session manager.
:param max_session_age: Maximum session age in seconds
:type max_session_age: int
:param proxy: Proxy URL
:type proxy: str, optional
"""
self.__proxy: str = proxy
self.__max_session_age: int = max_session_age
self.__session: requests.Session = self.create_new_session()
self.__session_start_time: float = time.time()

def create_new_session(self: SessionManager) -> requests.Session:
"""Create a new session."""
session = requests.Session()
session.headers.update(self.HEADERS)
if self.__proxy is not None:
session.proxies.update(
{
"http": self.__proxy,
"https": self.__proxy,
},
)
return session

def get_session(self: SessionManager) -> requests.Session:
"""Get a valid session, recreating if the current one is too old."""
if time.time() - self.__session_start_time > self.__max_session_age:
self.__session.close() # Close the old session
self.__session = self.create_new_session()
self.__session_start_time = time.time()
return self.__session


class AsyncSessionManager:
"""
Manages aiohttp-based sessions for the async Spot and Futures clients.
Kraken rejects requests that are older than a certain time without further
information. To avoid this, the session manager creates a new session
every 5 minutes.
"""

HEADERS: Final[dict] = {"User-Agent": "btschwertfeger/python-kraken-sdk"}

def __init__(
self: AsyncSessionManager,
*,
proxy: str | None = None,
max_session_age: int = 300,
) -> None:
"""
Initialize the session manager.
:param max_session_age: Maximum session age in seconds
:type max_session_age: int
:param proxy: Proxy URL
:type proxy: str, optional
"""
self.__proxy: str = proxy
self.__max_session_age: int = max_session_age
self.__session: aiohttp.ClientSession = self.create_new_session()
self.__session_start_time: float = time.time()

def create_new_session(self: AsyncSessionManager) -> aiohttp.ClientSession:
"""Create a new session."""
return aiohttp.ClientSession(headers=self.HEADERS, proxy=self.__proxy)

async def get_session(self: AsyncSessionManager) -> aiohttp.ClientSession:
"""Get a valid session, recreating if the current one is too old."""
if time.time() - self.__session_start_time > self.__max_session_age:
await self.__session.close()
self.__session = self.create_new_session()
self.__session_start_time = time.time()
return self.__session


class SpotClient:
"""
This class is the base for all Spot clients, handles un-/signed
requests and returns exception handled results.
This class is the base for all Spot clients, handles un-/signed requests and
returns exception handled results.
If you are facing timeout errors on derived clients, you can make use of the
``TIMEOUT`` attribute to deviate from the default ``10`` seconds.
Kraken sometimes rejects requests that are older than a certain time without
further information. To avoid this, the session manager creates a new
session every 5 minutes.
:param key: Spot API public key (default: ``""``)
:type key: str, optional
:param secret: Spot API secret key (default: ``""``)
Expand All @@ -287,6 +196,8 @@ class SpotClient:

URL: str = "https://api.kraken.com"
TIMEOUT: int = 10
MAX_SESSION_AGE: int = 300 # seconds
HEADERS: Final[dict] = {"User-Agent": "btschwertfeger/python-kraken-sdk"}

def __init__( # nosec: B107
self: SpotClient,
Expand All @@ -304,8 +215,28 @@ def __init__( # nosec: B107
self._secret: str = secret
self._use_custom_exceptions: bool = use_custom_exceptions
self._err_handler: ErrorHandler = ErrorHandler()
self.session_manager: SessionManager = SessionManager(proxy=proxy)
self.__session: requests.Session = self.session_manager.create_new_session()
self.__proxy: str | None = proxy
self.__session_start_time: float
self.__create_new_session()

Check warning

Code scanning / CodeQL

`__init__` method calls overridden method Warning

Call to self.
__create_new_session
in __init__ method, which is overridden by
method SpotAsyncClient.__create_new_session
.

def __create_new_session(self: SpotClient) -> None:
"""Create a new session."""
self.__session = requests.Session()
self.__session.headers.update(self.HEADERS)
if self.__proxy is not None:
self.__session.proxies.update(
{
"http": self.__proxy,
"https": self.__proxy,
},
)
self.__session_start_time = time.time()

def __check_renew_session(self: SpotClient) -> None:
"""Check if the session is too old and renew if necessary."""
if time.time() - self.__session_start_time > self.MAX_SESSION_AGE:
self.__session.close() # Close the old session
self.__create_new_session()

def _prepare_request(
self: SpotClient,
Expand Down Expand Up @@ -428,7 +359,7 @@ def request( # noqa: PLR0913 # pylint: disable=too-many-arguments
)

timeout: int = self.TIMEOUT if timeout != 10 else timeout # type: ignore[no-redef]
self.__session = self.session_manager.get_session()
self.__check_renew_session()

if method in {"GET", "DELETE"}:
return self.__check_response_data(
Expand Down Expand Up @@ -558,6 +489,10 @@ class SpotAsyncClient(SpotClient):
If you are facing timeout errors on derived clients, you can make use of the
``TIMEOUT`` attribute to deviate from the default ``10`` seconds.
Kraken sometimes rejects requests that are older than a certain time without
further information. To avoid this, the session manager creates a new
session every 5 minutes.
:param key: Spot API public key (default: ``""``)
:type key: str, optional
:param secret: Spot API secret key (default: ``""``)
Expand All @@ -583,10 +518,21 @@ def __init__( # nosec: B107
url=url,
use_custom_exceptions=use_custom_exceptions,
)
self.session_manager: AsyncSessionManager = AsyncSessionManager(proxy=proxy) # type: ignore[assignment]
self.__session: aiohttp.ClientSession = (
self.session_manager.create_new_session()
)
self.__proxy: str | None = proxy
self.__session_start_time: float
self.__session: aiohttp.ClientSession
self.__create_new_session()

def __create_new_session(self: SpotAsyncClient) -> None:
"""Create a new session."""
self.__session = aiohttp.ClientSession(headers=self.HEADERS, proxy=self.__proxy)
self.__session_start_time = time.time()

async def __check_renew_session(self: SpotAsyncClient) -> None:
"""Check if the session is too old and renew if necessary."""
if time.time() - self.__session_start_time > self.MAX_SESSION_AGE:
await self.__session.close() # Close the old session
self.__create_new_session()

async def request( # type: ignore[override] # pylint: disable=invalid-overridden-method,too-many-arguments # noqa: PLR0913
self: SpotAsyncClient,
Expand Down Expand Up @@ -642,7 +588,7 @@ async def request( # type: ignore[override] # pylint: disable=invalid-overridde
extra_params=extra_params,
)
timeout: int = self.TIMEOUT if timeout != 10 else timeout # type: ignore[no-redef]
self.__session = await self.session_manager.get_session()
await self.__check_renew_session()

if method in {"GET", "DELETE"}:
return await self.__check_response_data( # type: ignore[return-value]
Expand Down Expand Up @@ -731,22 +677,28 @@ class NFTClient(SpotClient):

class FuturesClient:
"""
The base class for all Futures clients handles un-/signed requests
and returns exception handled results.
The base class for all Futures clients handles un-/signed requests and
returns exception handled results.
If you are facing timeout errors on derived clients, you can make use of the
``TIMEOUT`` attribute to deviate from the default ``10`` seconds.
If the sandbox environment is chosen, the keys must be generated from here:
https://demo-futures.kraken.com/settings/api
Kraken sometimes rejects requests that are older than a certain time without
further information. To avoid this, the session manager creates a new
session every 5 minutes.
:param key: Futures API public key (default: ``""``)
:type key: str, optional
:param secret: Futures API secret key (default: ``""``)
:type secret: str, optional
:param url: The URL to access the Futures Kraken API (default: https://futures.kraken.com)
:param url: The URL to access the Futures Kraken API (default:
https://futures.kraken.com)
:type url: str, optional
:param sandbox: If set to ``True`` the URL will be https://demo-futures.kraken.com (default: ``False``)
:param sandbox: If set to ``True`` the URL will be
https://demo-futures.kraken.com (default: ``False``)
:type sandbox: bool, optional
:param proxy: proxy URL, may contain authentication information
:type proxy: str, optional
Expand All @@ -755,6 +707,8 @@ class FuturesClient:
URL: str = "https://futures.kraken.com"
SANDBOX_URL: str = "https://demo-futures.kraken.com"
TIMEOUT: int = 10
HEADERS: Final[dict] = {"User-Agent": "btschwertfeger/python-kraken-sdk"}
MAX_SESSION_AGE: int = 300 # seconds

def __init__( # nosec: B107
self: FuturesClient,
Expand All @@ -780,8 +734,30 @@ def __init__( # nosec: B107
self._use_custom_exceptions: bool = use_custom_exceptions

self._err_handler: ErrorHandler = ErrorHandler()
self.session_manager: SessionManager = SessionManager(proxy=proxy)
self.__session: requests.Session = self.session_manager.create_new_session()

self.__proxy: str | None = proxy
self.__session_start_time: float
self.__session: aiohttp.ClientSession
self.__create_new_session()

Check warning

Code scanning / CodeQL

`__init__` method calls overridden method Warning

Call to self.
__create_new_session
in __init__ method, which is overridden by
method FuturesAsyncClient.__create_new_session
.

def __create_new_session(self: FuturesClient) -> None:
"""Create a new session."""
self.__session = requests.Session()
self.__session.headers.update(self.HEADERS)
if self.__proxy is not None:
self.__session.proxies.update(
{
"http": self.__proxy,
"https": self.__proxy,
},
)
self.__session_start_time = time.time()

def __check_renew_session(self: FuturesClient) -> None:
"""Check if the session is too old and renew if necessary."""
if time.time() - self.__session_start_time > self.MAX_SESSION_AGE:
self.__session.close() # Close the old session
self.__create_new_session()

def _prepare_request(
self: FuturesClient,
Expand Down Expand Up @@ -887,7 +863,7 @@ def request( # pylint: disable=too-many-arguments
extra_params=extra_params,
)
timeout: int = self.TIMEOUT if timeout == 10 else timeout # type: ignore[no-redef]
self.__session = self.session_manager.get_session()
self.__check_renew_session()

if method in {"GET", "DELETE"}:
return self.__check_response_data(
Expand Down Expand Up @@ -1050,10 +1026,21 @@ def __init__( # nosec: B107
sandbox=sandbox,
use_custom_exceptions=use_custom_exceptions,
)
self.session_manager: AsyncSessionManager = AsyncSessionManager(proxy=proxy) # type: ignore[assignment]
self.__session: aiohttp.ClientSession = (
self.session_manager.create_new_session()
)
self.__proxy: str | None = proxy
self.__session_start_time: float
self.__session: aiohttp.ClientSession
self.__create_new_session()

def __create_new_session(self: FuturesAsyncClient) -> None:
"""Create a new session."""
self.__session = aiohttp.ClientSession(headers=self.HEADERS, proxy=self.__proxy)
self.__session_start_time = time.time()

async def __check_renew_session(self: FuturesAsyncClient) -> None:
"""Check if the session is too old and renew if necessary."""
if time.time() - self.__session_start_time > self.MAX_SESSION_AGE:
await self.__session.close() # Close the old session
self.__create_new_session()

async def request( # type: ignore[override] # pylint: disable=arguments-differ,invalid-overridden-method
self: FuturesAsyncClient,
Expand All @@ -1075,7 +1062,7 @@ async def request( # type: ignore[override] # pylint: disable=arguments-differ,
)

timeout = self.TIMEOUT if timeout != 10 else timeout
self.__session = await self.session_manager.get_session()
await self.__check_renew_session()

if method in {"GET", "DELETE"}:
return await self.__check_response_data(
Expand Down

0 comments on commit 9dc6518

Please sign in to comment.