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
The logic_processes_layer package provides a framework for structuring the logic of your Python programs in a flexible and maintainable way. It allows you to divide your program logic into separate processes, each of which can be easily modified or replaced without affecting the others.
3
+
The `logic_processes_layer` package provides a framework for structuring the logic of your Python programs in a flexible and maintainable way. It allows you to divide your program logic into separate processes, each of which can be easily modified or replaced without affecting the others.
5
4
6
5
## Features
7
6
8
7
-**Separation of concerns**: Each process in your program can be developed and tested independently.
9
-
10
8
-**Flexibility**: Processes can be easily added, removed, or modified without affecting the rest of your program.
11
-
12
9
-**Ease of testing**: By isolating each process, you can write more focused and effective unit tests.
13
10
14
11
## Installation
15
12
16
-
You can install the logic_processes_layer package via pip:
13
+
You can install the `logic_processes_layer` package via pip:
14
+
15
+
```bash
17
16
pip install logic-processes-layer
17
+
```
18
18
19
19
## New Features
20
20
21
21
-**ProcessAsSubprocess**: Use any process as a subprocess.
22
-
-**InitMapper**: Simplifies process initialization with attribute mapping from the context.
22
+
-**InitMapper**: Simplifies process initialization by mapping attributes from the context to processor arguments.
23
23
-**ProcessAttr**: Retrieve attributes from the process context or directly from the process.
24
24
-**Conditions Support**: Add logical conditions to control the execution of processes.
25
25
-**AttrCondition**: Define conditions based on attributes of the process or context.
26
26
-**FunctionCondition**: Wrap custom functions as conditions.
27
27
-**Logical Operators**: Combine conditions with `&` (AND), `|` (OR), `~` (NOT), and `^` (XOR) for advanced logic.
28
-
-[Examples](tests/examples) of how to use the logic_processes_layer package.
28
+
-[Examples](tests/examples) of how to use the `logic_processes_layer` package.
29
+
30
+
### Using `ProcessAttr` and `InitMapper`
31
+
32
+
In many cases, you may want to initialize your process with specific values drawn from the current context or from the process itself. To simplify this, the package provides two utilities: `ProcessAttr` and `InitMapper`.
33
+
34
+
#### `ProcessAttr`
35
+
36
+
`ProcessAttr` makes it easy to fetch required attributes from the context or from the process. This class can be declared as generic (i.e., `Generic[AttrResultT]`) to enable strict typing if needed:
-**`from_context`**: A flag indicating whether to take the attribute from `context.process` (default) or directly from `context`.
64
+
-**`cast`**: A callable to cast (transform) the retrieved value into a desired type (default simply returns the original value).
65
+
66
+
#### `InitMapper`
67
+
68
+
`InitMapper` helps you gather the needed `args` and `kwargs` for initializing a process or any other object. If certain values should come from the context, you can use `ProcessAttr` objects for those parameters.
-**`_init_attrs`**: A tuple of positional arguments to be passed to the constructor.
109
+
-**`_init_kwargs`**: A dictionary of keyword arguments (key is the argument name, value is either a fixed value or a `ProcessAttr` to load from the context).
110
+
111
+
##### Usage Example
112
+
113
+
```python
114
+
# Suppose we have a class MyProcessor that needs two arguments: name (str) and age (int).
115
+
116
+
classMyProcessor:
117
+
def__init__(self, name: str, age: int) -> None:
118
+
self.name = name
119
+
self.age = age
120
+
121
+
defrun(self):
122
+
print(f"Name: {self.name}, Age: {self.age}")
123
+
124
+
# Assume the required values are stored in context.process, for example:
# When mapper(context) is called, it will build (args, kwargs),
136
+
# where args is an empty tuple and kwargs is {"name": "Alice", "age": 30}.
137
+
138
+
args, kwargs = mapper(context)
139
+
processor = MyProcessor(*args, **kwargs)
140
+
processor.run() # Prints: Name: Alice, Age: 30
141
+
```
142
+
143
+
In this way, `InitMapper` provides a flexible mechanism for assembling parameters for object initialization, while `ProcessAttr` enables you to reference attributes from either the context or the process.
144
+
145
+
---
30
146
31
147
## Basic Usage
32
148
33
-
Here is a basic example of how to use the logic_processes_layer package:
149
+
Below is a basic example of how to use the `logic_processes_layer` package, creating a process with pre- and post-run steps:
34
150
35
151
```python
36
152
import dataclasses
@@ -61,84 +177,56 @@ process = MyClass()
61
177
process()
62
178
```
63
179
64
-
In this example, `MyClass` is a processor that has a pre-run step, a run step, and a post-run step. The pre-run step is performed by `MyPreProcess`, which saves a message in the context. The run step is defined in `MyClass` itself. The post-run step is performed by `MyPostProcess`, which retrieves the message from the context and prints it.
65
-
66
180
## Advanced Example: Processing Data from Multiple APIs
67
181
68
-
This example demonstrates how to use the logic_processes_layer package to process data from multiple APIs. The process is divided into three steps: pre-run, run, and post-run.
182
+
This example demonstrates how to use the `logic_processes_layer` package to process data from multiple APIs. The process is divided into three steps: pre-run, run, and post-run.
69
183
70
184
### Pre-run
71
185
72
-
In the pre-run step, we have two subprocesses, each making a GET request to a different API. The responses from these requests are stored in `self.results.pre_run`, and will be used in the main run step.
73
-
74
186
```python
75
187
classPreProcess1(BaseSubprocessor):
76
-
77
188
def__call__(self):
78
-
# Assuming that we are making a GET request to the first API
189
+
# Assuming a GET request to the first API
79
190
response1 = requests.get('http://api1.com')
80
-
# Return the response
81
191
return response1.json()
82
192
83
193
classPreProcess2(BaseSubprocessor):
84
-
85
194
def__call__(self):
86
-
# Assuming that we are making a GET request to the second API
195
+
# Assuming a GET request to the second API
87
196
response2 = requests.get('http://api2.com')
88
-
# Return the response
89
197
return response2.json()
90
198
```
91
199
92
200
### Run
93
201
94
-
In the run step, we process the data from the pre-run step. The results from the two pre-run subprocesses are retrieved from `self.results.pre_run`, and we assume that these results are combined in some way by a hypothetical function `process_data`.
95
-
96
202
```python
97
203
defrun(self):
98
-
#Process the data from the pre_run step
204
+
#Retrieve and process the data from the pre_run step
# Assume that we are combining the data from the two APIs in some way
102
-
result = process_data(api1_response, api2_response) # process_data is a hypothetical function
103
-
# Return the result
207
+
result = process_data(api1_response, api2_response) # process_data is hypothetical
104
208
return result
105
209
```
106
210
107
211
### Post-run
108
212
109
-
In the post-run step, we have a subprocess that sends the result from the run step to another API. The response from this POST request is stored in `self.results.post_run`.
Finally, we create an instance of our class and call it to execute the process:
123
-
124
-
```python
125
-
process = MyClass()
126
-
process()
127
-
```
128
-
129
-
Please note that you need to replace `'http://api1.com'`, `'http://api2.com'`, and `'http://api3.com'` with the actual API URLs, and implement the `process_data` function that processes the data from the first two APIs.
130
-
131
222
## Advanced Example: ChainPipeline with Custom Mapper and Steps
132
223
133
-
In this example, we delve deeper into the use of `logic_processes_layer` and demonstrate how `ChainPipeline`, `AbstractMapper`, and `AbstractPipelineStep` can be used to create more complex processes.
134
-
135
-
1.**Processors**: We have three processors, `ProcessorOne`, `ProcessorTwo`, and `ProcessorThree`. Each of these processors performs a certain task and returns a result.
224
+
In this example, we dive deeper into how `ChainPipeline`, `AbstractMapper`, and `AbstractPipelineStep` can be used to create more complex processes:
136
225
137
-
2.**Mappers**: We then have three mappers, `MapperOne`, `MapperTwo`, and `MapperThree`. These mappers are used to build attribute dictionaries that are passed into the processors.
138
-
139
-
3.**Steps**: We then have three steps, `StepOne`, `StepTwo`, and `StepThree`. Each of these steps uses one of the processors and one of the mappers.
140
-
141
-
4.**ChainPipeline**: Finally, we have a `ChainPipeline` that combines all three steps into one sequence.
226
+
1.**Processors**: Three processors (`ProcessorOne`, `ProcessorTwo`, `ProcessorThree`), each performing a certain task and returning a result.
227
+
2.**Mappers**: Three mappers (`MapperOne`, `MapperTwo`, `MapperThree`) to build attribute dictionaries to be passed into the processors.
228
+
3.**Steps**: Three steps (`StepOne`, `StepTwo`, `StepThree`), each using one of the processors and one of the mappers.
229
+
4.**ChainPipeline**: A `ChainPipeline` that combines all three steps into a sequence.
142
230
143
231
```python
144
232
import dataclasses
@@ -180,16 +268,16 @@ class MapperOne(AbstractMapper):
180
268
classMapperTwo(AbstractMapper):
181
269
defbuild_attrs_strategy(self, prev_results):
182
270
return AttrsData(
183
-
args=self.start_attrs.args,
184
-
kwargs={"data_for_init_two": prev_results["one"]}
271
+
args=self.start_attrs.args,
272
+
kwargs={"data_for_init_two": prev_results["one"]}
185
273
)
186
274
187
275
188
276
classMapperThree(AbstractMapper):
189
277
defbuild_attrs_strategy(self, prev_results):
190
278
return AttrsData(
191
-
args=tuple(),
192
-
kwargs={}
279
+
args=tuple(),
280
+
kwargs={}
193
281
)
194
282
195
283
@@ -216,8 +304,4 @@ class ChainPipeline(AbstractChainPipeline):
216
304
pipeline = ChainPipeline()
217
305
result = pipeline()
218
306
print(result)
219
-
220
307
```
221
-
In this example, we create an instance of `ChainPipeline` and call it to execute the whole process.
222
-
The result of each step is passed to the next step via the mapper,
223
-
allowing each step to use the result of the previous step when building its attributes.
0 commit comments