Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nest-asyncio for latest version - v0.16 #448

Closed
wants to merge 15 commits into from
Closed
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.16.1] - 2023-09-19

- Uses `nest_asyncio` patch in event loop - sync to async
- Retry Querier request on `AsyncLibraryNotFoundError`

## [0.16.0] - 2023-09-13

Expand Down Expand Up @@ -535,6 +539,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
}
```

## [0.14.10] - 2023-09-31

- Uses `nest_asyncio` patch in event loop - sync to async

## [0.14.9] - 2023-09-28

- Add logic to retry network calls if the core returns status 429

## [0.14.8] - 2023-07-07
## Fixes

Expand Down
6 changes: 0 additions & 6 deletions addDevTag
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#!/bin/bash

# check if we need to merge master into this branch------------
if [[ $(git log origin/master ^HEAD) ]]; then
echo "You need to merge master into this branch. Exiting"
exit 1
fi

# get version------------
version=`cat setup.py | grep -e 'version='`
while IFS='"' read -ra ADDR; do
Expand Down
2 changes: 1 addition & 1 deletion html/supertokens_python/constants.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ <h2>Index</h2>
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.10.0</a>.</p>
</footer>
</body>
</html>
</html>
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@

setup(
name="supertokens_python",
version="0.16.0",
version="0.16.1",
author="SuperTokens",
license="Apache 2.0",
author_email="[email protected]",
Expand Down Expand Up @@ -112,6 +112,7 @@
"twilio==7.9.1",
"aiosmtplib==1.1.6",
"pkce==1.0.3",
"nest-asyncio==1.5.1",
],
python_requires=">=3.7",
include_package_data=True,
Expand Down
13 changes: 8 additions & 5 deletions supertokens_python/async_to_sync_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@
# License for the specific language governing permissions and limitations
# under the License.

import nest_asyncio # type: ignore
import asyncio
from typing import Any, Coroutine, TypeVar

_T = TypeVar("_T")


def check_event_loop():
def create_or_get_event_loop() -> asyncio.AbstractEventLoop:
try:
asyncio.get_event_loop()
except RuntimeError as ex:
return asyncio.get_event_loop()
except Exception as ex:
if "There is no current event loop in thread" in str(ex):
loop = asyncio.new_event_loop()
nest_asyncio.apply(loop) # type: ignore
asyncio.set_event_loop(loop)
return loop
raise ex


def sync(co: Coroutine[Any, Any, _T]) -> _T:
check_event_loop()
loop = asyncio.get_event_loop()
loop = create_or_get_event_loop()
return loop.run_until_complete(co)
2 changes: 1 addition & 1 deletion supertokens_python/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

SUPPORTED_CDI_VERSIONS = ["3.0"]
VERSION = "0.16.0"
VERSION = "0.16.1"
TELEMETRY = "/telemetry"
USER_COUNT = "/users/count"
USER_DELETE = "/user/remove"
Expand Down
87 changes: 61 additions & 26 deletions supertokens_python/querier.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
from .exceptions import raise_general_exception
from .process_state import AllowedProcessStates, ProcessState
from .utils import find_max_version, is_4xx_error, is_5xx_error
from supertokens_python.async_to_sync_wrapper import create_or_get_event_loop
from sniffio import AsyncLibraryNotFoundError


class Querier:
Expand Down Expand Up @@ -71,6 +73,35 @@ def get_hosts_alive_for_testing():
raise_general_exception("calling testing function in non testing env")
return Querier.__hosts_alive_for_testing

async def api_request(
self,
url: str,
method: str,
attempts_remaining: int,
*args: Any,
**kwargs: Any,
) -> Response:
if attempts_remaining == 0:
raise_general_exception("Retry request failed")

try:
async with AsyncClient() as client:
if method == "GET":
return await client.get(url, *args, **kwargs) # type: ignore
if method == "POST":
return await client.post(url, *args, **kwargs) # type: ignore
if method == "PUT":
return await client.put(url, *args, **kwargs) # type: ignore
if method == "DELETE":
return await client.delete(url, *args, **kwargs) # type: ignore
raise Exception("Shouldn't come here")
except AsyncLibraryNotFoundError:
# Retry
loop = create_or_get_event_loop()
return loop.run_until_complete(
self.api_request(url, method, attempts_remaining - 1, *args, **kwargs)
)

async def get_api_version(self):
if Querier.api_version is not None:
return Querier.api_version
Expand All @@ -79,12 +110,11 @@ async def get_api_version(self):
AllowedProcessStates.CALLING_SERVICE_IN_GET_API_VERSION
)

async def f(url: str) -> Response:
async def f(url: str, method: str) -> Response:
headers = {}
if Querier.__api_key is not None:
headers = {API_KEY_HEADER: Querier.__api_key}
async with AsyncClient() as client:
return await client.get(url, headers=headers) # type:ignore
return await self.api_request(url, method, 2, headers=headers)

response = await self.__send_request_helper(
NormalisedURLPath(API_VERSION), "GET", f, len(self.__hosts)
Expand Down Expand Up @@ -134,13 +164,14 @@ async def send_get_request(
if params is None:
params = {}

async def f(url: str) -> Response:
async with AsyncClient() as client:
return await client.get( # type:ignore
url,
params=params,
headers=await self.__get_headers_with_api_version(path),
)
async def f(url: str, method: str) -> Response:
return await self.api_request(
url,
method,
2,
headers=await self.__get_headers_with_api_version(path),
params=params,
)

return await self.__send_request_helper(path, "GET", f, len(self.__hosts))

Expand All @@ -163,9 +194,14 @@ async def send_post_request(
headers = await self.__get_headers_with_api_version(path)
headers["content-type"] = "application/json; charset=utf-8"

async def f(url: str) -> Response:
async with AsyncClient() as client:
return await client.post(url, json=data, headers=headers) # type: ignore
async def f(url: str, method: str) -> Response:
return await self.api_request(
url,
method,
2,
headers=await self.__get_headers_with_api_version(path),
json=data,
)

return await self.__send_request_helper(path, "POST", f, len(self.__hosts))

Expand All @@ -175,13 +211,14 @@ async def send_delete_request(
if params is None:
params = {}

async def f(url: str) -> Response:
async with AsyncClient() as client:
return await client.delete( # type:ignore
url,
params=params,
headers=await self.__get_headers_with_api_version(path),
)
async def f(url: str, method: str) -> Response:
return await self.api_request(
url,
method,
2,
headers=await self.__get_headers_with_api_version(path),
params=params,
)

return await self.__send_request_helper(path, "DELETE", f, len(self.__hosts))

Expand All @@ -194,9 +231,8 @@ async def send_put_request(
headers = await self.__get_headers_with_api_version(path)
headers["content-type"] = "application/json; charset=utf-8"

async def f(url: str) -> Response:
async with AsyncClient() as client:
return await client.put(url, json=data, headers=headers) # type: ignore
async def f(url: str, method: str) -> Response:
return await self.api_request(url, method, 2, headers=headers, json=data)

return await self.__send_request_helper(path, "PUT", f, len(self.__hosts))

Expand All @@ -223,7 +259,7 @@ async def __send_request_helper(
self,
path: NormalisedURLPath,
method: str,
http_function: Callable[[str], Awaitable[Response]],
http_function: Callable[[str, str], Awaitable[Response]],
no_of_tries: int,
retry_info_map: Optional[Dict[str, int]] = None,
) -> Any:
Expand Down Expand Up @@ -253,7 +289,7 @@ async def __send_request_helper(
ProcessState.get_instance().add_state(
AllowedProcessStates.CALLING_SERVICE_IN_REQUEST_HELPER
)
response = await http_function(url)
response = await http_function(url, method)
if ("SUPERTOKENS_ENV" in environ) and (
environ["SUPERTOKENS_ENV"] == "testing"
):
Expand Down Expand Up @@ -289,7 +325,6 @@ async def __send_request_helper(
return response.json()
except JSONDecodeError:
return response.text

except (ConnectionError, NetworkError, ConnectTimeout) as _:
return await self.__send_request_helper(
path, method, http_function, no_of_tries - 1, retry_info_map
Expand Down
5 changes: 2 additions & 3 deletions supertokens_python/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from httpx import HTTPStatusError, Response
from tldextract import extract # type: ignore

from supertokens_python.async_to_sync_wrapper import check_event_loop
from supertokens_python.async_to_sync_wrapper import create_or_get_event_loop
from supertokens_python.framework.django.framework import DjangoFramework
from supertokens_python.framework.fastapi.framework import FastapiFramework
from supertokens_python.framework.flask.framework import FlaskFramework
Expand Down Expand Up @@ -212,8 +212,7 @@ def execute_async(mode: str, func: Callable[[], Coroutine[Any, Any, None]]):
if real_mode == "wsgi":
asyncio.run(func())
else:
check_event_loop()
loop = asyncio.get_event_loop()
loop = create_or_get_event_loop()
loop.create_task(func())


Expand Down
Loading