Skip to content

Commit 991717d

Browse files
committed
restructure imports + code base
1 parent 58e9f61 commit 991717d

File tree

7 files changed

+96
-60
lines changed

7 files changed

+96
-60
lines changed

servicestack/__init__.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
__all__ = [
2-
'JsonServiceClient',
32
'IReturn',
43
'IReturnVoid',
54
'IGet',
@@ -55,11 +54,29 @@
5554
'StringResponse',
5655
'StringsResponse',
5756
'AuditBase',
57+
'JsonServiceClient',
58+
'WebServiceException',
59+
'to_json',
60+
'from_json',
5861
'qsvalue',
5962
'resolve_httpmethod',
60-
'Bytes'
63+
'index_of',
64+
'last_index_of',
65+
'left_part',
66+
'right_part',
67+
'last_left_part',
68+
'last_right_part',
69+
'split_on_first',
70+
'split_on_last',
71+
'to_timespan',
72+
'from_timespan',
73+
'from_datetime',
74+
'to_bytearray',
75+
'from_bytearray',
76+
'Bytes',
6177
]
6278

63-
from .servicestack import JsonServiceClient, json_encode, qsvalue, resolve_httpmethod, Bytes
79+
from .dtos import *
80+
from .clients import JsonServiceClient, WebServiceException, to_json, from_json, qsvalue, resolve_httpmethod
6481
from .utils import *
65-
from .client_dtos import *
82+
from .fields import *

servicestack/servicestack.py renamed to servicestack/clients.py

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
import json
33
from re import L
44

5-
from servicestack.utils import to_timespan
5+
from requests.models import Response
6+
from requests.exceptions import HTTPError
67

7-
from requests.models import HTTPError, Response
8-
9-
from servicestack.client_dtos import IDelete, IGet, IPatch, IPost, IPut, IReturn, IReturnVoid, ResponseStatus, KeyValuePair
8+
from servicestack.dtos import *
109
from servicestack.log import Log
1110
from servicestack.utils import *
11+
from servicestack.fields import *
1212

1313
from typing import Callable, TypeVar, Generic, Optional, Dict, List, Tuple, get_args, Any, Type
1414
from dataclasses import dataclass, field, fields, asdict, is_dataclass, Field
@@ -19,21 +19,10 @@
1919
import requests
2020
import base64
2121
import decimal
22+
import inspect
2223

2324
JSON_MIME_TYPE = "application/json"
2425

25-
class Bytes(mf.Field):
26-
def _serialize(self, value, attr, obj, **kwargs):
27-
return to_bytearray(value)
28-
29-
def _deserialize(self, value, attr, data, **kwargs):
30-
return from_bytearray(value)
31-
32-
mm.TYPES[timedelta] = mf.DateTime
33-
mm.TYPES[KeyValuePair] = KeyValuePair[str,str]
34-
mm.TYPES[bytes] = Bytes
35-
mm.TYPES[Bytes] = Bytes
36-
3726
@dataclass
3827
class A:
3928
l: list
@@ -129,33 +118,20 @@ def _json_encoder(obj:Any):
129118
return base64.b64encode(obj).decode('ascii')
130119
raise TypeError(f"Unsupported Type in JSON encoding: {type(obj)}")
131120

132-
def json_encode(obj:Any):
121+
def to_json(obj:Any):
133122
if is_dataclass(obj):
134-
# return obj.to_json()
135123
return json.dumps(clean_any(obj.to_dict()), default=_json_encoder)
136124
return json.dumps(obj, default=_json_encoder)
137125

138-
def _json_decoder(obj:Any):
139-
# print('ZZZZZZZZZZZZZZZZZ')
140-
# print(type(obj))
141-
return obj
142-
143126
class TypeConverters:
144127
converters: dict[Type, Callable[[Any],Any]]
145128

146129
def register(type:Type, converter:Callable[[Any],Any]):
147130
TypeConverters.converters[type] = converter
148131

