pipelet 0.0.2__tar.gz → 0.0.3__tar.gz
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.
- {pipelet-0.0.2 → pipelet-0.0.3}/PKG-INFO +30 -10
- {pipelet-0.0.2 → pipelet-0.0.3}/README.md +29 -9
- pipelet-0.0.3/pipelet/decarators/__init__.py +3 -0
- pipelet-0.0.3/pipelet/decarators/logging_decorator.py +65 -0
- pipelet-0.0.3/pipelet/decarators/option_decorator.py +36 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/extract/http/http_extractor.py +0 -4
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/transform/csv/reader.py +0 -2
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/transform/json/reader.py +0 -2
- pipelet-0.0.3/pipelet/etl/transform/unzip/__init__.py +3 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/transform/unzip/base.py +2 -5
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/operations/operations.py +85 -7
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/meta.py +2 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pyproject.toml +1 -1
- pipelet-0.0.2/pipelet/etl/transform/unzip/__init__.py +0 -3
- {pipelet-0.0.2 → pipelet-0.0.3}/LICENSE +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/extract/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/extract/http/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/transform/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/transform/csv/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/etl/transform/json/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/exceptions/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/exceptions/base.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/exceptions/csv_processor.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/exceptions/download_processor.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/exceptions/file_system.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/exceptions/unzip_processor.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/factories/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/factories/exceptions_factory.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/factories/file_system_factory.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/log/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/log/logger.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/operations/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/operations/pipeline.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/abc.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/base.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/chain_processors/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/chain_processors/base.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/file_system/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/file_system/base.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/file_system/file_system_types.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/mixins.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/processor_types.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/retry_processor.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/processors/splitter_processor.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/utils/__init__.py +0 -0
- {pipelet-0.0.2 → pipelet-0.0.3}/pipelet/utils/common.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pipelet
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Daniel Naumowich
|
|
6
6
|
Author-email: cskteam4@gmail.com
|
|
@@ -69,7 +69,7 @@ A processor for parsing CSV files using the `pandas` library. It supports numero
|
|
|
69
69
|
### 7. **JsonParser**
|
|
70
70
|
A processor for parsing JSON data from strings or files into Python dictionaries. It supports configuring how numbers, constants (e.g., NaN), and JSON objects are handled with custom functions.
|
|
71
71
|
|
|
72
|
-
### 8. **
|
|
72
|
+
### 8. **UnzipProcessor**
|
|
73
73
|
A processor for extracting files from `.zip` archives. It supports extracting data into files, managing chunk sizes, and deleting the original archive after processing.
|
|
74
74
|
|
|
75
75
|
### 9. **RetryProcessor**
|
|
@@ -91,7 +91,7 @@ A processor designed to handle transient errors by retrying operations based on
|
|
|
91
91
|
from pipelet.processors.http import HttpDataExtractProcessor
|
|
92
92
|
from pipelet.processors.csv_parser import CsvParser
|
|
93
93
|
from pipelet.processors.json_parser import JsonParser
|
|
94
|
-
from pipelet.processors.unzip import
|
|
94
|
+
from pipelet.processors.unzip import UnzipProcessor
|
|
95
95
|
from pipelet.processors.file_system import AbstractFileSystemManager
|
|
96
96
|
|
|
97
97
|
# Create file system manager
|
|
@@ -101,7 +101,7 @@ file_system_manager = AbstractFileSystemManager()
|
|
|
101
101
|
http_processor = HttpDataExtractProcessor(file_system_manager)
|
|
102
102
|
csv_parser = CsvParser(file_system_manager)
|
|
103
103
|
json_parser = JsonParser(file_system_manager)
|
|
104
|
-
unzip_processor =
|
|
104
|
+
unzip_processor = UnzipProcessor(file_system_manager)
|
|
105
105
|
|
|
106
106
|
# Create processor chain
|
|
107
107
|
http_processor.set_next(csv_parser).set_next(json_parser).set_next(unzip_processor)
|
|
@@ -118,7 +118,7 @@ for output in http_processor.process(input_data):
|
|
|
118
118
|
from pipelet.processors.http import HttpDataExtractProcessor
|
|
119
119
|
from pipelet.processors.csv_parser import CsvParser
|
|
120
120
|
from pipelet.processors.json_parser import JsonParser
|
|
121
|
-
from pipelet.processors.unzip import
|
|
121
|
+
from pipelet.processors.unzip import UnzipProcessor
|
|
122
122
|
from pipelet.processors.file_system import AbstractFileSystemManager
|
|
123
123
|
|
|
124
124
|
# Create file system manager
|
|
@@ -128,7 +128,7 @@ file_system_manager = AbstractFileSystemManager()
|
|
|
128
128
|
http_processor = HttpDataExtractProcessor(file_system_manager)
|
|
129
129
|
csv_parser = CsvParser(file_system_manager)
|
|
130
130
|
json_parser = JsonParser(file_system_manager)
|
|
131
|
-
unzip_processor =
|
|
131
|
+
unzip_processor = UnzipProcessor(file_system_manager)
|
|
132
132
|
|
|
133
133
|
# Create processor chain using `>>`
|
|
134
134
|
pipeline = http_processor >> csv_parser >> json_parser >> unzip_processor
|
|
@@ -147,23 +147,35 @@ from pipelet.processors.file_system import FileModeEnum
|
|
|
147
147
|
|
|
148
148
|
def main():
|
|
149
149
|
# Define the pipeline with parallel processing for parsing and unzipping
|
|
150
|
+
|
|
150
151
|
pipeline = [
|
|
151
152
|
Op(
|
|
152
153
|
BaseOpEnum.downloading,
|
|
153
154
|
kwargs={
|
|
154
|
-
"file_system_manager": {
|
|
155
|
+
"file_system_manager": {
|
|
156
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
157
|
+
"kwargs": {...},
|
|
158
|
+
},
|
|
155
159
|
"retry_args": {
|
|
160
|
+
"retry_processor": ..., # Here can be your custom retry processor
|
|
156
161
|
"max_retries": 3,
|
|
157
162
|
"delay": 1,
|
|
158
163
|
"delay_step": 1,
|
|
159
164
|
"retry_with_white_exc": True,
|
|
160
165
|
},
|
|
166
|
+
"logging_args": {
|
|
167
|
+
"logger": ..., # Here can be your custom logger
|
|
168
|
+
"logging": True, # Logging intup data
|
|
169
|
+
},
|
|
161
170
|
}
|
|
162
171
|
),
|
|
163
172
|
Op(
|
|
164
173
|
BaseOpEnum.upzip,
|
|
165
174
|
kwargs={
|
|
166
|
-
"file_system_manager": {
|
|
175
|
+
"file_system_manager": {
|
|
176
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
177
|
+
"kwargs": {...},
|
|
178
|
+
},
|
|
167
179
|
}
|
|
168
180
|
),
|
|
169
181
|
OpAny(
|
|
@@ -171,13 +183,19 @@ def main():
|
|
|
171
183
|
Op(
|
|
172
184
|
BaseOpEnum.json_parsing,
|
|
173
185
|
kwargs={
|
|
174
|
-
"file_system_manager": {
|
|
186
|
+
"file_system_manager": {
|
|
187
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
188
|
+
"kwargs": {...},
|
|
189
|
+
},
|
|
175
190
|
}
|
|
176
191
|
),
|
|
177
192
|
Op(
|
|
178
193
|
BaseOpEnum.csv_parsing,
|
|
179
194
|
kwargs={
|
|
180
|
-
"file_system_manager": {
|
|
195
|
+
"file_system_manager": {
|
|
196
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
197
|
+
"kwargs": {...},
|
|
198
|
+
},,
|
|
181
199
|
"white_exceptions": [
|
|
182
200
|
TypeError,
|
|
183
201
|
BaseExceptionsEnum.csv_parsing_error,
|
|
@@ -213,8 +231,10 @@ The library is developer-friendly, making it easy to extend and integrate custom
|
|
|
213
231
|
|
|
214
232
|
```python
|
|
215
233
|
from pipelet.processors.base import BaseProcessor
|
|
234
|
+
from pipelet.decorators import logging_decorator
|
|
216
235
|
|
|
217
236
|
class CustomProcessor(BaseProcessor):
|
|
237
|
+
@logging_decorator(custom_logger=...) # If you need this you can use either a custom logger or a default logger
|
|
218
238
|
def process(self, input_data):
|
|
219
239
|
# Custom data processing logic
|
|
220
240
|
yield transformed_data
|
|
@@ -49,7 +49,7 @@ A processor for parsing CSV files using the `pandas` library. It supports numero
|
|
|
49
49
|
### 7. **JsonParser**
|
|
50
50
|
A processor for parsing JSON data from strings or files into Python dictionaries. It supports configuring how numbers, constants (e.g., NaN), and JSON objects are handled with custom functions.
|
|
51
51
|
|
|
52
|
-
### 8. **
|
|
52
|
+
### 8. **UnzipProcessor**
|
|
53
53
|
A processor for extracting files from `.zip` archives. It supports extracting data into files, managing chunk sizes, and deleting the original archive after processing.
|
|
54
54
|
|
|
55
55
|
### 9. **RetryProcessor**
|
|
@@ -71,7 +71,7 @@ A processor designed to handle transient errors by retrying operations based on
|
|
|
71
71
|
from pipelet.processors.http import HttpDataExtractProcessor
|
|
72
72
|
from pipelet.processors.csv_parser import CsvParser
|
|
73
73
|
from pipelet.processors.json_parser import JsonParser
|
|
74
|
-
from pipelet.processors.unzip import
|
|
74
|
+
from pipelet.processors.unzip import UnzipProcessor
|
|
75
75
|
from pipelet.processors.file_system import AbstractFileSystemManager
|
|
76
76
|
|
|
77
77
|
# Create file system manager
|
|
@@ -81,7 +81,7 @@ file_system_manager = AbstractFileSystemManager()
|
|
|
81
81
|
http_processor = HttpDataExtractProcessor(file_system_manager)
|
|
82
82
|
csv_parser = CsvParser(file_system_manager)
|
|
83
83
|
json_parser = JsonParser(file_system_manager)
|
|
84
|
-
unzip_processor =
|
|
84
|
+
unzip_processor = UnzipProcessor(file_system_manager)
|
|
85
85
|
|
|
86
86
|
# Create processor chain
|
|
87
87
|
http_processor.set_next(csv_parser).set_next(json_parser).set_next(unzip_processor)
|
|
@@ -98,7 +98,7 @@ for output in http_processor.process(input_data):
|
|
|
98
98
|
from pipelet.processors.http import HttpDataExtractProcessor
|
|
99
99
|
from pipelet.processors.csv_parser import CsvParser
|
|
100
100
|
from pipelet.processors.json_parser import JsonParser
|
|
101
|
-
from pipelet.processors.unzip import
|
|
101
|
+
from pipelet.processors.unzip import UnzipProcessor
|
|
102
102
|
from pipelet.processors.file_system import AbstractFileSystemManager
|
|
103
103
|
|
|
104
104
|
# Create file system manager
|
|
@@ -108,7 +108,7 @@ file_system_manager = AbstractFileSystemManager()
|
|
|
108
108
|
http_processor = HttpDataExtractProcessor(file_system_manager)
|
|
109
109
|
csv_parser = CsvParser(file_system_manager)
|
|
110
110
|
json_parser = JsonParser(file_system_manager)
|
|
111
|
-
unzip_processor =
|
|
111
|
+
unzip_processor = UnzipProcessor(file_system_manager)
|
|
112
112
|
|
|
113
113
|
# Create processor chain using `>>`
|
|
114
114
|
pipeline = http_processor >> csv_parser >> json_parser >> unzip_processor
|
|
@@ -127,23 +127,35 @@ from pipelet.processors.file_system import FileModeEnum
|
|
|
127
127
|
|
|
128
128
|
def main():
|
|
129
129
|
# Define the pipeline with parallel processing for parsing and unzipping
|
|
130
|
+
|
|
130
131
|
pipeline = [
|
|
131
132
|
Op(
|
|
132
133
|
BaseOpEnum.downloading,
|
|
133
134
|
kwargs={
|
|
134
|
-
"file_system_manager": {
|
|
135
|
+
"file_system_manager": {
|
|
136
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
137
|
+
"kwargs": {...},
|
|
138
|
+
},
|
|
135
139
|
"retry_args": {
|
|
140
|
+
"retry_processor": ..., # Here can be your custom retry processor
|
|
136
141
|
"max_retries": 3,
|
|
137
142
|
"delay": 1,
|
|
138
143
|
"delay_step": 1,
|
|
139
144
|
"retry_with_white_exc": True,
|
|
140
145
|
},
|
|
146
|
+
"logging_args": {
|
|
147
|
+
"logger": ..., # Here can be your custom logger
|
|
148
|
+
"logging": True, # Logging intup data
|
|
149
|
+
},
|
|
141
150
|
}
|
|
142
151
|
),
|
|
143
152
|
Op(
|
|
144
153
|
BaseOpEnum.upzip,
|
|
145
154
|
kwargs={
|
|
146
|
-
"file_system_manager": {
|
|
155
|
+
"file_system_manager": {
|
|
156
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
157
|
+
"kwargs": {...},
|
|
158
|
+
},
|
|
147
159
|
}
|
|
148
160
|
),
|
|
149
161
|
OpAny(
|
|
@@ -151,13 +163,19 @@ def main():
|
|
|
151
163
|
Op(
|
|
152
164
|
BaseOpEnum.json_parsing,
|
|
153
165
|
kwargs={
|
|
154
|
-
"file_system_manager": {
|
|
166
|
+
"file_system_manager": {
|
|
167
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
168
|
+
"kwargs": {...},
|
|
169
|
+
},
|
|
155
170
|
}
|
|
156
171
|
),
|
|
157
172
|
Op(
|
|
158
173
|
BaseOpEnum.csv_parsing,
|
|
159
174
|
kwargs={
|
|
160
|
-
"file_system_manager": {
|
|
175
|
+
"file_system_manager": {
|
|
176
|
+
"path": ..., # Here can be your custom file system manager (as an example S3)
|
|
177
|
+
"kwargs": {...},
|
|
178
|
+
},,
|
|
161
179
|
"white_exceptions": [
|
|
162
180
|
TypeError,
|
|
163
181
|
BaseExceptionsEnum.csv_parsing_error,
|
|
@@ -193,8 +211,10 @@ The library is developer-friendly, making it easy to extend and integrate custom
|
|
|
193
211
|
|
|
194
212
|
```python
|
|
195
213
|
from pipelet.processors.base import BaseProcessor
|
|
214
|
+
from pipelet.decorators import logging_decorator
|
|
196
215
|
|
|
197
216
|
class CustomProcessor(BaseProcessor):
|
|
217
|
+
@logging_decorator(custom_logger=...) # If you need this you can use either a custom logger or a default logger
|
|
198
218
|
def process(self, input_data):
|
|
199
219
|
# Custom data processing logic
|
|
200
220
|
yield transformed_data
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Any, Callable, Generator, Optional, Union
|
|
3
|
+
|
|
4
|
+
from pipelet.decarators.option_decorator import prepare_gen_processor
|
|
5
|
+
from pipelet.log import logger_factory
|
|
6
|
+
from pipelet.processors.base import BaseProcessor
|
|
7
|
+
|
|
8
|
+
default_logger = logger_factory()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def logging_decorator(
|
|
12
|
+
func: Callable[
|
|
13
|
+
...,
|
|
14
|
+
Generator[Any, Any, Optional[Any]],
|
|
15
|
+
],
|
|
16
|
+
custom_logger: Optional[Any] = None,
|
|
17
|
+
) -> Callable[
|
|
18
|
+
...,
|
|
19
|
+
Generator[Any, Any, Optional[Any]],
|
|
20
|
+
]:
|
|
21
|
+
"""
|
|
22
|
+
A decorator that adds logging functionality to a processor's `process` method.
|
|
23
|
+
|
|
24
|
+
Logs the processor's class name, arguments, and keyword arguments each time
|
|
25
|
+
the `process` method is invoked. Supports generator functions and normalizes
|
|
26
|
+
calls to ensure compatibility with bound and unbound methods.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
func (Callable): The generator function (`process` method) to wrap.
|
|
30
|
+
custom_logger (Optional[Any]): Custom logger to use. Defaults to the global logger.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Callable: A wrapped generator function with logging functionality.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
```
|
|
37
|
+
@logging_decorator
|
|
38
|
+
def process(self, *args, **kwargs):
|
|
39
|
+
yield ...
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
logger_to_use = (
|
|
43
|
+
custom_logger or default_logger
|
|
44
|
+
) # Используем кастомный логгер, если он передан
|
|
45
|
+
|
|
46
|
+
@wraps(func)
|
|
47
|
+
def wrapper(
|
|
48
|
+
obj: BaseProcessor[Any, Any, Any, Any],
|
|
49
|
+
*args,
|
|
50
|
+
**kwargs,
|
|
51
|
+
) -> Generator[
|
|
52
|
+
Any,
|
|
53
|
+
Any,
|
|
54
|
+
Union[Any, None],
|
|
55
|
+
]:
|
|
56
|
+
logger_to_use.info(
|
|
57
|
+
f"Processor: {obj.__class__.__name__}, args: {args}, kwargs: {kwargs}"
|
|
58
|
+
)
|
|
59
|
+
generator = prepare_gen_processor(
|
|
60
|
+
func, obj, *args, **kwargs
|
|
61
|
+
) # Normalized call
|
|
62
|
+
while True:
|
|
63
|
+
yield next(generator)
|
|
64
|
+
|
|
65
|
+
return wrapper
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import types
|
|
2
|
+
from typing import Any, Callable, Generator
|
|
3
|
+
|
|
4
|
+
from pipelet.log import logger_factory
|
|
5
|
+
from pipelet.processors.base import BaseProcessor
|
|
6
|
+
|
|
7
|
+
logger = logger_factory()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def prepare_gen_processor(
|
|
11
|
+
func: Callable[..., Generator[Any, Any, Any]],
|
|
12
|
+
obj: BaseProcessor[Any, Any, Any, Any],
|
|
13
|
+
*args,
|
|
14
|
+
**kwargs
|
|
15
|
+
) -> Generator[Any, Any, Any]:
|
|
16
|
+
"""
|
|
17
|
+
A utility function to prepare and execute generator-based processors.
|
|
18
|
+
|
|
19
|
+
This function normalizes the invocation of generator functions or methods.
|
|
20
|
+
If the provided callable `func` is a bound method, it is invoked directly
|
|
21
|
+
with `*args` and `**kwargs`. If `func` is an unbound function, it is
|
|
22
|
+
invoked with `obj` passed as the first argument along with `*args` and `**kwargs`.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
func (Callable): The generator function or method to be executed.
|
|
26
|
+
obj (BaseProcessor): The processor object, passed explicitly for unbound functions.
|
|
27
|
+
*args: Positional arguments to pass to the generator.
|
|
28
|
+
**kwargs: Keyword arguments to pass to the generator.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Generator: The generator object produced by executing `func`.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
if isinstance(func, types.MethodType):
|
|
35
|
+
return func(*args, **kwargs)
|
|
36
|
+
return func(obj, *args, **kwargs)
|
|
@@ -102,7 +102,6 @@ class HttpDataExtractProcessor(
|
|
|
102
102
|
Yields:
|
|
103
103
|
PathLike | str: The file name or path of the downloaded file.
|
|
104
104
|
"""
|
|
105
|
-
logger.debug(f"Starting download from {input_data}")
|
|
106
105
|
resp = httpx.get(
|
|
107
106
|
input_data,
|
|
108
107
|
params=self._params,
|
|
@@ -125,7 +124,6 @@ class HttpDataExtractProcessor(
|
|
|
125
124
|
resp.content,
|
|
126
125
|
)
|
|
127
126
|
|
|
128
|
-
logger.debug("Download was successful")
|
|
129
127
|
if self._next is not None:
|
|
130
128
|
yield from self._next.process(file_name)
|
|
131
129
|
else:
|
|
@@ -218,7 +216,6 @@ class HttpxStreamDownloadProcessor(
|
|
|
218
216
|
Yields:
|
|
219
217
|
PathLike[str] | str: The file path of the downloaded file.
|
|
220
218
|
"""
|
|
221
|
-
logger.debug(f"Starting download from {input_data}")
|
|
222
219
|
with httpx.stream(
|
|
223
220
|
method=self._method,
|
|
224
221
|
url=input_data,
|
|
@@ -238,7 +235,6 @@ class HttpxStreamDownloadProcessor(
|
|
|
238
235
|
if chunk:
|
|
239
236
|
self._file_system_manager.append_to_file(file_path, chunk)
|
|
240
237
|
|
|
241
|
-
logger.debug(f"Download was successful: {file_path}")
|
|
242
238
|
if self._next is not None:
|
|
243
239
|
yield from self._next.process(file_path)
|
|
244
240
|
else:
|
|
@@ -227,7 +227,6 @@ class CsvParser(
|
|
|
227
227
|
Raises:
|
|
228
228
|
CsvParsingError: If parsing fails.
|
|
229
229
|
"""
|
|
230
|
-
logger.debug(f"Start parsing: {input_data}")
|
|
231
230
|
suffix = Path(input_data).suffix
|
|
232
231
|
if suffix != ".csv":
|
|
233
232
|
raise CsvParsingError(
|
|
@@ -292,7 +291,6 @@ class CsvParser(
|
|
|
292
291
|
yield records
|
|
293
292
|
if self._auto_delete:
|
|
294
293
|
self._file_system_manager.delete_file(input_data)
|
|
295
|
-
logger.debug(f"Successful parsing: {input_data}")
|
|
296
294
|
|
|
297
295
|
def __str__(self) -> str:
|
|
298
296
|
return (
|
|
@@ -90,12 +90,10 @@ class JsonParser(
|
|
|
90
90
|
"""
|
|
91
91
|
# Read content using the file system manager
|
|
92
92
|
content = self._file_system_manager.read_file(input_data)
|
|
93
|
-
logger.debug("Read content from %s", input_data)
|
|
94
93
|
|
|
95
94
|
# Parse the JSON content with custom arguments
|
|
96
95
|
try:
|
|
97
96
|
data = json.loads(content, **self._parse_kwargs)
|
|
98
|
-
logger.debug(f"JSON {input_data} has parsed successfully")
|
|
99
97
|
except json.JSONDecodeError as e:
|
|
100
98
|
logger.error("Failed to parse JSON: %s", e)
|
|
101
99
|
raise
|
|
@@ -15,7 +15,7 @@ from pipelet.processors.file_system import (
|
|
|
15
15
|
logger = logger_factory()
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class UnzipProcessor(
|
|
19
19
|
BaseProcessor[
|
|
20
20
|
Union[str, PathLike[str]], Union[str, PathLike[str]], None, None
|
|
21
21
|
],
|
|
@@ -45,7 +45,7 @@ class BaseUnzipProcessor(
|
|
|
45
45
|
batch_size: Optional[int] = None,
|
|
46
46
|
) -> None:
|
|
47
47
|
"""
|
|
48
|
-
Initializes the
|
|
48
|
+
Initializes the UnzipProcessor.
|
|
49
49
|
|
|
50
50
|
Args:
|
|
51
51
|
file_system_manager (AbstractFileSystemManager): The file system manager for file operations.
|
|
@@ -84,7 +84,6 @@ class BaseUnzipProcessor(
|
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
file_path = self._file_system_manager.get_path(input_data)
|
|
87
|
-
logger.debug(f"Starting to unzip {input_data}")
|
|
88
87
|
|
|
89
88
|
unzip_files: List[Union[str, PathLike[str]]] = []
|
|
90
89
|
with zipfile.ZipFile(file_path, FileModeEnum.READ) as zip_ref:
|
|
@@ -107,8 +106,6 @@ class BaseUnzipProcessor(
|
|
|
107
106
|
|
|
108
107
|
unzip_files.append(extracted_file_path)
|
|
109
108
|
|
|
110
|
-
logger.debug("Unzipping was successful")
|
|
111
|
-
|
|
112
109
|
if self._next is not None:
|
|
113
110
|
for item in unzip_files:
|
|
114
111
|
gen = self._next.process(item)
|
|
@@ -24,7 +24,7 @@ Classes and Functions:
|
|
|
24
24
|
- `BaseOpEnum`: Enum defining paths to processor classes for common operations.
|
|
25
25
|
- `Op`: Represents a single operation configuration.
|
|
26
26
|
- `OpAny`, `OpAll`: Define composite operations requiring any or all conditions.
|
|
27
|
-
- `
|
|
27
|
+
- `prepare_retry_option`: Wraps a option with retry logic.
|
|
28
28
|
- `process_op`, `process_op_composite`: Handle individual and composite operation processing.
|
|
29
29
|
- `get_processor_for_operation`: Dispatch function for retrieving the appropriate processor.
|
|
30
30
|
|
|
@@ -37,10 +37,12 @@ This framework enables modular, reusable, and extensible ETL workflows by abstra
|
|
|
37
37
|
processing logic into well-defined processors and operations.
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
|
+
import types
|
|
40
41
|
from dataclasses import dataclass, field
|
|
41
42
|
from enum import Enum
|
|
42
43
|
from typing import Any, Callable, Dict, Generic, List, Type, TypeVar, Union
|
|
43
44
|
|
|
45
|
+
from pipelet.decarators.logging_decorator import logging_decorator
|
|
44
46
|
from pipelet.factories import KEYWORD_FACTORY_MAP
|
|
45
47
|
from pipelet.log import logger_factory
|
|
46
48
|
from pipelet.processors import get_processor
|
|
@@ -59,11 +61,12 @@ class BaseOpEnum(str, Enum):
|
|
|
59
61
|
|
|
60
62
|
downloading = "pipelet.etl.extract.http.HttpDataExtractProcessor"
|
|
61
63
|
stream_downloading = "pipelet.etl.extract.http.HttpxStreamDownloadProcessor"
|
|
62
|
-
unzip = "pipelet.etl.transform.unzip.
|
|
64
|
+
unzip = "pipelet.etl.transform.unzip.UnzipProcessor"
|
|
63
65
|
csv_parsing = "pipelet.etl.transform.csv.CsvParser"
|
|
64
66
|
json_parsing = "pipelet.etl.transform.json.JsonParser"
|
|
65
67
|
retrying = "pipelet.processors.retry_processor.RetryProcessor"
|
|
66
68
|
splitting = "pipelet.processors.splitter_processor.SplitterProcessor"
|
|
69
|
+
logging = "pipelet.processors.logging_processor.LoggingProcessor"
|
|
67
70
|
|
|
68
71
|
|
|
69
72
|
T = TypeVar("T", bound="BaseOpComposite")
|
|
@@ -75,7 +78,7 @@ class Op:
|
|
|
75
78
|
Represents an operation configuration.
|
|
76
79
|
|
|
77
80
|
Attributes:
|
|
78
|
-
operation (str): Path to the processor class in "module.
|
|
81
|
+
operation (str): Path to the processor class in "module.Classmeta_option_decoratorName" format.
|
|
79
82
|
"""
|
|
80
83
|
|
|
81
84
|
operation: str
|
|
@@ -115,7 +118,7 @@ class OpAll(BaseOpComposite[Union["OpAny", "OpAll"]]):
|
|
|
115
118
|
"""
|
|
116
119
|
|
|
117
120
|
|
|
118
|
-
def
|
|
121
|
+
def prepare_retry_option(
|
|
119
122
|
processor: BaseProcessor[Any, Any, Any, Any], kwargs: Dict[str, Any]
|
|
120
123
|
) -> BaseProcessor[Any, Any, Any, Any]:
|
|
121
124
|
"""
|
|
@@ -149,6 +152,79 @@ def prepare_retry_processor(
|
|
|
149
152
|
return processor
|
|
150
153
|
|
|
151
154
|
|
|
155
|
+
def prepare_logging_option(
|
|
156
|
+
processor: BaseProcessor[Any, Any, Any, Any], kwargs: Dict[str, Any]
|
|
157
|
+
) -> BaseProcessor[Any, Any, Any, Any]:
|
|
158
|
+
"""
|
|
159
|
+
Prepares and applies logging functionality to the processor.
|
|
160
|
+
|
|
161
|
+
This function checks for a `logging` flag in the provided `kwargs`. If `logging` is enabled,
|
|
162
|
+
it wraps the `process` method of the processor with the `logging_decorator`.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
processor (BaseProcessor): The processor instance to which logging functionality will be added.
|
|
166
|
+
kwargs (Dict[str, Any]): A dictionary of arguments, including the `logging` flag.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
BaseProcessor: The processor with logging functionality applied if enabled.
|
|
170
|
+
"""
|
|
171
|
+
logging = kwargs.pop("logging", False)
|
|
172
|
+
logger = kwargs.pop("logger", None)
|
|
173
|
+
if logging:
|
|
174
|
+
processor.process = types.MethodType(
|
|
175
|
+
logging_decorator(processor.process, logger), processor
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return processor
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
OPTION_TO_HANDLER_MAP: Dict[
|
|
182
|
+
str,
|
|
183
|
+
Callable[
|
|
184
|
+
[BaseProcessor[Any, Any, Any, Any], Dict[str, Any]],
|
|
185
|
+
BaseProcessor[Any, Any, Any, Any],
|
|
186
|
+
],
|
|
187
|
+
] = {
|
|
188
|
+
"logging_args": prepare_logging_option,
|
|
189
|
+
"retry_args": prepare_retry_option,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def prepare_additional_options(
|
|
194
|
+
processor: BaseProcessor[Any, Any, Any, Any],
|
|
195
|
+
kwargs: Dict[str, Dict[str, Any]],
|
|
196
|
+
) -> BaseProcessor[Any, Any, Any, Any]:
|
|
197
|
+
"""
|
|
198
|
+
Prepares and applies additional options to the processor.
|
|
199
|
+
|
|
200
|
+
This function processes a dictionary of option names and their associated arguments,
|
|
201
|
+
applying each option to the processor using a corresponding handler function from
|
|
202
|
+
the `OPTION_TO_HANDLER_MAP`.
|
|
203
|
+
|
|
204
|
+
Supported options:
|
|
205
|
+
- `logging_args`: Configures logging functionality.
|
|
206
|
+
- `retry_args`: Configures retry logic.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
processor (BaseProcessor): The processor instance to which additional options will be applied.
|
|
210
|
+
kwargs (Dict[str, Dict[str, Any]]): A dictionary where keys are option names
|
|
211
|
+
and values are their respective argument dictionaries.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
BaseProcessor: The processor with the additional options applied.
|
|
215
|
+
|
|
216
|
+
Raises:
|
|
217
|
+
KeyError: If an unsupported option name is provided in `kwargs`.
|
|
218
|
+
"""
|
|
219
|
+
if kwargs:
|
|
220
|
+
for option_name, option_kwargs in kwargs.items():
|
|
221
|
+
option_handler = OPTION_TO_HANDLER_MAP.get(option_name, None)
|
|
222
|
+
if option_handler is None:
|
|
223
|
+
raise KeyError(f"Option {option_name} isn't supported")
|
|
224
|
+
processor = option_handler(processor, option_kwargs)
|
|
225
|
+
return processor
|
|
226
|
+
|
|
227
|
+
|
|
152
228
|
def process_op(op: Op) -> BaseProcessor[Any, Any, Any, Any]:
|
|
153
229
|
"""
|
|
154
230
|
Processes an Op and returns an instance of BaseProcessor.
|
|
@@ -164,15 +240,17 @@ def process_op(op: Op) -> BaseProcessor[Any, Any, Any, Any]:
|
|
|
164
240
|
"""
|
|
165
241
|
type_processor = get_processor(op.operation)
|
|
166
242
|
kwargs: Dict[str, Any] = {}
|
|
243
|
+
kwargs_to_additional_processors: Dict[str, Any] = {}
|
|
167
244
|
for k, v in op.kwargs.items():
|
|
168
245
|
factory = KEYWORD_FACTORY_MAP.get(k, None)
|
|
169
246
|
if factory:
|
|
170
247
|
kwargs[k] = factory(**v) if isinstance(v, dict) else factory(v)
|
|
171
248
|
else:
|
|
172
|
-
|
|
173
|
-
retry_args: Dict[str, Any] = kwargs.pop("retry_args", {})
|
|
249
|
+
kwargs_to_additional_processors[k] = v
|
|
174
250
|
processor = type_processor(**kwargs)
|
|
175
|
-
processor =
|
|
251
|
+
processor = prepare_additional_options(
|
|
252
|
+
processor, kwargs_to_additional_processors
|
|
253
|
+
)
|
|
176
254
|
return processor
|
|
177
255
|
|
|
178
256
|
|
|
@@ -21,6 +21,7 @@ Key Features:
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
from abc import ABCMeta
|
|
24
|
+
from functools import wraps
|
|
24
25
|
from typing import Any, Callable, Generator, List, Optional, Type, Union
|
|
25
26
|
|
|
26
27
|
from pipelet.exceptions.base import (
|
|
@@ -68,6 +69,7 @@ class ProcessorMeta(ABCMeta):
|
|
|
68
69
|
Callable: A wrapped generator function with added exception handling.
|
|
69
70
|
"""
|
|
70
71
|
|
|
72
|
+
@wraps(func)
|
|
71
73
|
def wrapper(
|
|
72
74
|
obj: AbstractProcessor,
|
|
73
75
|
*args,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|