Skip to content
Merged
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
2 changes: 2 additions & 0 deletions sdk-compliance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1432,12 +1432,14 @@ supporting_symbols:
- AsyncClient.storage
- AsyncClient.functions
- AsyncClient.realtime
- AsyncClient.aclose
- Client
- Client.create
- Client.auth
- Client.postgrest
- Client.storage
- Client.functions
- Client.close
- AuthClientOptions
- StorageClientOptions
- SupabaseException
Expand Down
12 changes: 12 additions & 0 deletions src/auth/src/supabase_auth/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@ async def __aexit__(
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
self.stop_auto_refresh()

def stop_auto_refresh(self) -> None:
"""Cancel the scheduled refresh and the refresh in flight, and
schedule no new one."""
self.auto_refresh_token = False
self._cancel_refresh_timer()
task, self._refresh_task = self._refresh_task, None
if task is not None:
Expand Down Expand Up @@ -486,6 +492,12 @@ def __exit__(
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
self.stop_auto_refresh()

def stop_auto_refresh(self) -> None:
"""Cancel the scheduled refresh and schedule no new one, even from a
refresh that is running."""
self.auto_refresh_token = False
self._cancel_refresh_timer()

def __enter__(self) -> Self:
Expand Down
4 changes: 2 additions & 2 deletions src/supabase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ The response errors inherit both from the root of their package and from `Supaba

## Closing the client

The client keeps its HTTP connections open. Use it as a context manager to close them when you are done:
The client keeps its HTTP connections open and refreshes the session in the background. Use it as a context manager, or call `close()`, to stop the refresh and close the connections when you are done:

```python
import os
Expand All @@ -317,4 +317,4 @@ with create_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_KEY"]) as su
data = supabase.table("countries").select("*").execute()
```

`AsyncClient` works the same way with `async with`.
`AsyncClient` works the same way with `async with` or `await supabase.aclose()`. Closing the client closes the HTTP client that `create_client` created, but not an `http_client` you passed in, which stays yours to close. Realtime connections close when their own `async with` block ends.
10 changes: 6 additions & 4 deletions src/supabase/src/supabase/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ def create_async_client(
) -> AsyncClient:
"""Create an async client that sends its requests with aiohttp.

`http_client` defaults to a new aiohttp client. See `AsyncClient` for the
`http_client` defaults to a new aiohttp client, which is closed with the
client. An `http_client` passed in is left open. See `AsyncClient` for the
other arguments.
"""
validate_request_timeout(request_timeout)
client = http_client or ClientSession()
return AsyncClient(
client = AsyncClient(
supabase_url,
supabase_key,
AsyncAiohttpSession(client=client),
AsyncAiohttpSession(client=http_client or ClientSession()),
headers=headers,
request_timeout=request_timeout,
retry=retry,
Expand All @@ -50,3 +50,5 @@ def create_async_client(
functions=functions,
realtime=realtime,
)
client._owns_http_session = http_client is None
return client
36 changes: 30 additions & 6 deletions src/supabase/src/supabase/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,10 +448,12 @@ def __init__(
) -> None:
"""Instantiate the client.

`http_session` sends the requests and `realtime` configures the
realtime connections. See `SupabaseClient` for the other arguments.
`http_session` sends the requests and stays open when the client is
closed. `realtime` configures the realtime connections. See
`SupabaseClient` for the other arguments.
"""
self.http_session = http_session
self._owns_http_session = False
self._realtime_options = realtime or RealtimeClientOptions()
super().__init__(
supabase_url,
Expand Down Expand Up @@ -511,6 +513,17 @@ async def create(

return client

async def aclose(self) -> None:
"""Stop the auth auto-refresh and close the HTTP session the client created.

A session passed in by the caller is left open, as are the realtime
connections opened from `realtime`, which close when their
`async with` block ends. Closing the client more than once is safe.
"""
self.auth.session_manager.stop_auto_refresh()
if self._owns_http_session:
await self.http_session.__aexit__(None, None, None)

async def __aenter__(self) -> Self:
return self

Expand All @@ -520,7 +533,7 @@ async def __aexit__(
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
await self.http_session.__aexit__(exc_type, exc, tb)
await self.aclose()

@cached_property
@override
Expand Down Expand Up @@ -611,10 +624,11 @@ def __init__(
) -> None:
"""Instantiate the client.

`http_session` sends the requests. See `SupabaseClient` for the other
arguments.
`http_session` sends the requests and stays open when the client is
closed. See `SupabaseClient` for the other arguments.
"""
self.http_session = http_session
self._owns_http_session = False
super().__init__(
supabase_url,
supabase_key,
Expand Down Expand Up @@ -668,6 +682,16 @@ def create(

return client

def close(self) -> None:
"""Stop the auth auto-refresh and close the HTTP session the client created.

A session passed in by the caller is left open. Closing the client
more than once is safe.
"""
self.auth.session_manager.stop_auto_refresh()
if self._owns_http_session:
self.http_session.__exit__(None, None, None)

def __enter__(self) -> Self:
return self

Expand All @@ -677,7 +701,7 @@ def __exit__(
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
self.http_session.__exit__(exc_type, exc, tb)
self.close()

@cached_property
@override
Expand Down
24 changes: 15 additions & 9 deletions src/supabase/src/supabase/httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,20 @@ def create_client(
) -> Client:
"""Create a sync client that sends its requests with httpx.

`http_client` defaults to a new httpx client. See `Client` for the other
arguments.
`http_client` defaults to a new httpx client, which is closed with the
client. An `http_client` passed in is left open. See `Client` for the
other arguments.
"""
validate_request_timeout(request_timeout)
client = http_client or SyncHttpxClient(
httpx_client = http_client or SyncHttpxClient(
http2=True,
verify=True,
follow_redirects=True,
)
return Client(
client = Client(
supabase_url,
supabase_key,
HttpxSession(client=client),
HttpxSession(client=httpx_client),
headers=headers,
request_timeout=request_timeout,
retry=retry,
Expand All @@ -53,6 +54,8 @@ def create_client(
storage=storage,
functions=functions,
)
client._owns_http_session = http_client is None
return client


def create_async_client(
Expand All @@ -71,19 +74,20 @@ def create_async_client(
) -> AsyncClient:
"""Create an async client that sends its requests with httpx.

`http_client` defaults to a new httpx client. See `AsyncClient` for the
`http_client` defaults to a new httpx client, which is closed with the
client. An `http_client` passed in is left open. See `AsyncClient` for the
other arguments.
"""
validate_request_timeout(request_timeout)
client = http_client or AsyncHttpxClient(
httpx_client = http_client or AsyncHttpxClient(
http2=True,
verify=True,
follow_redirects=True,
)
return AsyncClient(
client = AsyncClient(
supabase_url,
supabase_key,
AsyncHttpxSession(client=client),
AsyncHttpxSession(client=httpx_client),
headers=headers,
request_timeout=request_timeout,
retry=retry,
Expand All @@ -93,3 +97,5 @@ def create_async_client(
functions=functions,
realtime=realtime,
)
client._owns_http_session = http_client is None
return client
Loading
Loading