aspyx-service 0.10.2__py3-none-any.whl → 0.10.4__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.

@@ -0,0 +1,97 @@
1
+ """
2
+ session related module
3
+ """
4
+ import contextvars
5
+ from typing import Type, Optional, Callable, Any, TypeVar
6
+ from datetime import datetime, timezone
7
+ from cachetools import TTLCache
8
+
9
+ from aspyx.di import injectable
10
+ from aspyx.threading import ThreadLocal
11
+
12
+
13
+ class Session:
14
+ """
15
+ Base class for objects covers data related to a server side session.
16
+ """
17
+ def __init__(self):
18
+ pass
19
+
20
+ T = TypeVar("T")
21
+
22
+ @injectable()
23
+ class SessionManager:
24
+ """
25
+ A SessionManager controls the lifecycle of sessions and is responsible to establish a session thread local.
26
+ """
27
+ #current_session = ThreadLocal[Session]()
28
+ current_session = contextvars.ContextVar("session")
29
+
30
+ @classmethod
31
+ def current(cls, type: Type[T]) -> T:
32
+ """
33
+ return the current session associated with the thread
34
+ Args:
35
+ type: the session type
36
+
37
+ Returns:
38
+ the current session
39
+ """
40
+ return cls.current_session.get()
41
+
42
+ @classmethod
43
+ def set_session(cls, session: Session) -> None:
44
+ """
45
+ set the current session in the thread context
46
+ Args:
47
+ session: the session
48
+ """
49
+ cls.current_session.set(session)
50
+
51
+ @classmethod
52
+ def delete_session(cls) -> None:
53
+ """
54
+ delete the current session
55
+ """
56
+ cls.current_session.set(None)#clear()
57
+
58
+ # constructor
59
+
60
+ def __init__(self):
61
+ self.sessions = TTLCache(maxsize=1000, ttl=3600)
62
+ self.session_creator : Optional[Callable[[Any], Session]] = None
63
+
64
+ # public
65
+
66
+ def set_session_factory(self, callable: Callable[..., Session]) -> None:
67
+ """
68
+ set a factory function that will be used to create a concrete session
69
+ Args:
70
+ callable: the function
71
+ """
72
+ self.session_creator = callable
73
+
74
+ def create_session(self, *args, **kwargs) -> Session:
75
+ """
76
+ create a session given the argument s(usually a token, etc.)
77
+ Args:
78
+ args: rest args
79
+ kwargs: keyword args
80
+
81
+ Returns:
82
+ the new session
83
+ """
84
+ return self.session_creator(*args, **kwargs)
85
+
86
+ def store_session(self, token: str, session: Session, expiry: datetime):
87
+ now = datetime.now(timezone.utc)
88
+ ttl_seconds = max(int((expiry - now).total_seconds()), 0)
89
+ self.sessions[token] = (session, ttl_seconds)
90
+
91
+ def get_session(self, token: str) -> Optional[Session]:
92
+ value = self.sessions.get(token)
93
+ if value is None:
94
+ return None
95
+
96
+ session, ttl = value
97
+ return session
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aspyx_service
3
- Version: 0.10.2
3
+ Version: 0.10.4
4
4
  Summary: Aspyx Service framework
5
5
  Author-email: Andreas Ernst <andreas.ernst7@gmail.com>
6
6
  License: MIT License
@@ -26,7 +26,8 @@ License: MIT License
26
26
  SOFTWARE.
27
27
  License-File: LICENSE
28
28
  Requires-Python: >=3.9
29
- Requires-Dist: aspyx>=1.5.1
29
+ Requires-Dist: aspyx>=1.5.3
30
+ Requires-Dist: cachetools~=5.5.2
30
31
  Requires-Dist: fastapi~=0.115.13
31
32
  Requires-Dist: httpx~=0.28.1
32
33
  Requires-Dist: msgpack~=1.1.1
