Skip to content

Commit 06bad31

Browse files
committed
update: README
1 parent 442b972 commit 06bad31

File tree

4 files changed

+175
-11
lines changed

4 files changed

+175
-11
lines changed

README.md

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,161 @@
55
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/os-3m-engine.svg)](https://pypi.python.org/pypi/os-3m-engine)
66
[![PyPI](https://img.shields.io/pypi/v/os-3m-engine.svg)](https://pypi.python.org/pypi/os-3m-engine)
77

8+
Multi-thread engine for 3(or 2) stages job.
9+
10+
11+
12+
Although multi-thread of python is buggy and slow, it is still necessary to write multi-thread program. Typically, producer-consumer is the most common model(so called 2-stage job), further more a transporter is needed between them(3-stage job). This library is used to simplify creating 3(or 2) stages program.
13+
14+
15+
16+
The 3 stages are:
17+
18+
* Frontend: think as producer, usually used to create or receive data.
19+
* Transport: receive data from frontend stage, transform and send the transformed data to backend.
20+
* Backend: think as consumer, process the data received from transport.
21+
22+
23+
24+
Something else need to know:
25+
26+
* Each of the stage can be multi-thread.
27+
* Frontend is required, transport or backend can be omitted.
28+
29+
830

9-
Multithread engine for 3(or 2) stages job.
10-
1131

1232
# Install
1333

1434
`pip install os-3m-engine`
1535

16-
# Usage
36+
# API
37+
38+
* Create a default engine, a typical 3-stage job:
39+
40+
frontend: ``os_3m_engine.ootb.StdinFrontend``, read from stdin, send to transport stage
41+
transport: ``os_3m_engine.ootb.LogTransport``, log data received from fronted, send to backend stage
42+
backend: ``os_3m_engine.ootb.LogBackend``, log data received from transport
43+
44+
```
45+
from os_3m_engine.launcher import create
46+
47+
engine = create()
48+
```
49+
50+
* Create engine with custom defined stage:
51+
52+
```
53+
from os_3m_engine.launcher import create
54+
55+
engine = create(transport_cls='transport_class_path_or_class')
56+
```
57+
58+
* Create engine with custom engine config:
59+
60+
```
61+
from os_3m_engine.launcher import create
62+
63+
config = WhateverOjbectYouWant
64+
config.thread_num = 10
65+
engine = create(transport_cls='your_transport_cls', engine_transport_config=config)
66+
```
67+
68+
* Start the engine:
69+
70+
``start`` will block the current thread until all of the stage threads stopped
71+
72+
```
73+
engine.start()
74+
```
75+
76+
* The regular practice of stopping the engine
77+
78+
```
79+
from signal
80+
from os_3m_engine.launcher import create
81+
82+
engine = create()
83+
84+
def stop(signum, frame):
85+
engine.stop()
86+
87+
signal.signal(signal.SIGINT, stop)
88+
89+
engine.start()
90+
```
91+
92+
* Custom frontend class:
93+
94+
1. inherit from ``os_3m_engine.core.frontend.Frontend``
95+
2. define ``produce`` method as generator
96+
97+
```
98+
from os_3m_engine.core.frontend import Frontend
99+
100+
class CustomFrontend(Frontend):
101+
def produce(self):
102+
yield 'Hello world!'
103+
```
104+
105+
* Custom transport class:
106+
107+
1. inherit from ``os_3m_engine.core.transport.Transport``
108+
2. define ``transport`` method, the only parameter is the data received, the return value will be sent to backend
109+
110+
```
111+
from os_3m_engine.core.transport import Transport
112+
113+
class CustomTransport(Transport):
114+
def transport(self, data):
115+
return data
116+
```
117+
118+
* Custom backend class:
119+
120+
1. inherit from ``os_3m_engine.core.backend.Backend``
121+
2. define ``process`` method, the only parameter is the data received, no need return
122+
123+
```
124+
from os_3m_engine.core.backend import Backend
125+
126+
class CustomBackend(Backend):
127+
def process(self, data):
128+
print(data)
129+
```
130+
131+
* Passing parameters
132+
133+
* create engine with custom config object
134+
* use ``self.config`` to get the config in stage class
135+
136+
```
137+
from os_3m_engine.launcher import create
138+
139+
config = WhateverOjbectYouWant
140+
engine = create(app_config=config)
141+
```
142+
143+
144+
* Custom ``setup``, ``cleanup`` behavior
145+
146+
each stage class can define ``setup``, ``cleanup`` methods, these will be called at each thread start/stop
147+
148+
149+
```
150+
from os_3m_engine.core.backend import Backend
151+
152+
class CustomBackend(Backend):
153+
154+
def setup(self):
155+
print('Setup')
156+
157+
def cleanup(self):
158+
print('Cleanup')
159+
160+
def process(self, data):
161+
print(data)
162+
```
17163
18164
19165

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
six
12
os-config>0.1

src/os_3m_engine/utils.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
import inspect
22
from importlib import import_module
33

4+
import six
5+
46

57
def getatters(obj, attrs):
68
return [getattr(obj, attr) for attr in attrs if hasattr(obj, attr)]
79

810

911
def load_class(class_path, base_class=None, include_base_class=False):
10-
module_path, class_name = class_path.rsplit('.', 1)
11-
_mod = import_module(module_path)
12-
if not hasattr(_mod, class_name):
13-
return None
14-
_cls = getattr(_mod, class_name)
15-
if not inspect.isclass(_cls):
16-
return None
17-
elif base_class is None:
12+
if isinstance(class_path, six.string_types):
13+
module_path, class_name = class_path.rsplit('.', 1)
14+
_mod = import_module(module_path)
15+
if not hasattr(_mod, class_name):
16+
return None
17+
_cls = getattr(_mod, class_name)
18+
if not inspect.isclass(_cls):
19+
return None
20+
elif inspect.isclass(class_path):
21+
_cls = class_path
22+
23+
if base_class is None:
1824
return _cls
1925
elif issubclass(_cls, base_class) and (include_base_class or _cls != base_class):
2026
return _cls

tests/test_engine.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,14 @@ def test_engine_002(config):
3737
engine.stop()
3838
t1.join()
3939
assert engine.stopped()
40+
41+
42+
def test_engine_003(config):
43+
from misc import OneRecordFrontend
44+
45+
engine = create(
46+
frontend_cls=OneRecordFrontend,
47+
transport_cls='misc.ConfirmTransport',
48+
backend_cls='misc.ConfirmBackend',
49+
)
50+
engine.start()

0 commit comments

Comments
 (0)