aspyx-service 0.10.7__py3-none-any.whl → 0.11.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aspyx-service might be problematic. Click here for more details.
- aspyx_service/__init__.py +5 -0
- aspyx_service/channels.py +24 -60
- aspyx_service/healthcheck.py +2 -2
- aspyx_service/protobuf.py +1093 -0
- aspyx_service/restchannel.py +23 -3
- aspyx_service/server.py +97 -54
- aspyx_service/service.py +37 -14
- {aspyx_service-0.10.7.dist-info → aspyx_service-0.11.1.dist-info}/METADATA +21 -31
- aspyx_service-0.11.1.dist-info/RECORD +14 -0
- aspyx_service-0.10.7.dist-info/RECORD +0 -13
- {aspyx_service-0.10.7.dist-info → aspyx_service-0.11.1.dist-info}/WHEEL +0 -0
- {aspyx_service-0.10.7.dist-info → aspyx_service-0.11.1.dist-info}/licenses/LICENSE +0 -0
aspyx_service/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@ from .healthcheck import health_checks, health_check, HealthCheckManager, Health
|
|
|
12
12
|
from .restchannel import RestChannel, post, get, put, delete, QueryParam, Body, rest
|
|
13
13
|
from .session import Session, SessionManager, SessionContext
|
|
14
14
|
from .authorization import AuthorizationManager, AbstractAuthorizationFactory
|
|
15
|
+
from .protobuf import ProtobufManager
|
|
15
16
|
|
|
16
17
|
@module()
|
|
17
18
|
class ServiceModule:
|
|
@@ -48,6 +49,10 @@ __all__ = [
|
|
|
48
49
|
"MissingTokenException",
|
|
49
50
|
"AuthorizationException",
|
|
50
51
|
|
|
52
|
+
# protobuf
|
|
53
|
+
|
|
54
|
+
"ProtobufManager",
|
|
55
|
+
|
|
51
56
|
# authorization
|
|
52
57
|
|
|
53
58
|
"AuthorizationManager",
|
aspyx_service/channels.py
CHANGED
|
@@ -16,7 +16,7 @@ from pydantic import BaseModel
|
|
|
16
16
|
from aspyx.di.configuration import inject_value
|
|
17
17
|
from aspyx.reflection import DynamicProxy, TypeDescriptor
|
|
18
18
|
from aspyx.threading import ThreadLocal, ContextLocal
|
|
19
|
-
from aspyx.util import get_deserializer, TypeDeserializer, TypeSerializer, get_serializer
|
|
19
|
+
from aspyx.util import get_deserializer, TypeDeserializer, TypeSerializer, get_serializer, CopyOnWriteCache
|
|
20
20
|
from .service import ServiceManager, ServiceCommunicationException, TokenExpiredException, InvalidTokenException, \
|
|
21
21
|
AuthorizationException, MissingTokenException
|
|
22
22
|
|
|
@@ -61,6 +61,9 @@ class TokenContext:
|
|
|
61
61
|
cls.refresh_token.reset(refresh_token)
|
|
62
62
|
|
|
63
63
|
class HTTPXChannel(Channel):
|
|
64
|
+
"""
|
|
65
|
+
A channel using the httpx clients.
|
|
66
|
+
"""
|
|
64
67
|
__slots__ = [
|
|
65
68
|
"client",
|
|
66
69
|
"async_client",
|
|
@@ -75,28 +78,6 @@ class HTTPXChannel(Channel):
|
|
|
75
78
|
client_local = ThreadLocal[Client]()
|
|
76
79
|
async_client_local = ThreadLocal[AsyncClient]()
|
|
77
80
|
|
|
78
|
-
# class methods
|
|
79
|
-
|
|
80
|
-
@classmethod
|
|
81
|
-
def to_dict(cls, obj: Any) -> Any:
|
|
82
|
-
if isinstance(obj, BaseModel):
|
|
83
|
-
return obj.model_dump()
|
|
84
|
-
|
|
85
|
-
elif is_dataclass(obj):
|
|
86
|
-
return {
|
|
87
|
-
f.name: cls.to_dict(getattr(obj, f.name))
|
|
88
|
-
|
|
89
|
-
for f in fields(obj)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
elif isinstance(obj, (list, tuple)):
|
|
93
|
-
return [cls.to_dict(item) for item in obj]
|
|
94
|
-
|
|
95
|
-
elif isinstance(obj, dict):
|
|
96
|
-
return {key: cls.to_dict(value) for key, value in obj.items()}
|
|
97
|
-
|
|
98
|
-
return obj
|
|
99
|
-
|
|
100
81
|
# constructor
|
|
101
82
|
|
|
102
83
|
def __init__(self):
|
|
@@ -104,9 +85,8 @@ class HTTPXChannel(Channel):
|
|
|
104
85
|
|
|
105
86
|
self.timeout = 1000.0
|
|
106
87
|
self.service_names: dict[Type, str] = {}
|
|
107
|
-
self.serializers
|
|
108
|
-
self.deserializers
|
|
109
|
-
self.optimize_serialization = True
|
|
88
|
+
self.serializers = CopyOnWriteCache[Callable, list[Callable]]()
|
|
89
|
+
self.deserializers = CopyOnWriteCache[Callable, Callable]()
|
|
110
90
|
|
|
111
91
|
# inject
|
|
112
92
|
|
|
@@ -132,7 +112,7 @@ class HTTPXChannel(Channel):
|
|
|
132
112
|
|
|
133
113
|
serializers = [get_serializer(type) for type in param_types]
|
|
134
114
|
|
|
135
|
-
self.serializers
|
|
115
|
+
self.serializers.put(method, serializers)
|
|
136
116
|
|
|
137
117
|
return serializers
|
|
138
118
|
|
|
@@ -143,7 +123,7 @@ class HTTPXChannel(Channel):
|
|
|
143
123
|
|
|
144
124
|
deserializer = get_deserializer(return_type)
|
|
145
125
|
|
|
146
|
-
self.deserializers
|
|
126
|
+
self.deserializers.put(method, deserializer)
|
|
147
127
|
|
|
148
128
|
return deserializer
|
|
149
129
|
|
|
@@ -291,16 +271,11 @@ class DispatchJSONChannel(HTTPXChannel):
|
|
|
291
271
|
def invoke(self, invocation: DynamicProxy.Invocation):
|
|
292
272
|
service_name = self.service_names[invocation.type] # map type to registered service name
|
|
293
273
|
|
|
294
|
-
request
|
|
295
|
-
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}"
|
|
296
|
-
|
|
274
|
+
request = {
|
|
275
|
+
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}",
|
|
276
|
+
"args": self.serialize_args(invocation)
|
|
297
277
|
}
|
|
298
278
|
|
|
299
|
-
if self.optimize_serialization:
|
|
300
|
-
request["args"] = self.serialize_args(invocation)
|
|
301
|
-
else:
|
|
302
|
-
request["args"] = self.to_dict(invocation.args)
|
|
303
|
-
|
|
304
279
|
try:
|
|
305
280
|
http_result = self.request( "post", f"{self.get_url()}/invoke", json=request, timeout=self.timeout)
|
|
306
281
|
result = http_result.json()
|
|
@@ -317,15 +292,11 @@ class DispatchJSONChannel(HTTPXChannel):
|
|
|
317
292
|
|
|
318
293
|
async def invoke_async(self, invocation: DynamicProxy.Invocation):
|
|
319
294
|
service_name = self.service_names[invocation.type] # map type to registered service name
|
|
320
|
-
request
|
|
321
|
-
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}"
|
|
295
|
+
request = {
|
|
296
|
+
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}",
|
|
297
|
+
"args": self.serialize_args(invocation)
|
|
322
298
|
}
|
|
323
299
|
|
|
324
|
-
if self.optimize_serialization:
|
|
325
|
-
request["args"] = self.serialize_args(invocation)
|
|
326
|
-
else:
|
|
327
|
-
request["args"] = self.to_dict(invocation.args)
|
|
328
|
-
|
|
329
300
|
try:
|
|
330
301
|
data = await self.request_async("post", f"{self.get_url()}/invoke", json=request, timeout=self.timeout)
|
|
331
302
|
result = data.json()
|
|
@@ -361,15 +332,11 @@ class DispatchMSPackChannel(HTTPXChannel):
|
|
|
361
332
|
|
|
362
333
|
def invoke(self, invocation: DynamicProxy.Invocation):
|
|
363
334
|
service_name = self.service_names[invocation.type] # map type to registered service name
|
|
364
|
-
request
|
|
365
|
-
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}"
|
|
335
|
+
request = {
|
|
336
|
+
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}",
|
|
337
|
+
"args": self.serialize_args(invocation)
|
|
366
338
|
}
|
|
367
339
|
|
|
368
|
-
if self.optimize_serialization:
|
|
369
|
-
request["args"] = self.serialize_args(invocation)
|
|
370
|
-
else:
|
|
371
|
-
request["args"] = self.to_dict(invocation.args)
|
|
372
|
-
|
|
373
340
|
try:
|
|
374
341
|
packed = msgpack.packb(request, use_bin_type=True)
|
|
375
342
|
|
|
@@ -397,10 +364,11 @@ class DispatchMSPackChannel(HTTPXChannel):
|
|
|
397
364
|
if "invalid_token" in www_auth:
|
|
398
365
|
if 'expired' in www_auth:
|
|
399
366
|
raise TokenExpiredException() from e
|
|
400
|
-
|
|
367
|
+
|
|
368
|
+
if 'missing' in www_auth:
|
|
401
369
|
raise MissingTokenException() from e
|
|
402
|
-
|
|
403
|
-
|
|
370
|
+
|
|
371
|
+
raise InvalidTokenException() from e
|
|
404
372
|
|
|
405
373
|
raise RemoteServiceException(str(e)) from e
|
|
406
374
|
except httpx.HTTPError as e:
|
|
@@ -417,15 +385,11 @@ class DispatchMSPackChannel(HTTPXChannel):
|
|
|
417
385
|
|
|
418
386
|
async def invoke_async(self, invocation: DynamicProxy.Invocation):
|
|
419
387
|
service_name = self.service_names[invocation.type] # map type to registered service name
|
|
420
|
-
request
|
|
421
|
-
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}"
|
|
388
|
+
request = {
|
|
389
|
+
"method": f"{self.component_descriptor.name}:{service_name}:{invocation.method.__name__}",
|
|
390
|
+
"args": self.serialize_args(invocation)
|
|
422
391
|
}
|
|
423
392
|
|
|
424
|
-
if self.optimize_serialization:
|
|
425
|
-
request["args"] = self.serialize_args(invocation)
|
|
426
|
-
else:
|
|
427
|
-
request["args"] = self.to_dict(invocation.args)
|
|
428
|
-
|
|
429
393
|
try:
|
|
430
394
|
packed = msgpack.packb(request, use_bin_type=True)
|
|
431
395
|
|
aspyx_service/healthcheck.py
CHANGED
|
@@ -15,7 +15,7 @@ from aspyx.reflection import Decorators, TypeDescriptor
|
|
|
15
15
|
|
|
16
16
|
def health_checks():
|
|
17
17
|
"""
|
|
18
|
-
Instances of classes that are annotated with @
|
|
18
|
+
Instances of classes that are annotated with @health_checks contain healt mehtods.
|
|
19
19
|
"""
|
|
20
20
|
def decorator(cls):
|
|
21
21
|
Decorators.add(cls, health_checks)
|
|
@@ -31,7 +31,7 @@ def health_checks():
|
|
|
31
31
|
|
|
32
32
|
def health_check(name="", cache = 0, fail_if_slower_than = 0):
|
|
33
33
|
"""
|
|
34
|
-
Methods annotated with `@
|
|
34
|
+
Methods annotated with `@health_check` specify health checks that will be executed.
|
|
35
35
|
"""
|
|
36
36
|
def decorator(func):
|
|
37
37
|
Decorators.add(func, health_check, name, cache, fail_if_slower_than)
|