Skip to content
Open
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
1 change: 1 addition & 0 deletions src/functions/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ requires-python = ">=3.9"
dependencies = [
"httpx[http2] >=0.26,<0.29",
"strenum >=0.4.15",
"yarl>=1.20.1",
]


Expand Down
37 changes: 16 additions & 21 deletions src/functions/src/supabase_functions/_async/functions_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from warnings import warn

from httpx import AsyncClient, HTTPError, Response, QueryParams
from yarl import URL

from ..errors import FunctionsHttpError, FunctionsRelayError
from ..utils import (
Expand All @@ -24,7 +25,7 @@ def __init__(
):
if not is_http_url(url):
raise ValueError("url must be a valid HTTP URL string")
self.url = url
self.url = URL(url)
self.headers = {
"User-Agent": f"supabase-py/functions-py v{__version__}",
**headers,
Expand All @@ -51,37 +52,32 @@ def __init__(

self.verify = bool(verify) if verify is not None else True
self.timeout = int(abs(timeout)) if timeout is not None else 60

if http_client is not None:
http_client.base_url = self.url
http_client.headers.update({**self.headers})
self._client = http_client
else:
self._client = AsyncClient(
base_url=self.url,
headers=self.headers,
verify=self.verify,
timeout=self.timeout,
proxy=proxy,
follow_redirects=True,
http2=True,
)
self._client = http_client or AsyncClient(
verify=self.verify,
timeout=self.timeout,
proxy=proxy,
follow_redirects=True,
http2=True,
)

async def _request(
self,
method: Literal["GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
url: str,
path: list[str],
headers: Optional[Dict[str, str]] = None,
json: Optional[Dict[Any, Any]] = None,
params: Optional[QueryParams] = None,
) -> Response:
url = self.url.joinpath(*path)
headers = headers or dict()
headers.update(self.headers)
response = (
await self._client.request(
method, url, data=json, headers=headers, params=params
method, str(url), data=json, headers=headers, params=params
)
if isinstance(json, str)
else await self._client.request(
method, url, json=json, headers=headers, params=params
method, str(url), json=json, headers=headers, params=params
)
)
try:
Expand Down Expand Up @@ -129,7 +125,6 @@ async def invoke(
params = QueryParams()
body = None
response_type = "text/plain"
url = f"{self.url}/{function_name}"

if invoke_options is not None:
headers.update(invoke_options.get("headers", {}))
Expand All @@ -153,7 +148,7 @@ async def invoke(
headers["Content-Type"] = "application/json"

response = await self._request(
"POST", url, headers=headers, json=body, params=params
"POST", [function_name], headers=headers, json=body, params=params
)
is_relay_error = response.headers.get("x-relay-header")

Expand Down
41 changes: 20 additions & 21 deletions src/functions/src/supabase_functions/_sync/functions_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from warnings import warn

from httpx import Client, HTTPError, Response, QueryParams
from yarl import URL

from ..errors import FunctionsHttpError, FunctionsRelayError
from ..utils import (
Expand All @@ -24,7 +25,7 @@ def __init__(
):
if not is_http_url(url):
raise ValueError("url must be a valid HTTP URL string")
self.url = url
self.url = URL(url)
self.headers = {
"User-Agent": f"supabase-py/functions-py v{__version__}",
**headers,
Expand All @@ -51,35 +52,32 @@ def __init__(

self.verify = bool(verify) if verify is not None else True
self.timeout = int(abs(timeout)) if timeout is not None else 60

if http_client is not None:
http_client.base_url = self.url
http_client.headers.update({**self.headers})
self._client = http_client
else:
self._client = Client(
base_url=self.url,
headers=self.headers,
verify=self.verify,
timeout=self.timeout,
proxy=proxy,
follow_redirects=True,
http2=True,
)
self._client = http_client or Client(
verify=self.verify,
timeout=self.timeout,
proxy=proxy,
follow_redirects=True,
http2=True,
)

def _request(
self,
method: Literal["GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
url: str,
path: list[str],
headers: Optional[Dict[str, str]] = None,
json: Optional[Dict[Any, Any]] = None,
params: Optional[QueryParams] = None,
) -> Response:
url = self.url.joinpath(*path)
headers = headers or dict()
headers.update(self.headers)
response = (
self._client.request(method, url, data=json, headers=headers, params=params)
self._client.request(
method, str(url), data=json, headers=headers, params=params
)
if isinstance(json, str)
else self._client.request(
method, url, json=json, headers=headers, params=params
method, str(url), json=json, headers=headers, params=params
)
)
try:
Expand Down Expand Up @@ -127,7 +125,6 @@ def invoke(
params = QueryParams()
body = None
response_type = "text/plain"
url = f"{self.url}/{function_name}"

if invoke_options is not None:
headers.update(invoke_options.get("headers", {}))
Expand All @@ -150,7 +147,9 @@ def invoke(
elif isinstance(body, dict):
headers["Content-Type"] = "application/json"

response = self._request("POST", url, headers=headers, json=body, params=params)
response = self._request(
"POST", [function_name], headers=headers, json=body, params=params
)
is_relay_error = response.headers.get("x-relay-header")

if is_relay_error and is_relay_error == "true":
Expand Down
2 changes: 1 addition & 1 deletion src/functions/tests/_async/test_function_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async def test_init_with_valid_params(valid_url, default_headers):
client = AsyncFunctionsClient(
url=valid_url, headers=default_headers, timeout=10, verify=True
)
assert client.url == valid_url
assert str(client.url) == valid_url
assert "User-Agent" in client.headers
assert client.headers["User-Agent"] == f"supabase-py/functions-py v{__version__}"
assert client._client.timeout == Timeout(10)
Expand Down
2 changes: 1 addition & 1 deletion src/functions/tests/_sync/test_function_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_init_with_valid_params(valid_url, default_headers):
client = SyncFunctionsClient(
url=valid_url, headers=default_headers, timeout=10, verify=True
)
assert client.url == valid_url
assert str(client.url) == valid_url
assert "User-Agent" in client.headers
assert client.headers["User-Agent"] == f"supabase-py/functions-py v{__version__}"
assert client._client.timeout == Timeout(10)
Expand Down
4 changes: 2 additions & 2 deletions src/functions/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_create_async_client(valid_url, valid_headers):
)

assert isinstance(client, AsyncFunctionsClient)
assert client.url == valid_url
assert str(client.url) == valid_url
assert all(client.headers[key] == value for key, value in valid_headers.items())


Expand All @@ -33,7 +33,7 @@ def test_create_sync_client(valid_url, valid_headers):
)

assert isinstance(client, SyncFunctionsClient)
assert client.url == valid_url
assert str(client.url) == valid_url
assert all(client.headers[key] == value for key, value in valid_headers.items())


Expand Down
2 changes: 1 addition & 1 deletion src/postgrest/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ unasync:

build-sync: unasync
sed -i 's/@pytest.mark.asyncio//g' tests/_sync/test_client.py
sed -i 's/_async/_sync/g' tests/_sync/test_client.py
sed -i 's/_async/_sync/g' tests/_sync/test_client.py tests/_sync/test_query_request_builder.py tests/_sync/test_filter_request_builder.py
sed -i 's/Async/Sync/g' src/postgrest/_sync/request_builder.py tests/_sync/test_client.py
sed -i 's/_client\.SyncClient/_client\.Client/g' tests/_sync/test_client.py
sed -i 's/SyncHTTPTransport/HTTPTransport/g' tests/_sync/**.py
Expand Down
1 change: 1 addition & 0 deletions src/postgrest/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies = [
"deprecation >=2.1.0",
"pydantic >=1.9,<3.0",
"strenum >=0.4.9; python_version < \"3.11\"",
"yarl>=1.20.1",
]

[project.urls]
Expand Down
74 changes: 32 additions & 42 deletions src/postgrest/src/postgrest/_async/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from deprecation import deprecated
from httpx import AsyncClient, Headers, QueryParams, Timeout
from yarl import URL

from ..base_client import BasePostgrestClient
from ..constants import (
Expand All @@ -13,7 +14,11 @@
)
from ..types import CountMethod
from ..version import __version__
from .request_builder import AsyncRequestBuilder, AsyncRPCFilterRequestBuilder
from .request_builder import (
AsyncRequestBuilder,
AsyncRPCFilterRequestBuilder,
RequestConfig,
)


class AsyncPostgrestClient(BasePostgrestClient):
Expand Down Expand Up @@ -59,52 +64,32 @@ def __init__(
else DEFAULT_POSTGREST_CLIENT_TIMEOUT
)
)

BasePostgrestClient.__init__(
self,
base_url,
URL(base_url),
schema=schema,
headers=headers,
timeout=self.timeout,
verify=self.verify,
proxy=proxy,
http_client=http_client,
)
self.session: AsyncClient = self.session

def create_session(
self,
base_url: str,
headers: Dict[str, str],
timeout: Union[int, float, Timeout],
verify: bool = True,
proxy: Optional[str] = None,
) -> AsyncClient:
http_client = None
if isinstance(self.http_client, AsyncClient):
http_client = self.http_client

if http_client is not None:
http_client.base_url = base_url
http_client.headers.update({**headers})
return http_client

return AsyncClient(
self.session = http_client or AsyncClient(
base_url=base_url,
headers=headers,
headers=self.headers,
timeout=timeout,
verify=verify,
verify=self.verify,
proxy=proxy,
follow_redirects=True,
http2=True,
)

def schema(self, schema: str):
def schema(self, schema: str) -> AsyncPostgrestClient:
"""Switch to another schema."""
return AsyncPostgrestClient(
base_url=self.base_url,
base_url=str(self.base_url),
schema=schema,
headers=self.headers,
headers=dict(self.headers),
timeout=self.timeout,
verify=self.verify,
proxy=self.proxy,
Expand All @@ -128,7 +113,9 @@ def from_(self, table: str) -> AsyncRequestBuilder:
Returns:
:class:`AsyncRequestBuilder`
"""
return AsyncRequestBuilder(self.session, f"/{table}")
return AsyncRequestBuilder(
self.session, self.base_url.joinpath(table), self.headers, self.basic_auth
)

def table(self, table: str) -> AsyncRequestBuilder:
"""Alias to :meth:`from_`."""
Expand All @@ -142,7 +129,7 @@ def from_table(self, table: str) -> AsyncRequestBuilder:
def rpc(
self,
func: str,
params: dict,
params: dict[str, str],
count: Optional[CountMethod] = None,
head: bool = False,
get: bool = False,
Expand Down Expand Up @@ -171,17 +158,20 @@ def rpc(
method = "HEAD" if head else "GET" if get else "POST"

headers = Headers({"Prefer": f"count={count}"}) if count else Headers()

if method in ("HEAD", "GET"):
return AsyncRPCFilterRequestBuilder(
self.session,
f"/rpc/{func}",
method,
headers,
QueryParams(params),
json={},
)
headers.update(self.headers)
# the params here are params to be sent to the RPC and not the queryparams!
return AsyncRPCFilterRequestBuilder(
self.session, f"/rpc/{func}", method, headers, QueryParams(), json=params
json, http_params = (
({}, QueryParams(params))
if method in ("HEAD", "GET")
else (params, QueryParams())
)
request = RequestConfig(
self.session,
self.base_url.joinpath("rpc", func),
method,
headers,
http_params,
self.basic_auth,
json,
)
return AsyncRPCFilterRequestBuilder(request)
Loading