Skip to content

Commit 29cb042

Browse files
fix: handle Py311 Validation errors (#219)
* fix: handle NoneType response * fix: add default error message for non JSONDecodable objects * fix: rename message -> details * fix: handle exception instead of checking for no content --------- Co-authored-by: [email protected] <[email protected]>
1 parent c1fb15f commit 29cb042

File tree

6 files changed

+40
-17
lines changed

6 files changed

+40
-17
lines changed

postgrest/_async/request_builder.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from json import JSONDecodeError
34
from typing import Optional, Union
45

56
from httpx import Headers, QueryParams
@@ -17,7 +18,7 @@
1718
pre_update,
1819
pre_upsert,
1920
)
20-
from ..exceptions import APIError
21+
from ..exceptions import APIError, generate_default_error_message
2122
from ..types import ReturnMethod
2223
from ..utils import AsyncClient
2324

@@ -67,6 +68,8 @@ async def execute(self) -> APIResponse:
6768
raise APIError(r.json())
6869
except ValidationError as e:
6970
raise APIError(r.json()) from e
71+
except JSONDecodeError as e:
72+
raise APIError(generate_default_error_message(r))
7073

7174

7275
class AsyncSingleRequestBuilder:
@@ -114,6 +117,8 @@ async def execute(self) -> SingleAPIResponse:
114117
raise APIError(r.json())
115118
except ValidationError as e:
116119
raise APIError(r.json()) from e
120+
except JSONDecodeError as e:
121+
raise APIError(generate_default_error_message(r))
117122

118123

119124
class AsyncMaybeSingleRequestBuilder(AsyncSingleRequestBuilder):

postgrest/_sync/client.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@ def from_(self, table: str) -> SyncRequestBuilder:
6363
Args:
6464
table: The name of the table
6565
Returns:
66-
:class:`SyncRequestBuilder`
66+
:class:`AsyncRequestBuilder`
6767
"""
6868
return SyncRequestBuilder(self.session, f"/{table}")
6969

7070
def table(self, table: str) -> SyncRequestBuilder:
71-
"""Alias to self.from_()."""
71+
"""Alias to :meth:`from_`."""
7272
return self.from_(table)
7373

7474
@deprecated("0.2.0", "1.0.0", __version__, "Use self.from_() instead")
7575
def from_table(self, table: str) -> SyncRequestBuilder:
76-
"""Alias to self.from_()."""
76+
"""Alias to :meth:`from_`."""
7777
return self.from_(table)
7878

7979
def rpc(self, func: str, params: dict) -> SyncFilterRequestBuilder:
@@ -83,14 +83,14 @@ def rpc(self, func: str, params: dict) -> SyncFilterRequestBuilder:
8383
func: The name of the remote procedure to run.
8484
params: The parameters to be passed to the remote procedure.
8585
Returns:
86-
:class:`SyncFilterRequestBuilder`
86+
:class:`AsyncFilterRequestBuilder`
8787
Example:
88-
::
88+
.. code-block:: python
8989
9090
await client.rpc("foobar", {"arg": "value"}).execute()
9191
9292
.. versionchanged:: 0.11.0
93-
This method now returns a :class:`SyncFilterRequestBuilder` which allows you to
93+
This method now returns a :class:`AsyncFilterRequestBuilder` which allows you to
9494
filter on the RPC's resultset.
9595
"""
9696
# the params here are params to be sent to the RPC and not the queryparams!

postgrest/_sync/request_builder.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

3-
from typing import Optional
3+
from json import JSONDecodeError
4+
from typing import Optional, Union
45

56
from httpx import Headers, QueryParams
67
from pydantic import ValidationError
@@ -17,7 +18,7 @@
1718
pre_update,
1819
pre_upsert,
1920
)
20-
from ..exceptions import APIError
21+
from ..exceptions import APIError, generate_default_error_message
2122
from ..types import ReturnMethod
2223
from ..utils import SyncClient
2324

@@ -67,6 +68,8 @@ def execute(self) -> APIResponse:
6768
raise APIError(r.json())
6869
except ValidationError as e:
6970
raise APIError(r.json()) from e
71+
except JSONDecodeError as e:
72+
raise APIError(generate_default_error_message(r))
7073

7174

