Skip to content

Commit 02448cc

Browse files
authored
Merge pull request #2 from cfhamlet/develop
First Release
2 parents 442b972 + 51de593 commit 02448cc

File tree

5 files changed

+177
-11
lines changed

5 files changed

+177
-11
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ deploy:
2121
provider: pypi
2222
user: cfhamlet
2323
password:
24+
secure: eL8ptaVhcvilnSvCFlETjDlBFUL0ecbLjN8BWGwRBkjDcYqHPOze/E9sff0sdDBjiyiQbKu7MD554xAfSn8FsqlQLuQfVqT8RbgSKs2vdmwjOHwCENlINY4fmk+HCtvjWnK8r2hxWB87pxQOZ6Uqb4tYizRpQtOFxNEO9QlfjRRz/M4M6fjzO7BWybEUlxnPQn9IHXP+eGoSdHVg63d4EBmh7CcNCR4/lG7OSo89fJ0IjgWgDHv2P5vHOGeiZW7gc1jiHPeppc3vtCUfwLZ2D6bAps65zv/8jpgnzIiKz+XPtzzkcxMG9ZydN1vLMRFKHp+w2uPWXFn3BHxQ0ImQ5uW5hbyT9I7LehtWkdkEZhiyKTTfCUltns8lsSVG+w74SAHPUHyVj09msWvYioHx5gVKqSKzteEek/dZGxTBi6S1ifaMs4mWyqs2wCvcMkPGlLVCdGuO0soYqxXj4HmMQ0Fq9l4Bo4tRXZNjcshNO8Dje3MSTP6eO2XCnOf547D5/X3cZ6MqBPjZnqv6bg/qZFrxathJEXfqcHseWPUM4eTBEGu/69QRSEE72cx0Gmzo9HW18RQE1O/7+/Gn/rzKkGj7S/JrR3fYZVUj4AQnxroq2qgBYsW7R348jJHDq0EVRwzvRrVpUDrzIOHpdlJRBiZ6mtEObBRdrYlWRhFbuBM=
2425
true:
2526
tags: true
2627
condition: ${TOXENV} == py27

README.md

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,162 @@
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 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+
- inherit from ``os_3m_engine.core.frontend.Frontend``
95+
- 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+
- inherit from ``os_3m_engine.core.transport.Transport``
108+
- 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+
- inherit from ``os_3m_engine.core.backend.Backend``
121+
- 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
147+
- these will be called at each thread start/stop
148+
149+
150+
```
151+
from os_3m_engine.core.backend import Backend
152+
153+
class CustomBackend(Backend):
154+
155+
def setup(self):
156+
print('Setup')
157+
158+
def cleanup(self):
159+
print('Cleanup')
160+
161+
def process(self, data):
162+
print(data)
163+
```
17164
18165
19166

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)