Skip to content

Commit

Permalink
feat: implement async sync with unasync-cli (#30)
Browse files Browse the repository at this point in the history
Co-authored-by: Dani Reinón <dani@dribo.es>
  • Loading branch information
leynier and Dani Reinón committed Nov 16, 2021
1 parent ba83ba4 commit b1423b5
Show file tree
Hide file tree
Showing 23 changed files with 676 additions and 79 deletions.
56 changes: 47 additions & 9 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions postgrest_py/__init__.py
@@ -1,2 +1,13 @@
from postgrest_py.__version__ import __version__
from postgrest_py.client import DEFAULT_POSTGREST_CLIENT_HEADERS, Client, PostgrestClient
from postgrest_py._async.client import AsyncPostgrestClient # noqa: F401
from postgrest_py._async.request_builder import AsyncFilterRequestBuilder # noqa: F401
from postgrest_py._async.request_builder import AsyncQueryRequestBuilder # noqa: F401
from postgrest_py._async.request_builder import AsyncRequestBuilder # noqa: F401
from postgrest_py._async.request_builder import AsyncSelectRequestBuilder # noqa: F401
from postgrest_py._sync.client import SyncPostgrestClient # noqa: F401
from postgrest_py._sync.request_builder import SyncFilterRequestBuilder # noqa: F401
from postgrest_py._sync.request_builder import SyncQueryRequestBuilder # noqa: F401
from postgrest_py._sync.request_builder import SyncRequestBuilder # noqa: F401
from postgrest_py._sync.request_builder import SyncSelectRequestBuilder # noqa: F401
from postgrest_py.config import DEFAULT_POSTGREST_CLIENT_HEADERS # noqa: F401
from postgrest_py.deprecated_client import Client, PostgrestClient # noqa: F401
from postgrest_py.deprecated_get_request_builder import GetRequestBuilder # noqa: F401
Empty file added postgrest_py/_async/__init__.py
Empty file.
26 changes: 8 additions & 18 deletions postgrest_py/client.py → postgrest_py/_async/client.py
@@ -1,18 +1,16 @@
from typing import Dict, Optional, Union

from deprecation import deprecated
from httpx import AsyncClient, BasicAuth, Response
from httpx import BasicAuth, Response

from postgrest_py.__version__ import __version__
from postgrest_py.request_builder import RequestBuilder
from postgrest_py.config import DEFAULT_POSTGREST_CLIENT_HEADERS
from postgrest_py.utils import AsyncClient

DEFAULT_POSTGREST_CLIENT_HEADERS: Dict[str, str] = {
"Accept": "application/json",
"Content-Type": "application/json",
}
from .request_builder import AsyncRequestBuilder


class PostgrestClient:
class AsyncPostgrestClient:
"""PostgREST client."""

def __init__(
Expand Down Expand Up @@ -66,12 +64,12 @@ def schema(self, schema: str):
self.session.headers.update({"Accept-Profile": schema, "Content-Profile": schema})
return self

def from_(self, table: str) -> RequestBuilder:
def from_(self, table: str) -> AsyncRequestBuilder:
"""Perform a table operation."""
return RequestBuilder(self.session, f"/{table}")
return AsyncRequestBuilder(self.session, f"/{table}")

@deprecated("0.2.0", "1.0.0", __version__, "Use PostgrestClient.from_() instead")
def from_table(self, table: str) -> RequestBuilder:
def from_table(self, table: str) -> AsyncRequestBuilder:
"""Alias to Self.from_()."""
return self.from_(table)

Expand All @@ -80,11 +78,3 @@ async def rpc(self, func: str, params: dict) -> Response:
path = f"/rpc/{func}"
r = await self.session.post(path, json=params)
return r


class Client(PostgrestClient):
"""Alias to PostgrestClient."""

@deprecated("0.2.0", "1.0.0", __version__, "Use PostgrestClient instead")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Up @@ -7,16 +7,12 @@
else:
from typing import Literal

from deprecation import deprecated
from httpx import AsyncClient

from postgrest_py.__version__ import __version__
from postgrest_py.utils import sanitize_param, sanitize_pattern_param
from postgrest_py.utils import AsyncClient, sanitize_param, sanitize_pattern_param

CountMethod = Union[Literal["exact"], Literal["planned"], Literal["estimated"]]


class RequestBuilder:
class AsyncRequestBuilder:
def __init__(self, session: AsyncClient, path: str):
self.session = session
self.path = path
Expand All @@ -31,7 +27,7 @@ def select(self, *columns: str, count: Optional[CountMethod] = None):
if count:
self.session.headers["Prefer"] = f"count={count}"

return SelectRequestBuilder(self.session, self.path, method, {})
return AsyncSelectRequestBuilder(self.session, self.path, method, {})

def insert(self, json: dict, *, count: Optional[CountMethod] = None, upsert=False):
prefer_headers = ["return=representation"]
Expand All @@ -40,24 +36,24 @@ def insert(self, json: dict, *, count: Optional[CountMethod] = None, upsert=Fals
if upsert:
prefer_headers.append("resolution=merge-duplicates")
self.session.headers["prefer"] = ",".join(prefer_headers)
return QueryRequestBuilder(self.session, self.path, "POST", json)
return AsyncQueryRequestBuilder(self.session, self.path, "POST", json)

def update(self, json: dict, *, count: Optional[CountMethod] = None):
prefer_headers = ["return=representation"]
if count:
prefer_headers.append(f"count={count}")
self.session.headers["prefer"] = ",".join(prefer_headers)
return FilterRequestBuilder(self.session, self.path, "PATCH", json)
return AsyncFilterRequestBuilder(self.session, self.path, "PATCH", json)

def delete(self, *, count: Optional[CountMethod] = None):
prefer_headers = ["return=representation"]
if count:
prefer_headers.append(f"count={count}")
self.session.headers["prefer"] = ",".join(prefer_headers)
return FilterRequestBuilder(self.session, self.path, "DELETE", {})
return AsyncFilterRequestBuilder(self.session, self.path, "DELETE", {})


class QueryRequestBuilder:
class AsyncQueryRequestBuilder:
def __init__(self, session: AsyncClient, path: str, http_method: str, json: dict):
self.session = session
self.path = path
Expand All @@ -81,7 +77,7 @@ async def execute(self) -> Tuple[Any, Optional[int]]:
return r.json(), count


class FilterRequestBuilder(QueryRequestBuilder):
class AsyncFilterRequestBuilder(AsyncQueryRequestBuilder):
def __init__(self, session: AsyncClient, path: str, http_method: str, json: dict):
super().__init__(session, path, http_method, json)

Expand Down Expand Up @@ -183,7 +179,7 @@ def match(self, query: Dict[str, Any]):
return updated_query


class SelectRequestBuilder(FilterRequestBuilder):
class AsyncSelectRequestBuilder(AsyncFilterRequestBuilder):
def __init__(self, session: AsyncClient, path: str, http_method: str, json: dict):
super().__init__(session, path, http_method, json)

Expand All @@ -207,11 +203,3 @@ def range(self, start: int, end: int):
def single(self):
self.session.headers["Accept"] = "application/vnd.pgrst.object+json"
return self


class GetRequestBuilder(SelectRequestBuilder):
"""Alias to SelectRequestBuilder."""

@deprecated("0.4.0", "1.0.0", __version__, "Use SelectRequestBuilder instead")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Empty file added postgrest_py/_sync/__init__.py
Empty file.
80 changes: 80 additions & 0 deletions postgrest_py/_sync/client.py
@@ -0,0 +1,80 @@
from typing import Dict, Optional, Union

from deprecation import deprecated
from httpx import BasicAuth, Response

from postgrest_py.__version__ import __version__
from postgrest_py.config import DEFAULT_POSTGREST_CLIENT_HEADERS
from postgrest_py.utils import SyncClient

from .request_builder import SyncRequestBuilder


class SyncPostgrestClient:
"""PostgREST client."""

def __init__(
self,
base_url: str,
*,
schema: str = "public",
headers: Dict[str, str] = DEFAULT_POSTGREST_CLIENT_HEADERS,
) -> None:
headers = {
**headers,
"Accept-Profile": schema,
"Content-Profile": schema,
}
self.session = SyncClient(base_url=base_url, headers=headers)

def __enter__(self):
return self

def __exit__(self, exc_type, exc, tb) -> None:
self.aclose()

def aclose(self) -> None:
self.session.aclose()

def auth(
self,
token: Optional[str],
*,
username: Union[str, bytes, None] = None,
password: Union[str, bytes] = "",
):
"""
Authenticate the client with either bearer token or basic authentication.
Raise `ValueError` if neither authentication scheme is provided.
Bearer token is preferred if both ones are provided.
"""
if token:
self.session.headers["Authorization"] = f"Bearer {token}"
elif username:
self.session.auth = BasicAuth(username, password)
else:
raise ValueError(
"Neither bearer token or basic authentication scheme is provided"
)
return self

def schema(self, schema: str):
"""Switch to another schema."""
self.session.headers.update({"Accept-Profile": schema, "Content-Profile": schema})
return self

def from_(self, table: str) -> SyncRequestBuilder:
"""Perform a table operation."""
return SyncRequestBuilder(self.session, f"/{table}")

@deprecated("0.2.0", "1.0.0", __version__, "Use PostgrestClient.from_() instead")
def from_table(self, table: str) -> SyncRequestBuilder:
"""Alias to Self.from_()."""
return self.from_(table)

def rpc(self, func: str, params: dict) -> Response:
"""Perform a stored procedure call."""
path = f"/rpc/{func}"
r = self.session.post(path, json=params)
return r

0 comments on commit b1423b5

Please sign in to comment.