7275
class SyncSingleRequestBuilder:
@@ -114,6 +117,8 @@ def execute(self) -> SingleAPIResponse:
114117
raise APIError(r.json())
115118
except ValidationError as e:
116119
raise APIError(r.json()) from e
120+
except JSONDecodeError as e:
121+
raise APIError(generate_default_error_message(r))
117122

118123

119124
class SyncMaybeSingleRequestBuilder(SyncSingleRequestBuilder):
@@ -220,7 +225,7 @@ def select(
220225
*columns: The names of the columns to fetch.
221226
count: The method to use to get the count of rows returned.
222227
Returns:
223-
:class:`SyncSelectRequestBuilder`
228+
:class:`AsyncSelectRequestBuilder`
224229
"""
225230
method, params, headers, json = pre_select(*columns, count=count)
226231
return SyncSelectRequestBuilder(
@@ -229,7 +234,7 @@ def select(
229234

230235
def insert(
231236
self,
232-
json: dict,
237+
json: Union[dict, list],
233238
*,
234239
count: Optional[CountMethod] = None,
235240
returning: ReturnMethod = ReturnMethod.representation,
@@ -243,7 +248,7 @@ def insert(
243248
returning: Either 'minimal' or 'representation'
244249
upsert: Whether the query should be an upsert.
245250
Returns:
246-
:class:`SyncQueryRequestBuilder`
251+
:class:`AsyncQueryRequestBuilder`
247252
"""
248253
method, params, headers, json = pre_insert(
249254
json,
@@ -273,7 +278,7 @@ def upsert(
273278
ignore_duplicates: Whether duplicate rows should be ignored.
274279
on_conflict: Specified columns to be made to work with UNIQUE constraint.
275280
Returns:
276-
:class:`SyncQueryRequestBuilder`
281+
:class:`AsyncQueryRequestBuilder`
277282
"""
278283
method, params, headers, json = pre_upsert(
279284
json,
@@ -300,7 +305,7 @@ def update(
300305
count: The method to use to get the count of rows returned.
301306
returning: Either 'minimal' or 'representation'
302307
Returns:
303-
:class:`SyncFilterRequestBuilder`
308+
:class:`AsyncFilterRequestBuilder`
304309
"""
305310
method, params, headers, json = pre_update(
306311
json,
@@ -323,7 +328,7 @@ def delete(
323328
count: The method to use to get the count of rows returned.
324329
returning: Either 'minimal' or 'representation'
325330
Returns:
326-
:class:`SyncFilterRequestBuilder`
331+
:class:`AsyncFilterRequestBuilder`
327332
"""
328333
method, params, headers, json = pre_delete(
329334
count=count,

postgrest/base_request_builder.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
from json import JSONDecodeError
45
from re import search
56
from typing import (
67
Any,
@@ -153,7 +154,10 @@ def _get_count_from_http_request_response(
153154
def from_http_request_response(
154155
cls: Type[APIResponse], request_response: RequestResponse
155156
) -> APIResponse:
156-
data = request_response.json()
157+
try:
158+
data = request_response.json()
159+
except JSONDecodeError as e:
160+
return cls(data=[], count=0)
157161
count = cls._get_count_from_http_request_response(request_response)
158162
return cls(data=data, count=count)
159163

postgrest/exceptions.py

+9
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ def json(self) -> Dict[str, str]:
3939
:class:`dict`
4040
"""
4141
return self._raw_error
42+
43+
44+
def generate_default_error_message(r):
45+
return {
46+
"message": "JSON could not be generated",
47+
"code": r.status_code,
48+
"hint": "Refer to full message for details",
49+
"details": str(r.content),
50+
}

tests/_sync/test_client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_response_status_code_outside_ok(postgrest_client: SyncPostgrestClient):
9999
@pytest.mark.asyncio
100100
def test_response_maybe_single(postgrest_client: SyncPostgrestClient):
101101
with patch(
102-
"postgrest._sync.request_builder.SyncSingleRequestBuilder.execute",
102+
"postgrest._async.request_builder.AsyncSingleRequestBuilder.execute",
103103
side_effect=APIError(
104104
{"message": "mock error", "code": "400", "hint": "mock", "details": "mock"}
105105
),

0 commit comments

Comments
 (0)