149132
TypeConverters.converters = {
150-
mf.Integer: int,
151-
mf.Float: float,
152-
mf.Decimal: decimal.Decimal,
153-
mf.String: str,
154-
mf.Boolean: bool,
155133
mf.DateTime: from_datetime,
156134
mf.TimeDelta: from_timespan,
157-
Bytes: from_bytearray,
158-
bytes: from_bytearray,
159135
}
160136

161137
def is_optional(cls:Type): return f"{cls}".startswith("typing.Optional")
@@ -212,16 +188,25 @@ def convert(into:Type, obj:Any):
212188
try:
213189
return converter(obj)
214190
except Exception as e:
215-
Log.error(f"ERROR converter(obj) {into}({obj})", e)
191+
Log.error(f"converter(obj) {into}({obj})", e)
192+
raise e
193+
elif inspect.isclass(into) and issubclass(into, mf.Field):
194+
try:
195+
return into().deserialize(obj)
196+
except Exception as e:
197+
Log.error(f"into().deserialize(obj) {into}({obj})", e)
216198
raise e
217199
else:
218-
# print(f"TRY {obj} into {into}")
219200
try:
220201
return into(obj)
221202
except Exception as e:
222-
Log.error(f"ERROR into(obj) {into}({obj})", e)
203+
Log.error(f"into(obj) {into}({obj})", e)
223204
raise e
224205

206+
def from_json(into:Type, json_str:str):
207+
json_obj = json.loads(json_str)
208+
return convert(into, json_obj)
209+
225210
def ex_message(e:Exception):
226211
if hasattr(e,'message'):
227212
return e.message
@@ -378,17 +363,14 @@ def _create_response(self, response:Response, info:SendContext):
378363
json_str = response.text
379364
if Log.debug_enabled: Log.debug(f"json_str: {json_str}")
380365

381-
if not into:
366+
if into is None:
382367
return json.loads(json_str)
383368

384369
if into is str:
385370
return json_str
386371

387372
try:
388-
# res_dto = into.schema().loads(json_str, object_hook=_json_decoder)
389-
390-
json_obj = json.loads(json_str)
391-
res_dto = convert(into, json_obj)
373+
res_dto = from_json(into, json_str)
392374
except Exception as e:
393375
Log.error(f"Failed to deserialize into {into}: {e}", e)
394376
raise e
@@ -399,20 +381,32 @@ def _raise_error(self, e:Exception):
399381
return e
400382

401383
def _handle_error(self, hold_res:Response, e:Exception):
402-
if e is WebServiceException:
384+
if type(e) == WebServiceException:
403385
raise self._raise_error(e)
404386

405387
web_ex = WebServiceException()
406388
web_ex.inner_exception = e
407389
web_ex.status_code = 500
408390
web_ex.status_description = ex_message(e)
409391

410-
if e is HTTPError:
411-
web_ex.status_code = e.response.status_code
412-
if Log.debug_enabled: Log.debug(f"{e}")
392+
res = hold_res
393+
if type(e) == HTTPError and not e.response is None:
394+
res = e.response
413395

414-
if hold_res:
415-
pass
396+
if not res is None:
397+
if Log.debug_enabled(): Log.debug(f"error.text: {res.text}")
398+
web_ex.status_code = res.status_code
399+
web_ex.status_description = res.reason
400+
401+
web_ex.response_status = ResponseStatus(
402+
error_code=f"{res.status_code}",
403+
message=res.reason)
404+
405+
try:
406+
error_response = from_json(EmptyResponse, res.text)
407+
web_ex.response_status = error_response.response_status
408+
except Exception as ex:
409+
Log.error(f"Could not deserialize error response {res.text}", ex)
416410

417411
raise web_ex
418412

@@ -449,7 +443,7 @@ def send_request(self, info:SendContext):
449443
if type(body) is str:
450444
info.body_string = body
451445
else:
452-
info.body_string = json_encode(body)
446+
info.body_string = to_json(body)
453447

454448
Log.debug(f"info method: {info.method}, url: {info.url}, body_string: {info.body_string}")
455449
response:Response = None
File renamed without changes.

