diff --git a/html/supertokens_python/constants.html b/html/supertokens_python/constants.html index 954d8ea4b..ddabaf437 100644 --- a/html/supertokens_python/constants.html +++ b/html/supertokens_python/constants.html @@ -42,7 +42,7 @@

Module supertokens_python.constants

from __future__ import annotations SUPPORTED_CDI_VERSIONS = ["3.0"] -VERSION = "0.15.2" +VERSION = "0.15.3" TELEMETRY = "/telemetry" USER_COUNT = "/users/count" USER_DELETE = "/user/remove" @@ -56,7 +56,8 @@

Module supertokens_python.constants

API_VERSION = "/apiversion" API_VERSION_HEADER = "cdi-version" DASHBOARD_VERSION = "0.7" -HUNDRED_YEARS_IN_MS = 3153600000000 +HUNDRED_YEARS_IN_MS = 3153600000000 +RATE_LIMIT_STATUS_CODE = 429
diff --git a/html/supertokens_python/normalised_url_path.html b/html/supertokens_python/normalised_url_path.html index f5287fcac..d36078bb8 100644 --- a/html/supertokens_python/normalised_url_path.html +++ b/html/supertokens_python/normalised_url_path.html @@ -68,7 +68,9 @@

Module supertokens_python.normalised_url_path

Classes def is_a_recipe_path(self) -> bool: parts = self.__value.split("/") - return parts[1] == "recipe" or parts[2] == "recipe" + return (len(parts) > 1 and parts[1] == "recipe") or ( + len(parts) > 2 and parts[2] == "recipe" + )

Methods

@@ -301,7 +305,9 @@

Methods

def is_a_recipe_path(self) -> bool:
     parts = self.__value.split("/")
-    return parts[1] == "recipe" or parts[2] == "recipe"
+ return (len(parts) > 1 and parts[1] == "recipe") or ( + len(parts) > 2 and parts[2] == "recipe" + )
diff --git a/html/supertokens_python/querier.html b/html/supertokens_python/querier.html index 8636020dc..f95a55798 100644 --- a/html/supertokens_python/querier.html +++ b/html/supertokens_python/querier.html @@ -41,9 +41,11 @@

Module supertokens_python.querier

# under the License. from __future__ import annotations +import asyncio + from json import JSONDecodeError from os import environ -from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Optional from httpx import AsyncClient, ConnectTimeout, NetworkError, Response @@ -53,6 +55,7 @@

Module supertokens_python.querier

API_VERSION_HEADER, RID_KEY_HEADER, SUPPORTED_CDI_VERSIONS, + RATE_LIMIT_STATUS_CODE, ) from .normalised_url_path import NormalisedURLPath @@ -250,6 +253,7 @@

Module supertokens_python.querier

method: str, http_function: Callable[[str], Awaitable[Response]], no_of_tries: int, + retry_info_map: Optional[Dict[str, int]] = None, ) -> Any: if no_of_tries == 0: raise_general_exception("No SuperTokens core available to query") @@ -266,6 +270,14 @@

Module supertokens_python.querier

Querier.__last_tried_index %= len(self.__hosts) url = current_host + path.get_as_string_dangerous() + max_retries = 5 + + if retry_info_map is None: + retry_info_map = {} + + if retry_info_map.get(url) is None: + retry_info_map[url] = max_retries + ProcessState.get_instance().add_state( AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER ) @@ -275,6 +287,20 @@

Module supertokens_python.querier

): Querier.__hosts_alive_for_testing.add(current_host) + if response.status_code == RATE_LIMIT_STATUS_CODE: + retries_left = retry_info_map[url] + + if retries_left > 0: + retry_info_map[url] = retries_left - 1 + + attempts_made = max_retries - retries_left + delay = (10 + attempts_made * 250) / 1000 + + await asyncio.sleep(delay) + return await self.__send_request_helper( + path, method, http_function, no_of_tries, retry_info_map + ) + if is_4xx_error(response.status_code) or is_5xx_error(response.status_code): # type: ignore raise_general_exception( "SuperTokens core threw an error for a " @@ -292,9 +318,9 @@

Module supertokens_python.querier

except JSONDecodeError: return response.text - except (ConnectionError, NetworkError, ConnectTimeout): + except (ConnectionError, NetworkError, ConnectTimeout) as _: return await self.__send_request_helper( - path, method, http_function, no_of_tries - 1 + path, method, http_function, no_of_tries - 1, retry_info_map ) except Exception as e: raise_general_exception(e)
@@ -503,6 +529,7 @@

Classes

method: str, http_function: Callable[[str], Awaitable[Response]], no_of_tries: int, + retry_info_map: Optional[Dict[str, int]] = None, ) -> Any: if no_of_tries == 0: raise_general_exception("No SuperTokens core available to query") @@ -519,6 +546,14 @@

Classes

Querier.__last_tried_index %= len(self.__hosts) url = current_host + path.get_as_string_dangerous() + max_retries = 5 + + if retry_info_map is None: + retry_info_map = {} + + if retry_info_map.get(url) is None: + retry_info_map[url] = max_retries + ProcessState.get_instance().add_state( AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER ) @@ -528,6 +563,20 @@

Classes

): Querier.__hosts_alive_for_testing.add(current_host) + if response.status_code == RATE_LIMIT_STATUS_CODE: + retries_left = retry_info_map[url] + + if retries_left > 0: + retry_info_map[url] = retries_left - 1 + + attempts_made = max_retries - retries_left + delay = (10 + attempts_made * 250) / 1000 + + await asyncio.sleep(delay) + return await self.__send_request_helper( + path, method, http_function, no_of_tries, retry_info_map + ) + if is_4xx_error(response.status_code) or is_5xx_error(response.status_code): # type: ignore raise_general_exception( "SuperTokens core threw an error for a " @@ -545,9 +594,9 @@

Classes

except JSONDecodeError: return response.text - except (ConnectionError, NetworkError, ConnectTimeout): + except (ConnectionError, NetworkError, ConnectTimeout) as _: return await self.__send_request_helper( - path, method, http_function, no_of_tries - 1 + path, method, http_function, no_of_tries - 1, retry_info_map ) except Exception as e: raise_general_exception(e)