diff --git a/supertokens_python/__init__.py b/supertokens_python/__init__.py index 43d3573b2..0048da2a3 100644 --- a/supertokens_python/__init__.py +++ b/supertokens_python/__init__.py @@ -16,6 +16,7 @@ from typing_extensions import Literal +from supertokens_python.async_to_sync.handler import ConcreteAsyncHandler from supertokens_python.framework.request import BaseRequest from supertokens_python.types import RecipeUserId @@ -36,9 +37,17 @@ def init( mode: Optional[Literal["asgi", "wsgi"]] = None, telemetry: Optional[bool] = None, debug: Optional[bool] = None, + async_handler: Optional[ConcreteAsyncHandler] = None, ): return Supertokens.init( - app_info, framework, supertokens_config, recipe_list, mode, telemetry, debug + app_info=app_info, + framework=framework, + supertokens_config=supertokens_config, + recipe_list=recipe_list, + mode=mode, + telemetry=telemetry, + debug=debug, + async_handler=async_handler, ) diff --git a/supertokens_python/async_to_sync/__init__.py b/supertokens_python/async_to_sync/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/supertokens_python/async_to_sync/base.py b/supertokens_python/async_to_sync/base.py new file mode 100644 index 000000000..0e8c9d11d --- /dev/null +++ b/supertokens_python/async_to_sync/base.py @@ -0,0 +1,30 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from typing import Any, Coroutine, TypeVar + +_T = TypeVar("_T") + + +def sync(co: Coroutine[Any, Any, _T]) -> _T: + """ + Convert async function calls to sync calls using the specified `_AsyncHandler` + """ + # Disabling cyclic import since the import is lazy, and will not cause issues + from supertokens_python import supertokens # pylint: disable=cyclic-import + + st = supertokens.Supertokens.get_instance() + handler = st.async_handler + + return handler.run_as_sync(co) diff --git a/supertokens_python/async_to_sync/handler.py b/supertokens_python/async_to_sync/handler.py new file mode 100644 index 000000000..9bbed0124 --- /dev/null +++ b/supertokens_python/async_to_sync/handler.py @@ -0,0 +1,254 @@ +# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. +# +# This software is licensed under the Apache License, Version 2.0 (the +# "License") as published by the Apache Software Foundation. +# +# You may not use this file except in compliance with the License. You may +# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from abc import ABC, abstractmethod +import asyncio +from enum import Enum +from threading import Thread +from typing import Any, Coroutine, Optional, TypeVar, Union +from supertokens_python.async_to_sync.utils import create_or_get_event_loop + +_T = TypeVar("_T") + + +class AsyncType(Enum): + asyncio = "asyncio" + gevent = "gevent" + eventlet = "eventlet" + + +class _AsyncHandler(ABC): + """ + Abstract class to handle async-to-sync in various environments. + """ + + async_type: AsyncType + """The type of async handling to use""" + + create_loop_thread: bool + """Whether a thread needs to be created to run an event loop""" + + loop: Optional[asyncio.AbstractEventLoop] + """The event loop to use for async-to-sync conversions""" + + is_loop_threaded: bool + """Whether the passed loop is running in a thread""" + + def __init__( + self, + create_loop_thread: bool, + loop: Optional[asyncio.AbstractEventLoop], + is_loop_threaded: bool, + ): + # TODO: Add checks on the socket to see if it's patched by Gevent/Eventlet + + # Either the user passes in a loop or tells us to create a thread, not both + # If neither is passed, we use the default event loop handling + if loop is not None and create_loop_thread: + raise ValueError("Pass either `loop` or `create_loop_thread`, not both") + + if is_loop_threaded: + if loop is None and not create_loop_thread: + raise ValueError( + "Loop cannot be marked as threaded without passing in `loop` or `create_loop_thread`" + ) + + if create_loop_thread: + is_loop_threaded = True + + self.loop = loop + self.create_loop_thread = create_loop_thread + self.is_loop_threaded = is_loop_threaded + self._create_loop_thread() + self._register_loop() + + def _create_loop_thread(self): + if self.create_loop_thread: + self.loop = asyncio.new_event_loop() + loop_thread = Thread(target=self.loop.run_forever, daemon=True) + loop_thread.start() + + def _register_loop(self): + import nest_asyncio # type: ignore + + if self.loop is None: + nest_asyncio.apply() # type: ignore + else: + # Need to set the event loop before `nest_asyncio.apply` + asyncio.set_event_loop(loop=self.loop) + nest_asyncio.apply(loop=self.loop) # type: ignore + + @abstractmethod + def run_as_sync(self, coroutine: Coroutine[Any, Any, _T]) -> _T: + pass + + def _default_run_as_sync( + self, + coroutine: Coroutine[Any, Any, _T], + loop: Optional[asyncio.AbstractEventLoop], + ) -> _T: + # Event loop running in separate thread + if self.is_loop_threaded: + if self.loop is None: + raise ValueError( + "Expected `loop` to not be `None` when `is_loop_threaded` is True" + ) + + future = asyncio.run_coroutine_threadsafe(coroutine, self.loop) + return future.result() + + # Normal event loop in the current thread + if loop is None: + loop = create_or_get_event_loop() + + return loop.run_until_complete(coroutine) + + +class DefaultHandler(_AsyncHandler): + """ + Default async handler for Asyncio-based apps. + """ + async_type = AsyncType.asyncio + + def __init__(self): + super().__init__(create_loop_thread=False, loop=None, is_loop_threaded=False) + + def run_as_sync(self, coroutine: Coroutine[Any, Any, _T]) -> _T: + return super()._default_run_as_sync(coroutine, self.loop) + + +class AsyncioHandler(_AsyncHandler): + """ + Async handler specific to Asyncio-based apps. + + Only meant for cases where existing event loops need to be re-used, or new + threaded-loops need to be created. + For normal use-cases, prefer the `DefaultHandler`. + """ + async_type = AsyncType.asyncio + + def __init__( + self, + create_loop_thread: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + is_loop_threaded: bool = False, + ): + # NOTE: Creating a non-threaded loop and storing it causes asyncio context issues. + # Handles missing loops similar to `DefaultHandler` + if loop is not None and not is_loop_threaded: + raise ValueError( + "For existing, non-threaded loops in asyncio, prefer using DefaultHandler" + ) + + super().__init__( + create_loop_thread=create_loop_thread, + loop=loop, + is_loop_threaded=is_loop_threaded, + ) + + def run_as_sync(self, coroutine: Coroutine[Any, Any, _T]) -> _T: + return super()._default_run_as_sync(coroutine, self.loop) + + +class GeventHandler(_AsyncHandler): + """ + Async handler specific to Gevent-based apps. + + Does not work optimally with event loops on the same thread, will drop requests. + Requires a separate thread for the event loop to work well. + """ + async_type = AsyncType.gevent + + def __init__( + self, + create_loop_thread: bool = True, + loop: Optional[asyncio.AbstractEventLoop] = None, + is_loop_threaded: bool = True, + ): + if not create_loop_thread: + if not is_loop_threaded: + raise ValueError( + "Non-Threaded gevent loops result in stuck requests, use a threaded loop instead" + ) + + super().__init__( + create_loop_thread=create_loop_thread, + loop=loop, + is_loop_threaded=is_loop_threaded, + ) + + def run_as_sync(self, coroutine: Coroutine[Any, Any, _T]) -> _T: + # When a loop isn't declared or is not in a thread, handle as usual + if self.loop is None or not self.is_loop_threaded: + return super()._default_run_as_sync(coroutine, self.loop) + + # When loop is in a thread, we can optimize using Events + from gevent.event import Event # type: ignore + + future = asyncio.run_coroutine_threadsafe(coroutine, self.loop) + event = Event() # type: ignore + future.add_done_callback(lambda _: event.set()) # type: ignore + event.wait() # type: ignore + return future.result() + + +class EventletHandler(_AsyncHandler): + """ + Async handler specific to Eventlet-based apps. + + Does not work with event loops on the same thread. + Requires a separate thread for the event loop. + """ + async_type = AsyncType.eventlet + + def __init__( + self, + create_loop_thread: bool = True, + loop: Optional[asyncio.AbstractEventLoop] = None, + is_loop_threaded: bool = True, + ): + if not create_loop_thread: + if loop is None or not is_loop_threaded: + raise ValueError( + "Cannot use eventlet with Supertokens without a dedicated event loop thread. " + "Please set `create_loop_thread=True` or pass in a threaded event loop." + ) + + super().__init__( + create_loop_thread=create_loop_thread, + loop=loop, + is_loop_threaded=is_loop_threaded, + ) + + def run_as_sync(self, coroutine: Coroutine[Any, Any, _T]) -> _T: + # Eventlet only works well when the event loop is in a different thread + if self.loop is None or not self.is_loop_threaded: + raise ValueError( + "Cannot use eventlet with Supertokens without a dedicated event loop thread. " + "Please set `create_loop_thread=True` or pass in a threaded event loop." + ) + + # Use Events to handle loop callbacks + from eventlet.event import Event # type: ignore + + future = asyncio.run_coroutine_threadsafe(coroutine, loop=self.loop) + event = Event() # type: ignore + future.add_done_callback(lambda _: event.send()) # type: ignore + event.wait() # type: ignore + return future.result() + + +ConcreteAsyncHandler = Union[ + DefaultHandler, AsyncioHandler, GeventHandler, EventletHandler +] diff --git a/supertokens_python/async_to_sync_wrapper.py b/supertokens_python/async_to_sync/utils.py similarity index 87% rename from supertokens_python/async_to_sync_wrapper.py rename to supertokens_python/async_to_sync/utils.py index 8e019336d..31d009d4d 100644 --- a/supertokens_python/async_to_sync_wrapper.py +++ b/supertokens_python/async_to_sync/utils.py @@ -13,11 +13,8 @@ # under the License. import asyncio -from typing import Any, Coroutine, TypeVar from os import getenv -_T = TypeVar("_T") - def nest_asyncio_enabled(): return getenv("SUPERTOKENS_NEST_ASYNCIO", "") == "1" @@ -38,8 +35,3 @@ def create_or_get_event_loop() -> asyncio.AbstractEventLoop: asyncio.set_event_loop(loop) return loop raise ex - - -def sync(co: Coroutine[Any, Any, _T]) -> _T: - loop = create_or_get_event_loop() - return loop.run_until_complete(co) diff --git a/supertokens_python/framework/flask/flask_middleware.py b/supertokens_python/framework/flask/flask_middleware.py index d4eff2eaf..957b47cf1 100644 --- a/supertokens_python/framework/flask/flask_middleware.py +++ b/supertokens_python/framework/flask/flask_middleware.py @@ -16,7 +16,7 @@ import json from typing import TYPE_CHECKING, Union -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.framework import BaseResponse if TYPE_CHECKING: @@ -54,7 +54,7 @@ def _(): user_context = default_user_context(request_) result: Union[BaseResponse, None] = sync( - st.middleware(request_, response_, user_context) + st.middleware(request_, response_, user_context), ) if result is not None: @@ -109,8 +109,9 @@ def _(error: Exception): error, FlaskResponse(response), user_context, - ) + ), ) + if isinstance(result, FlaskResponse): return result.response raise Exception("Should never come here") diff --git a/supertokens_python/querier.py b/supertokens_python/querier.py index 69945493d..764963609 100644 --- a/supertokens_python/querier.py +++ b/supertokens_python/querier.py @@ -38,7 +38,7 @@ from .process_state import PROCESS_STATE, ProcessState from .utils import find_max_version, is_4xx_error, is_5xx_error from sniffio import AsyncLibraryNotFoundError -from supertokens_python.async_to_sync_wrapper import create_or_get_event_loop +from supertokens_python.async_to_sync.utils import create_or_get_event_loop from supertokens_python.utils import get_timestamp_ms diff --git a/supertokens_python/recipe/accountlinking/syncio/__init__.py b/supertokens_python/recipe/accountlinking/syncio/__init__.py index 6de893c1f..16a133932 100644 --- a/supertokens_python/recipe/accountlinking/syncio/__init__.py +++ b/supertokens_python/recipe/accountlinking/syncio/__init__.py @@ -13,7 +13,7 @@ # under the License. from typing import Any, Dict, Optional -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from ..types import AccountInfoWithRecipeId from supertokens_python.types import RecipeUserId diff --git a/supertokens_python/recipe/emailpassword/syncio/__init__.py b/supertokens_python/recipe/emailpassword/syncio/__init__.py index 1681589cb..f42330643 100644 --- a/supertokens_python/recipe/emailpassword/syncio/__init__.py +++ b/supertokens_python/recipe/emailpassword/syncio/__init__.py @@ -14,7 +14,7 @@ from typing import Any, Dict, Union, Optional from typing_extensions import Literal -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.session import SessionContainer from supertokens_python.recipe.emailpassword.interfaces import ( SignUpOkResult, diff --git a/supertokens_python/recipe/emailverification/syncio/__init__.py b/supertokens_python/recipe/emailverification/syncio/__init__.py index 914f86498..3122a4ced 100644 --- a/supertokens_python/recipe/emailverification/syncio/__init__.py +++ b/supertokens_python/recipe/emailverification/syncio/__init__.py @@ -14,7 +14,7 @@ from typing import Any, Dict, Optional, Union -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.emailverification.types import EmailTemplateVars from supertokens_python.types import RecipeUserId diff --git a/supertokens_python/recipe/jwt/syncio/__init__.py b/supertokens_python/recipe/jwt/syncio/__init__.py index 8befd40a8..269cce1b6 100644 --- a/supertokens_python/recipe/jwt/syncio/__init__.py +++ b/supertokens_python/recipe/jwt/syncio/__init__.py @@ -13,7 +13,7 @@ # under the License. from typing import Any, Dict, Union, Optional -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.jwt import asyncio from supertokens_python.recipe.jwt.interfaces import ( CreateJwtOkResult, diff --git a/supertokens_python/recipe/multifactorauth/syncio/__init__.py b/supertokens_python/recipe/multifactorauth/syncio/__init__.py index 8268fd19a..ebf1b06c9 100644 --- a/supertokens_python/recipe/multifactorauth/syncio/__init__.py +++ b/supertokens_python/recipe/multifactorauth/syncio/__init__.py @@ -17,7 +17,7 @@ from typing import Any, Dict, Optional, List from supertokens_python.recipe.session import SessionContainer -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync def assert_allowed_to_setup_factor_else_throw_invalid_claim_error( diff --git a/supertokens_python/recipe/multitenancy/syncio/__init__.py b/supertokens_python/recipe/multitenancy/syncio/__init__.py index 5448f2612..25d80e96e 100644 --- a/supertokens_python/recipe/multitenancy/syncio/__init__.py +++ b/supertokens_python/recipe/multitenancy/syncio/__init__.py @@ -14,7 +14,7 @@ from __future__ import annotations from typing import Any, Dict, Optional, TYPE_CHECKING -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.multitenancy.interfaces import TenantConfigCreateOrUpdate from supertokens_python.types import RecipeUserId diff --git a/supertokens_python/recipe/openid/syncio/__init__.py b/supertokens_python/recipe/openid/syncio/__init__.py index a74b0c544..f371f114b 100644 --- a/supertokens_python/recipe/openid/syncio/__init__.py +++ b/supertokens_python/recipe/openid/syncio/__init__.py @@ -13,7 +13,7 @@ # under the License. from typing import Any, Dict, Union, Optional -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.openid import asyncio from supertokens_python.recipe.openid.interfaces import ( GetOpenIdDiscoveryConfigurationResult, diff --git a/supertokens_python/recipe/passwordless/syncio/__init__.py b/supertokens_python/recipe/passwordless/syncio/__init__.py index e5a0105a4..3a298fa86 100644 --- a/supertokens_python/recipe/passwordless/syncio/__init__.py +++ b/supertokens_python/recipe/passwordless/syncio/__init__.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. from typing import Any, Dict, List, Optional, Union -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.auth_utils import LinkingToSessionUserFailedError from supertokens_python.recipe.passwordless import asyncio from supertokens_python.recipe.session import SessionContainer diff --git a/supertokens_python/recipe/session/framework/django/syncio/__init__.py b/supertokens_python/recipe/session/framework/django/syncio/__init__.py index 99b9b52ad..ce8fbd4ae 100644 --- a/supertokens_python/recipe/session/framework/django/syncio/__init__.py +++ b/supertokens_python/recipe/session/framework/django/syncio/__init__.py @@ -15,7 +15,7 @@ from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional from supertokens_python import Supertokens -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.exceptions import SuperTokensError from supertokens_python.framework.django.django_request import DjangoRequest from supertokens_python.framework.django.django_response import DjangoResponse diff --git a/supertokens_python/recipe/session/framework/flask/__init__.py b/supertokens_python/recipe/session/framework/flask/__init__.py index 5e5137d15..e88d29efa 100644 --- a/supertokens_python/recipe/session/framework/flask/__init__.py +++ b/supertokens_python/recipe/session/framework/flask/__init__.py @@ -15,7 +15,7 @@ from typing import Any, Callable, Dict, TypeVar, Union, cast, List, Optional from supertokens_python import Supertokens -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.framework.flask.flask_request import FlaskRequest from supertokens_python.framework.flask.flask_response import FlaskResponse from supertokens_python.recipe.session import SessionRecipe, SessionContainer diff --git a/supertokens_python/recipe/session/interfaces.py b/supertokens_python/recipe/session/interfaces.py index 0ffe24518..43c9b0e62 100644 --- a/supertokens_python/recipe/session/interfaces.py +++ b/supertokens_python/recipe/session/interfaces.py @@ -27,7 +27,7 @@ ) from typing_extensions import TypedDict -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.types import ( APIResponse, GeneralErrorResponse, diff --git a/supertokens_python/recipe/session/syncio/__init__.py b/supertokens_python/recipe/session/syncio/__init__.py index 50eec7a11..9ed602146 100644 --- a/supertokens_python/recipe/session/syncio/__init__.py +++ b/supertokens_python/recipe/session/syncio/__init__.py @@ -14,7 +14,7 @@ from __future__ import annotations from typing import Any, Dict, List, Union, Callable, Optional, TypeVar -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.openid.interfaces import ( GetOpenIdDiscoveryConfigurationResult, ) diff --git a/supertokens_python/recipe/thirdparty/syncio/__init__.py b/supertokens_python/recipe/thirdparty/syncio/__init__.py index 4481c8131..ecd2e2a56 100644 --- a/supertokens_python/recipe/thirdparty/syncio/__init__.py +++ b/supertokens_python/recipe/thirdparty/syncio/__init__.py @@ -13,7 +13,7 @@ # under the License. from typing import Any, Dict, Optional, Union -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.auth_utils import LinkingToSessionUserFailedError from supertokens_python.recipe.session import SessionContainer from supertokens_python.recipe.thirdparty.interfaces import ( diff --git a/supertokens_python/recipe/totp/syncio/__init__.py b/supertokens_python/recipe/totp/syncio/__init__.py index 24eb4d2ed..2cc3dce9b 100644 --- a/supertokens_python/recipe/totp/syncio/__init__.py +++ b/supertokens_python/recipe/totp/syncio/__init__.py @@ -16,7 +16,7 @@ from typing import Any, Dict, Union, Optional -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.totp.types import ( CreateDeviceOkResult, diff --git a/supertokens_python/recipe/usermetadata/syncio/__init__.py b/supertokens_python/recipe/usermetadata/syncio/__init__.py index aa2dc9c2b..8544a22eb 100644 --- a/supertokens_python/recipe/usermetadata/syncio/__init__.py +++ b/supertokens_python/recipe/usermetadata/syncio/__init__.py @@ -1,6 +1,6 @@ from typing import Any, Dict, Union -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync def get_user_metadata(user_id: str, user_context: Union[Dict[str, Any], None] = None): diff --git a/supertokens_python/recipe/userroles/syncio/__init__.py b/supertokens_python/recipe/userroles/syncio/__init__.py index 8cb30ad82..6c255d5df 100644 --- a/supertokens_python/recipe/userroles/syncio/__init__.py +++ b/supertokens_python/recipe/userroles/syncio/__init__.py @@ -14,7 +14,7 @@ from typing import Any, Dict, List, Union -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.userroles.interfaces import ( AddRoleToUserOkResult, CreateNewRoleOrAddPermissionsOkResult, diff --git a/supertokens_python/supertokens.py b/supertokens_python/supertokens.py index 6aae8aa9b..2ed88ad7e 100644 --- a/supertokens_python/supertokens.py +++ b/supertokens_python/supertokens.py @@ -19,6 +19,10 @@ from typing_extensions import Literal +from supertokens_python.async_to_sync.handler import ( + ConcreteAsyncHandler, + DefaultHandler, +) from supertokens_python.logger import ( get_maybe_none_as_str, log_debug_message, @@ -211,7 +215,14 @@ def __init__( mode: Optional[Literal["asgi", "wsgi"]], telemetry: Optional[bool], debug: Optional[bool], + async_handler: Optional[ConcreteAsyncHandler], ): + # Handling async setup before anything else is initialized to prevent event loop issues + if async_handler is None: + async_handler = DefaultHandler() + + self.async_handler = async_handler + if not isinstance(app_info, InputAppInfo): # type: ignore raise ValueError("app_info must be an instance of InputAppInfo") @@ -301,6 +312,7 @@ def init( mode: Optional[Literal["asgi", "wsgi"]], telemetry: Optional[bool], debug: Optional[bool], + async_handler: Optional[ConcreteAsyncHandler], ): if Supertokens.__instance is None: Supertokens.__instance = Supertokens( @@ -311,6 +323,7 @@ def init( mode, telemetry, debug, + async_handler=async_handler, ) PostSTInitCallbacks.run_post_init_callbacks() diff --git a/supertokens_python/syncio/__init__.py b/supertokens_python/syncio/__init__.py index e7b50292f..a8815a852 100644 --- a/supertokens_python/syncio/__init__.py +++ b/supertokens_python/syncio/__init__.py @@ -14,7 +14,7 @@ from typing import Dict, List, Optional, Union, Any from supertokens_python import Supertokens -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.interfaces import ( CreateUserIdMappingOkResult, DeleteUserIdMappingOkResult, diff --git a/tests/frontendIntegration/django2x/polls/views.py b/tests/frontendIntegration/django2x/polls/views.py index eda19409a..80e26d5e5 100644 --- a/tests/frontendIntegration/django2x/polls/views.py +++ b/tests/frontendIntegration/django2x/polls/views.py @@ -53,7 +53,7 @@ from supertokens_python.recipe.session.syncio import get_session_information from supertokens_python.normalised_url_path import NormalisedURLPath from supertokens_python.querier import Querier -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync protected_prop_name = { "sub", diff --git a/tests/frontendIntegration/drf_sync/polls/views.py b/tests/frontendIntegration/drf_sync/polls/views.py index cd776104b..92fbce0e7 100644 --- a/tests/frontendIntegration/drf_sync/polls/views.py +++ b/tests/frontendIntegration/drf_sync/polls/views.py @@ -51,7 +51,7 @@ ) from supertokens_python.recipe.session.framework.django.syncio import verify_session from supertokens_python.recipe.session.syncio import merge_into_access_token_payload -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from supertokens_python.constants import VERSION from supertokens_python.types import RecipeUserId diff --git a/tests/frontendIntegration/flask-server/app.py b/tests/frontendIntegration/flask-server/app.py index e829b61fc..5ff030e07 100644 --- a/tests/frontendIntegration/flask-server/app.py +++ b/tests/frontendIntegration/flask-server/app.py @@ -49,7 +49,7 @@ from supertokens_python.recipe.session.syncio import get_session_information from supertokens_python.normalised_url_path import NormalisedURLPath from supertokens_python.querier import Querier -from supertokens_python.async_to_sync_wrapper import sync +from supertokens_python.async_to_sync.base import sync from werkzeug.exceptions import NotFound diff --git a/tests/test-server/accountlinking.py b/tests/test-server/accountlinking.py index 30b73b694..65364b475 100644 --- a/tests/test-server/accountlinking.py +++ b/tests/test-server/accountlinking.py @@ -1,5 +1,6 @@ from flask import Flask, request, jsonify -from supertokens_python import async_to_sync_wrapper, convert_to_recipe_user_id +from supertokens_python import convert_to_recipe_user_id +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.accountlinking.syncio import can_create_primary_user from supertokens_python.recipe.accountlinking.recipe import AccountLinkingRecipe from supertokens_python.recipe.accountlinking.syncio import is_sign_in_allowed @@ -233,7 +234,7 @@ def verify_email_for_recipe_user_if_linked_accounts_are_verified_api(): # type: assert request.json is not None recipe_user_id = convert_to_recipe_user_id(request.json["recipeUserId"]) user = User.from_json(request.json["user"]) - async_to_sync_wrapper.sync( + sync( AccountLinkingRecipe.get_instance().verify_email_for_recipe_user_if_linked_accounts_are_verified( user=user, recipe_user_id=recipe_user_id, diff --git a/tests/test-server/emailverification.py b/tests/test-server/emailverification.py index e47660f18..918726739 100644 --- a/tests/test-server/emailverification.py +++ b/tests/test-server/emailverification.py @@ -1,6 +1,6 @@ from flask import Flask, request, jsonify -from supertokens_python import async_to_sync_wrapper +from supertokens_python.async_to_sync.base import sync from supertokens_python.framework.flask.flask_request import FlaskRequest from supertokens_python.recipe.emailverification.interfaces import ( CreateEmailVerificationTokenOkResult, @@ -116,7 +116,7 @@ def update_session_if_required_post_email_verification(): # type: ignore ) session = convert_session_to_container(data) if "session" in data else None - session_resp = async_to_sync_wrapper.sync( + session_resp = sync( EmailVerificationRecipe.get_instance_or_throw().update_session_if_required_post_email_verification( recipe_user_id_whose_email_got_verified=recipe_user_id_whose_email_got_verified, session=session, diff --git a/tests/test-server/multifactorauth.py b/tests/test-server/multifactorauth.py index d82048917..99c3c68b4 100644 --- a/tests/test-server/multifactorauth.py +++ b/tests/test-server/multifactorauth.py @@ -1,7 +1,7 @@ from typing import List from flask import Flask, request, jsonify -from supertokens_python import async_to_sync_wrapper +from supertokens_python.async_to_sync.base import sync from supertokens_python.recipe.multifactorauth.types import MFAClaimValue from supertokens_python.types import User @@ -15,7 +15,7 @@ def fetch_value_api(): # type: ignore ) assert request.json is not None - response: MFAClaimValue = async_to_sync_wrapper.sync( # type: ignore + response: MFAClaimValue = sync( # type: ignore MultiFactorAuthClaim.fetch_value( # type: ignore request.json["_userId"], convert_to_recipe_user_id(request.json["recipeUserId"]), @@ -184,7 +184,7 @@ async def required_secondary_factors_for_tenant() -> List[str]: assert request.json is not None return request.json["requiredSecondaryFactorsForTenant"] - response = async_to_sync_wrapper.sync( + response = sync( MultiFactorAuthRecipe.get_instance_or_throw_error().recipe_implementation.get_mfa_requirements_for_auth( tenant_id=request.json["tenantId"], access_token_payload=request.json["accessTokenPayload"],