servicestack/fields.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from servicestack.utils import *
2+
import marshmallow.fields as mf
3+
4+
class Bytes(mf.Field):
5+
def _serialize(self, value, attr, obj, **kwargs):
6+
return to_bytearray(value)
7+
8+
def _deserialize(self, value, attr, data, **kwargs):
9+
return from_bytearray(value)

servicestack/log.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ def warn(msg:str, e:Exception=None):
4747
def error(msg:str, e:Exception=None):
4848
if Log.error_enabled():
4949
Log.logger.log(LogLevel.ERROR, msg, e)
50+
51+
# Log.levels.append(LogLevel.DEBUG)

tests/test_client.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
""" Basic Serialization Tests
22
"""
33

4-
from requests.api import put
5-
from servicestack.servicestack import json_encode
4+
from requests.api import put, request
65
import unittest
76
from .dtos import *
87
from datetime import datetime, timedelta, timezone
98

10-
from servicestack import JsonServiceClient
9+
from servicestack import JsonServiceClient, WebServiceException, to_json
1110

1211
# TEST_URL = "https://localhost:5001"
1312
TEST_URL = "http://localhost:5000"
@@ -169,7 +168,7 @@ def test_can_post_Hello_with_CustomPath(self):
169168
self.assertEqual(response.result, "Hello, World!")
170169

171170
def test_can_post_hello_with_CustomPath_json_object(self):
172-
json_obj = client.post_url("/hello", json_encode(Hello(name="World")))
171+
json_obj = client.post_url("/hello", to_json(Hello(name="World")))
173172
self.assertIsInstance(json_obj, dict)
174173
response = HelloResponse(**json_obj)
175174
self.assertEqual(response.result, "Hello, World!")
@@ -179,3 +178,18 @@ def test_can_post_HelloAllTypes(self):
179178
response:HelloAllTypesResponse=client.post(request)
180179
self.assert_HelloAllTypesResponse(response)
181180

181+
def test_can_put_HelloAllTypes(self):
182+
request=create_HelloAllTypes()
183+
response:HelloAllTypesResponse=client.put(request)
184+
self.assert_HelloAllTypesResponse(response)
185+
186+
def test_does_handle_404_error(self):
187+
request = ThrowType(type="NotFound", message="not here")
188+
try:
189+
client.put(request)
190+
self.fail("should throw")
191+
except WebServiceException as ex:
192+
status = ex.response_status
193+
self.assertEqual(status.error_code, "NotFound")
194+
self.assertEqual(status.message, "not here")
195+

tests/test_client_utils.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33

44
import unittest
55
from .dtos import *
6-
from servicestack import json_encode, qsvalue, resolve_httpmethod
6+
from servicestack import to_json, qsvalue, resolve_httpmethod
77

88
class ClientUtils(unittest.TestCase):
99

1010
def test_json_encode(self):
1111
h = Hello(name='A')
12-
self.assertEqual(json_encode(None), 'null')
13-
self.assertEqual(json_encode("A"), '"A"')
14-
self.assertEqual(json_encode(1), '1')
15-
self.assertEqual(json_encode(h), '{"name": "A"}')
16-
self.assertEqual(json_encode([h,h]), '[{"name": "A"}, {"name": "A"}]')
17-
self.assertEqual(json_encode({'a':h,'b':h}), '{"a": {"name": "A"}, "b": {"name": "A"}}')
12+
self.assertEqual(to_json(None), 'null')
13+
self.assertEqual(to_json("A"), '"A"')
14+
self.assertEqual(to_json(1), '1')
15+
self.assertEqual(to_json(h), '{"name": "A"}')
16+
self.assertEqual(to_json([h,h]), '[{"name": "A"}, {"name": "A"}]')
17+
self.assertEqual(to_json({'a':h,'b':h}), '{"a": {"name": "A"}, "b": {"name": "A"}}')
1818

1919
def test_qsvalue(self):
2020
self.assertEqual(qsvalue(None), "")

0 commit comments

Comments
 (0)