You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Service manager for asyncio (and classic blocking code since version 0.10.0).
3
+
2
4
[](https://github.com/pohmelie/facet/actions/workflows/ci.yaml)
3
5
[](https://codecov.io/gh/pohmelie/facet)
- Inheritance restrict naming and forces `super()` calls.
13
40
- Forced logging module and logging configuration.
41
+
### Blocking code
42
+
-[`ExitStack`](https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack) is too low-level to manage services.
43
+
- Common api for async and blocking worlds.
14
44
15
-
# Features
45
+
##Features
16
46
- Simple (`start`, `stop`, `dependencies` and `add_task`).
17
47
- Configurable via inheritance (graceful shutdown timeout).
18
48
- Mixin (no `super()` required).
19
-
- Requires no runner engine (`Worker`, `Runner`, etc.) just plain `await` or `async with`.
49
+
- Requires no runner engine (`Worker`, `Runner`, etc.) just plain `await` or `async with`/`with`.
20
50
21
-
# License
51
+
##License
22
52
`facet` is offered under MIT license.
23
53
24
-
# Requirements
25
-
* python 3.6+
26
-
27
-
# Usage
28
-
```python
29
-
import asyncio
30
-
import logging
54
+
## Requirements
55
+
- python 3.11+
31
56
32
-
from facet import ServiceMixin
57
+
## Usage
33
58
59
+
### Asyncio
34
60
35
-
classB(ServiceMixin):
61
+
```python
62
+
import asyncio
63
+
from facet import AsyncioServiceMixin
36
64
65
+
classB(AsyncioServiceMixin):
37
66
def__init__(self):
38
67
self.value =0
39
68
40
69
asyncdefstart(self):
41
70
self.value +=1
42
-
logging.info("b started")
71
+
print("b started")
43
72
44
73
asyncdefstop(self):
45
74
self.value -=1
46
-
logging.info("b stopped")
47
-
48
-
49
-
classA(ServiceMixin):
75
+
print("b stopped")
50
76
77
+
classA(AsyncioServiceMixin):
51
78
def__init__(self):
52
79
self.b = B()
53
80
@@ -56,26 +83,24 @@ class A(ServiceMixin):
56
83
return [self.b]
57
84
58
85
asyncdefstart(self):
59
-
logging.info("a started")
86
+
print("a started")
60
87
61
88
asyncdefstop(self):
62
-
logging.info("a stopped")
89
+
print("a stopped")
63
90
64
-
65
-
logging.basicConfig(level=logging.DEBUG)
66
91
asyncio.run(A().run())
67
92
```
68
93
This will produce:
69
94
```
70
-
INFO:root:b started
71
-
INFO:root:a started
95
+
b started
96
+
a started
72
97
```
73
98
Start and stop order determined by strict rule: **dependencies must be started first and stopped last**. That is why `B` starts before `A`. Since `A` may use `B` in `start` routine.
74
99
75
100
Hit `ctrl-c` and you will see:
76
101
```
77
-
INFO:root:a stopped
78
-
INFO:root:b stopped
102
+
a stopped
103
+
b stopped
79
104
Traceback (most recent call last):
80
105
...
81
106
KeyboardInterrupt
@@ -98,33 +123,29 @@ asyncio.run(main())
98
123
99
124
Another service feature is `add_task` method:
100
125
```python
101
-
classA(ServiceMixin):
102
-
126
+
classA(AsyncioServiceMixin):
103
127
asyncdeftask(self):
104
128
await asyncio.sleep(1)
105
-
logging.info("task done")
129
+
print("task done")
106
130
107
131
asyncdefstart(self):
108
132
self.add_task(self.task())
109
-
logging.info("start done")
133
+
print("start done")
110
134
111
-
112
-
logging.basicConfig(level=logging.DEBUG)
113
135
asyncio.run(A().run())
114
136
```
115
137
This will lead to background task creation and handling:
116
138
```
117
-
INFO:root:start done
118
-
INFO:root:task done
139
+
start done
140
+
task done
119
141
```
120
142
Any non-handled exception on background task will lead the whole service stack crashed. This is also a key feature to fall down fast and loud.
121
143
122
144
All background tasks will be cancelled and awaited on service stop.
123
145
124
146
You can manage dependencies start/stop to start sequently, parallel or mixed. Like this:
125
147
```python
126
-
classA(ServiceMixin):
127
-
148
+
classA(AsyncioServiceMixin):
128
149
def__init__(self):
129
150
self.b = B()
130
151
self.c = C()
@@ -141,65 +162,143 @@ This leads to first `b` and `c` starts parallel, after they successfully started
141
162
142
163
The rule here is **first nesting level is sequential, second nesting level is parallel**
143
164
144
-
# API
165
+
### Blocking code
166
+
Since version 0.10.0 `facet` can be used in blocking code with pretty same rules. **But with limited API**. For example:
167
+
```python
168
+
from facet import BlockingServiceMixin
169
+
170
+
classB(BlockingServiceMixin):
171
+
def__init__(self):
172
+
self.value =0
173
+
174
+
defstart(self):
175
+
self.value +=1
176
+
print("b started")
177
+
178
+
defstop(self):
179
+
self.value -=1
180
+
print("b stopped")
181
+
182
+
classA(BlockingServiceMixin):
183
+
def__init__(self):
184
+
self.b = B()
185
+
186
+
@property
187
+
defdependencies(self):
188
+
return [self.b]
189
+
190
+
defstart(self):
191
+
print("a started")
192
+
193
+
defstop(self):
194
+
print("a stopped")
195
+
196
+
with A() as a:
197
+
assert a.b.value ==1
198
+
```
199
+
This will produce:
200
+
```
201
+
b started
202
+
a started
203
+
a stopped
204
+
b stopped
205
+
```
206
+
As you can see, there is no `wait` method. Waiting and background tasks are on user shoulders and technically can be implemented with `concurrent.futures` module. But `facet` do not provide such functionality, since there are a lot of ways to do it: `threading`/`multiprocessing` and their primitives.
207
+
208
+
Also, there are no «sequential, parallel and mixed starts/stops for dependencies» feature. So, just put dependencies in `dependencies` property as a plain `list` and they will be started/stopped sequentially.
209
+
210
+
## API
211
+
### Asyncio
145
212
Here is public methods you get on inheritance/mixin:
146
-
## `wait`
213
+
214
+
#### `start`
147
215
```python
148
-
asyncdefwait(self):
216
+
asyncdefstart(self):
217
+
pass
149
218
```
150
-
Wait for service stop. Service must be started. This is useful when you use service as a context manager.
0 commit comments