@@ -60,6 +61,8 @@ Description-Content-Type: text/markdown
60
61
  - [Rest Calls](#rest-calls)
61
62
  - [Intercepting calls](#intercepting-calls)
62
63
  - [FastAPI server](#fastapi-server)
64
+ - [Session](#session)
65
+ - [Authorization](#authorization)
63
66
  - [Implementing Channels](#implementing-channels)
64
67
  - [Version History](#version-history)
65
68
 
@@ -70,19 +73,23 @@ that lets you deploy, discover and call services with different remoting protoco
70
73
 
71
74
  The basic design consists of four different concepts:
72
75
 
73
- !!! info "Service"
76
+ **Service**
77
+
74
78
  defines a group of methods that can be called either locally or remotely.
75
79
  These methods represent the functional interface exposed to clients — similar to an interface in traditional programming
76
80
 
77
- !!! info "Component"
81
+ **Component**
82
+
78
83
  a component bundles one or more services and declares the channels (protocols) used to expose them.
79
84
  Think of a component as a deployment unit or module.
80
85
 
81
- !!! info "Component Registry "
86
+ **Component Registry**
87
+
82
88
  acts as the central directory for managing available components.
83
89
  It allows the framework to register, discover, and resolve components and their services.
84
90
 
85
- !!! info "Channel"
91
+ **Channel**
92
+
86
93
  is a pluggable transport layer that defines how service method invocations are transmitted and handled.
87
94
 
88
95
  Let's look at the "interface" layer first.
@@ -111,7 +118,7 @@ class Module:
111
118
 
112
119
  @create()
113
120
  def create_registry(self) -> ConsulComponentRegistry:
114
- return ConsulComponentRegistry(Server.port, "http://localhost:8500") # a consul based registry!
121
+ return ConsulComponentRegistry(Server.port, Consul(host="localhost", port=8500)) # a consul based registry!
115
122
 
116
123
  environment = Environment(Module)
117
124
  service_manager = environment.get(ServiceManager)
@@ -162,10 +169,24 @@ environment = server.boot(Module)
162
169
  Of course, service can also be called locally. In case of multiple possible channels, a keyword argument is used to
163
170
  determine a specific channel. As a local channel has the name "local", the appropriate call is:
164
171
 
172
+ **Example**:
173
+
165
174
  ```python
166
175
  service = service_manager.get_service(TestService, preferred_channel="local")
167
176
  ```
168
177
 
178
+ The default can be set globally with the method `set_preferred_channel(channel: str)`
179
+
180
+ Injecting services is also possible via the decorator `@inject_service(preferred_channel=""")`
181
+
182
+ **Example**:
183
+
184
+ ```python
185
+ @inject_service()
186
+ def set_service(self, service: TestService)
187
+ self.service = service
188
+ ```
189
+
169
190
  ## Features
170
191
 
171
192
  The library offers:
@@ -348,12 +369,14 @@ Channels implement the possible transport layer protocols. In the sense of a dyn
348
369
  Several channels are implemented:
349
370
 
350
371
  - `dispatch-json`
351
- channel that dispatches generic `Request` objects via a `invoke` POST-call
372
+ channel that posts generic `Request` objects via a `invoke` POST-call
352
373
  - `dispatch-msgpack`
353
- channel that dispatches generic `Request` objects via a `invoke` POST-call after packing the json with msgpack
374
+ channel that posts generic `Request` objects via a `invoke` POST-call after packing the json with msgpack
354
375
  - `rest`
355
376
  channel that executes regular rest-calls as defined by a couple of decorators.
356
377
 
378
+ The `dispatch`channels have the big advantage, that you don`t have to deal with additional http decorators!
379
+
357
380
  All channels react on changed URLs as provided by the component registry.
358
381
 
359
382
  A so called `URLSelector` is used internally to provide URLs for every single call. Two subclasses exist that offer a different logic
@@ -361,7 +384,7 @@ A so called `URLSelector` is used internally to provide URLs for every single ca
361
384
  - `FirstURLSelector` always returns the first URL of the list of possible URLs
362
385
  - `RoundRobinURLSelector` switches sequentially between all URLs.
363
386
 
364
- To customize the behavior, an around advice can be implemented easily:
387
+ To customize the behavior, an `around` advice can be implemented easily:
365
388
 
366
389
  **Example**:
367
390
 
@@ -478,6 +501,14 @@ class Module():
478
501
  This setup will also expose all service interfaces decorated with the corresponding http decorators!
479
502
  No need to add any FastAPI decorators, since the mapping is already done internally!
480
503
 
504
+ ## Session
505
+
506
+ TODO
507
+
508
+ ## Authorization
509
+
510
+ TODO
511
+
481
512
  ## Implementing Channels
482
513
 
483
514
  To implement a new channel, you only need to derive from one of the possible base classes ( `Channel` or `HTTPXChannel` that already has a `httpx` client)
@@ -0,0 +1,14 @@
1
+ aspyx_service/__init__.py,sha256=h9zcGzYaVdU2_mXON-k-mgYErEJ7eIs-wjNDKtet1_s,2488
2
+ aspyx_service/authorization.py,sha256=vBM8uPsAZwMiTilqFZMJ101Qy37gL2Y9vdGTLp-ykFg,3983
3
+ aspyx_service/channels.py,sha256=3Fv6055n1hw8HQ6MKu2BROsq6gmPdKIhayUhQiTRLic,16461
4
+ aspyx_service/healthcheck.py,sha256=vjfY7s5kd5mRJynVpvAJ4BvVF7QY1xrvj94Y-m041LQ,5615
5
+ aspyx_service/registries.py,sha256=bnTjKb40fbZXA52E2lDSEzCWI5_NBKZzQjc8ffufB5g,8039
6
+ aspyx_service/restchannel.py,sha256=wutLGnxqMAS1oX7cc1pvN8qIIpjeBEvPz_hsKeWHVZs,8474
7
+ aspyx_service/serialization.py,sha256=OrwOAUsHQyGDyhYTkTc-0v8urYMbh0_3fgkpTvNOl0o,4214
8
+ aspyx_service/server.py,sha256=_LFRy1XIXTbu7CLoXsoPGQwKHkpSPDh82VV4ppahzr0,9057
9
+ aspyx_service/service.py,sha256=drETAZasbYJZisnmbhAqW0-mHghJ3IWyPaU-7etxvBI,27003
10
+ aspyx_service/session.py,sha256=ytWRTlnu1kDpTkLBCy_WF2i-mdffG-exIqsUQZ1Udo0,2592
11
+ aspyx_service-0.10.4.dist-info/METADATA,sha256=-WA5_ta5gP3AXy8EA0JhFzoFstaS_RgeGjyglOAYqXw,17499
12
+ aspyx_service-0.10.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ aspyx_service-0.10.4.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
14
+ aspyx_service-0.10.4.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- aspyx_service/__init__.py,sha256=6t24VPrSCG83EAvYlqCKdEcEbyCY3vrSb5GoAx01Ymg,1662
2
- aspyx_service/channels.py,sha256=9EjnY6DV_toqeQNswYZgmAtirXM_TTLGErs6hb1U9yw,8934
3
- aspyx_service/healthcheck.py,sha256=8ZPSkAx6ypoYaxDMkJT_MtL2pEN2LcUAishAWPCy-3I,5624
4
- aspyx_service/registries.py,sha256=JSsD32F8VffZMHyEDuapEWtvmem5SK9kR6bgsFRLFZQ,8002
5
- aspyx_service/restchannel.py,sha256=OVGyKNtORJEFnWsFB5MPkNldVFfJTGuUa4X658_x9Kg,9169
6
- aspyx_service/serialization.py,sha256=lEr106yiZ0UzVxGTGdiorZSSSNI7vP4C7RyrBJku-U0,4133
7
- aspyx_service/server.py,sha256=PHKx3O90jnxm8dA4z331_51znhU-HlRB2Fa1AikmFXA,7052
8
- aspyx_service/service.py,sha256=-hhxRpEtMn-JP57bHNF521zbGMHUy5YaXkbXyiu7dW4,25927
9
- aspyx_service-0.10.2.dist-info/METADATA,sha256=KEdh30D63sXs26FVOtCph_LF521bBJa7A6Qpc8d-9iQ,16966
10
- aspyx_service-0.10.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- aspyx_service-0.10.2.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
12
- aspyx_service-0.10.2.dist-info/RECORD,,