etlplus 0.14.3__py3-none-any.whl → 0.16.0__py3-none-any.whl
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.
- etlplus/README.md +4 -4
- etlplus/api/README.md +33 -2
- etlplus/api/auth.py +1 -1
- etlplus/api/config.py +5 -10
- etlplus/api/endpoint_client.py +4 -4
- etlplus/api/pagination/config.py +1 -1
- etlplus/api/pagination/paginator.py +6 -7
- etlplus/api/rate_limiting/config.py +4 -4
- etlplus/api/rate_limiting/rate_limiter.py +1 -1
- etlplus/api/retry_manager.py +2 -2
- etlplus/api/transport.py +1 -1
- etlplus/api/types.py +99 -0
- etlplus/api/utils.py +6 -2
- etlplus/cli/README.md +2 -2
- etlplus/cli/commands.py +75 -42
- etlplus/cli/constants.py +1 -1
- etlplus/cli/handlers.py +33 -15
- etlplus/cli/io.py +2 -2
- etlplus/cli/main.py +2 -2
- etlplus/cli/state.py +4 -7
- etlplus/connector/__init__.py +43 -0
- etlplus/connector/api.py +161 -0
- etlplus/connector/connector.py +26 -0
- etlplus/connector/core.py +132 -0
- etlplus/connector/database.py +122 -0
- etlplus/connector/enums.py +52 -0
- etlplus/connector/file.py +120 -0
- etlplus/connector/types.py +40 -0
- etlplus/connector/utils.py +122 -0
- etlplus/database/README.md +2 -2
- etlplus/database/ddl.py +2 -2
- etlplus/database/engine.py +19 -3
- etlplus/database/orm.py +2 -0
- etlplus/enums.py +1 -33
- etlplus/file/README.md +2 -2
- etlplus/file/_imports.py +1 -0
- etlplus/file/_io.py +52 -4
- etlplus/file/accdb.py +3 -2
- etlplus/file/arrow.py +3 -2
- etlplus/file/avro.py +3 -2
- etlplus/file/bson.py +3 -2
- etlplus/file/cbor.py +3 -2
- etlplus/file/cfg.py +3 -2
- etlplus/file/conf.py +3 -2
- etlplus/file/core.py +11 -8
- etlplus/file/csv.py +3 -2
- etlplus/file/dat.py +3 -2
- etlplus/file/dta.py +3 -2
- etlplus/file/duckdb.py +3 -2
- etlplus/file/enums.py +1 -1
- etlplus/file/feather.py +3 -2
- etlplus/file/fwf.py +3 -2
- etlplus/file/gz.py +3 -2
- etlplus/file/hbs.py +3 -2
- etlplus/file/hdf5.py +3 -2
- etlplus/file/ini.py +3 -2
- etlplus/file/ion.py +3 -2
- etlplus/file/jinja2.py +3 -2
- etlplus/file/json.py +5 -16
- etlplus/file/log.py +3 -2
- etlplus/file/mat.py +3 -2
- etlplus/file/mdb.py +3 -2
- etlplus/file/msgpack.py +3 -2
- etlplus/file/mustache.py +3 -2
- etlplus/file/nc.py +3 -2
- etlplus/file/ndjson.py +3 -2
- etlplus/file/numbers.py +3 -2
- etlplus/file/ods.py +3 -2
- etlplus/file/orc.py +3 -2
- etlplus/file/parquet.py +3 -2
- etlplus/file/pb.py +3 -2
- etlplus/file/pbf.py +3 -2
- etlplus/file/properties.py +3 -2
- etlplus/file/proto.py +3 -2
- etlplus/file/psv.py +3 -2
- etlplus/file/rda.py +3 -2
- etlplus/file/rds.py +3 -2
- etlplus/file/sas7bdat.py +3 -2
- etlplus/file/sav.py +3 -2
- etlplus/file/sqlite.py +3 -2
- etlplus/file/stub.py +1 -0
- etlplus/file/sylk.py +3 -2
- etlplus/file/tab.py +3 -2
- etlplus/file/toml.py +3 -2
- etlplus/file/tsv.py +3 -2
- etlplus/file/txt.py +4 -3
- etlplus/file/vm.py +3 -2
- etlplus/file/wks.py +3 -2
- etlplus/file/xls.py +3 -2
- etlplus/file/xlsm.py +3 -2
- etlplus/file/xlsx.py +3 -2
- etlplus/file/xml.py +9 -3
- etlplus/file/xpt.py +3 -2
- etlplus/file/yaml.py +5 -16
- etlplus/file/zip.py +3 -2
- etlplus/file/zsav.py +3 -2
- etlplus/ops/extract.py +13 -1
- etlplus/ops/load.py +15 -2
- etlplus/ops/run.py +18 -13
- etlplus/ops/transform.py +2 -2
- etlplus/ops/utils.py +6 -35
- etlplus/ops/validate.py +3 -3
- etlplus/templates/README.md +2 -2
- etlplus/types.py +3 -2
- etlplus/utils.py +163 -29
- etlplus/{config → workflow}/README.md +6 -6
- etlplus/workflow/__init__.py +32 -0
- etlplus/{dag.py → workflow/dag.py} +6 -4
- etlplus/{config → workflow}/jobs.py +101 -38
- etlplus/{config → workflow}/pipeline.py +59 -51
- etlplus/{config → workflow}/profile.py +8 -5
- {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/METADATA +4 -4
- etlplus-0.16.0.dist-info/RECORD +141 -0
- {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/WHEEL +1 -1
- etlplus/config/__init__.py +0 -56
- etlplus/config/connector.py +0 -372
- etlplus/config/types.py +0 -204
- etlplus/config/utils.py +0 -120
- etlplus-0.14.3.dist-info/RECORD +0 -135
- {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/entry_points.txt +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.jobs` module.
|
|
3
3
|
|
|
4
4
|
Data classes modeling job orchestration references (extract, validate,
|
|
5
5
|
transform, load).
|
|
6
6
|
|
|
7
7
|
Notes
|
|
8
8
|
-----
|
|
9
|
-
- Lightweight references used inside
|
|
9
|
+
- Lightweight references used inside :class:`PipelineConfig` to avoid storing
|
|
10
10
|
large nested structures.
|
|
11
11
|
- All attributes are simple and optional where appropriate, keeping parsing
|
|
12
12
|
tolerant.
|
|
@@ -19,6 +19,7 @@ from dataclasses import field
|
|
|
19
19
|
from typing import Any
|
|
20
20
|
from typing import Self
|
|
21
21
|
|
|
22
|
+
from ..types import StrAnyMap
|
|
22
23
|
from ..utils import coerce_dict
|
|
23
24
|
from ..utils import maybe_mapping
|
|
24
25
|
|
|
@@ -26,6 +27,7 @@ from ..utils import maybe_mapping
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
__all__ = [
|
|
30
|
+
# Data Classes
|
|
29
31
|
'ExtractRef',
|
|
30
32
|
'JobConfig',
|
|
31
33
|
'LoadRef',
|
|
@@ -34,6 +36,75 @@ __all__ = [
|
|
|
34
36
|
]
|
|
35
37
|
|
|
36
38
|
|
|
39
|
+
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _coerce_optional_str(value: Any) -> str | None:
|
|
43
|
+
"""
|
|
44
|
+
Normalize optional string values, coercing non-strings when needed.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
value : Any
|
|
49
|
+
Optional value to normalize.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
str | None
|
|
54
|
+
``None`` when *value* is ``None``; otherwise a string value.
|
|
55
|
+
"""
|
|
56
|
+
if value is None:
|
|
57
|
+
return None
|
|
58
|
+
return value if isinstance(value, str) else str(value)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _parse_depends_on(
|
|
62
|
+
value: Any,
|
|
63
|
+
) -> list[str]:
|
|
64
|
+
"""
|
|
65
|
+
Normalize dependency declarations into a string list.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
value : Any
|
|
70
|
+
Input dependency specification (string or list of strings).
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
list[str]
|
|
75
|
+
Normalized dependency list.
|
|
76
|
+
"""
|
|
77
|
+
if isinstance(value, str):
|
|
78
|
+
return [value]
|
|
79
|
+
if isinstance(value, list):
|
|
80
|
+
return [entry for entry in value if isinstance(entry, str)]
|
|
81
|
+
return []
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _require_str(
|
|
85
|
+
# data: dict[str, Any],
|
|
86
|
+
data: StrAnyMap,
|
|
87
|
+
key: str,
|
|
88
|
+
) -> str | None:
|
|
89
|
+
"""
|
|
90
|
+
Extract a required string field from a mapping.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
data : StrAnyMap
|
|
95
|
+
Mapping containing the target field.
|
|
96
|
+
key : str
|
|
97
|
+
Field name to extract.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
str | None
|
|
102
|
+
The string value when present and valid; otherwise ``None``.
|
|
103
|
+
"""
|
|
104
|
+
value = data.get(key)
|
|
105
|
+
return value if isinstance(value, str) else None
|
|
106
|
+
|
|
107
|
+
|
|
37
108
|
# SECTION: DATA CLASSES ===================================================== #
|
|
38
109
|
|
|
39
110
|
|
|
@@ -62,12 +133,13 @@ class ExtractRef:
|
|
|
62
133
|
cls,
|
|
63
134
|
obj: Any,
|
|
64
135
|
) -> Self | None:
|
|
65
|
-
"""
|
|
136
|
+
"""
|
|
137
|
+
Parse a mapping into an :class:`ExtractRef` instance.
|
|
66
138
|
|
|
67
139
|
Parameters
|
|
68
140
|
----------
|
|
69
141
|
obj : Any
|
|
70
|
-
Mapping with
|
|
142
|
+
Mapping with :attr:`source` and optional :attr:`options`.
|
|
71
143
|
|
|
72
144
|
Returns
|
|
73
145
|
-------
|
|
@@ -77,8 +149,8 @@ class ExtractRef:
|
|
|
77
149
|
data = maybe_mapping(obj)
|
|
78
150
|
if not data:
|
|
79
151
|
return None
|
|
80
|
-
source = data
|
|
81
|
-
if
|
|
152
|
+
source = _require_str(data, 'source')
|
|
153
|
+
if source is None:
|
|
82
154
|
return None
|
|
83
155
|
return cls(
|
|
84
156
|
source=source,
|
|
@@ -126,7 +198,8 @@ class JobConfig:
|
|
|
126
198
|
cls,
|
|
127
199
|
obj: Any,
|
|
128
200
|
) -> Self | None:
|
|
129
|
-
"""
|
|
201
|
+
"""
|
|
202
|
+
Parse a mapping into a :class:`JobConfig` instance.
|
|
130
203
|
|
|
131
204
|
Parameters
|
|
132
205
|
----------
|
|
@@ -141,22 +214,13 @@ class JobConfig:
|
|
|
141
214
|
data = maybe_mapping(obj)
|
|
142
215
|
if not data:
|
|
143
216
|
return None
|
|
144
|
-
name = data
|
|
145
|
-
if
|
|
217
|
+
name = _require_str(data, 'name')
|
|
218
|
+
if name is None:
|
|
146
219
|
return None
|
|
147
220
|
|
|
148
|
-
description = data.get('description')
|
|
149
|
-
if description is not None and not isinstance(description, str):
|
|
150
|
-
description = str(description)
|
|
221
|
+
description = _coerce_optional_str(data.get('description'))
|
|
151
222
|
|
|
152
|
-
|
|
153
|
-
depends_on: list[str] = []
|
|
154
|
-
if isinstance(depends_raw, str):
|
|
155
|
-
depends_on = [depends_raw]
|
|
156
|
-
elif isinstance(depends_raw, list):
|
|
157
|
-
for entry in depends_raw:
|
|
158
|
-
if isinstance(entry, str):
|
|
159
|
-
depends_on.append(entry)
|
|
223
|
+
depends_on = _parse_depends_on(data.get('depends_on'))
|
|
160
224
|
|
|
161
225
|
return cls(
|
|
162
226
|
name=name,
|
|
@@ -194,12 +258,13 @@ class LoadRef:
|
|
|
194
258
|
cls,
|
|
195
259
|
obj: Any,
|
|
196
260
|
) -> Self | None:
|
|
197
|
-
"""
|
|
261
|
+
"""
|
|
262
|
+
Parse a mapping into a :class:`LoadRef` instance.
|
|
198
263
|
|
|
199
264
|
Parameters
|
|
200
265
|
----------
|
|
201
266
|
obj : Any
|
|
202
|
-
Mapping with
|
|
267
|
+
Mapping with :attr:`target` and optional :attr:`overrides`.
|
|
203
268
|
|
|
204
269
|
Returns
|
|
205
270
|
-------
|
|
@@ -209,8 +274,8 @@ class LoadRef:
|
|
|
209
274
|
data = maybe_mapping(obj)
|
|
210
275
|
if not data:
|
|
211
276
|
return None
|
|
212
|
-
target = data
|
|
213
|
-
if
|
|
277
|
+
target = _require_str(data, 'target')
|
|
278
|
+
if target is None:
|
|
214
279
|
return None
|
|
215
280
|
return cls(
|
|
216
281
|
target=target,
|
|
@@ -240,12 +305,13 @@ class TransformRef:
|
|
|
240
305
|
cls,
|
|
241
306
|
obj: Any,
|
|
242
307
|
) -> Self | None:
|
|
243
|
-
"""
|
|
308
|
+
"""
|
|
309
|
+
Parse a mapping into a :class:`TransformRef` instance.
|
|
244
310
|
|
|
245
311
|
Parameters
|
|
246
312
|
----------
|
|
247
313
|
obj : Any
|
|
248
|
-
Mapping with
|
|
314
|
+
Mapping with :attr:`pipeline`.
|
|
249
315
|
|
|
250
316
|
Returns
|
|
251
317
|
-------
|
|
@@ -255,8 +321,8 @@ class TransformRef:
|
|
|
255
321
|
data = maybe_mapping(obj)
|
|
256
322
|
if not data:
|
|
257
323
|
return None
|
|
258
|
-
pipeline = data
|
|
259
|
-
if
|
|
324
|
+
pipeline = _require_str(data, 'pipeline')
|
|
325
|
+
if pipeline is None:
|
|
260
326
|
return None
|
|
261
327
|
return cls(pipeline=pipeline)
|
|
262
328
|
|
|
@@ -290,12 +356,13 @@ class ValidationRef:
|
|
|
290
356
|
cls,
|
|
291
357
|
obj: Any,
|
|
292
358
|
) -> Self | None:
|
|
293
|
-
"""
|
|
359
|
+
"""
|
|
360
|
+
Parse a mapping into a :class:`ValidationRef` instance.
|
|
294
361
|
|
|
295
362
|
Parameters
|
|
296
363
|
----------
|
|
297
364
|
obj : Any
|
|
298
|
-
Mapping with
|
|
365
|
+
Mapping with :attr:`ruleset` plus optional metadata.
|
|
299
366
|
|
|
300
367
|
Returns
|
|
301
368
|
-------
|
|
@@ -305,15 +372,11 @@ class ValidationRef:
|
|
|
305
372
|
data = maybe_mapping(obj)
|
|
306
373
|
if not data:
|
|
307
374
|
return None
|
|
308
|
-
ruleset = data
|
|
309
|
-
if
|
|
375
|
+
ruleset = _require_str(data, 'ruleset')
|
|
376
|
+
if ruleset is None:
|
|
310
377
|
return None
|
|
311
|
-
severity = data.get('severity')
|
|
312
|
-
|
|
313
|
-
severity = str(severity)
|
|
314
|
-
phase = data.get('phase')
|
|
315
|
-
if phase is not None and not isinstance(phase, str):
|
|
316
|
-
phase = str(phase)
|
|
378
|
+
severity = _coerce_optional_str(data.get('severity'))
|
|
379
|
+
phase = _coerce_optional_str(data.get('phase'))
|
|
317
380
|
return cls(
|
|
318
381
|
ruleset=ruleset,
|
|
319
382
|
severity=severity,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.pipeline` module.
|
|
3
3
|
|
|
4
4
|
Pipeline configuration model and helpers for job orchestration.
|
|
5
5
|
|
|
@@ -16,6 +16,7 @@ Notes
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
import os
|
|
19
|
+
from collections.abc import Callable
|
|
19
20
|
from collections.abc import Mapping
|
|
20
21
|
from dataclasses import dataclass
|
|
21
22
|
from dataclasses import field
|
|
@@ -24,72 +25,90 @@ from typing import Any
|
|
|
24
25
|
from typing import Self
|
|
25
26
|
|
|
26
27
|
from ..api import ApiConfig
|
|
28
|
+
from ..connector import Connector
|
|
29
|
+
from ..connector import parse_connector
|
|
27
30
|
from ..file import File
|
|
28
31
|
from ..file import FileFormat
|
|
29
32
|
from ..types import StrAnyMap
|
|
30
33
|
from ..utils import coerce_dict
|
|
34
|
+
from ..utils import deep_substitute
|
|
31
35
|
from ..utils import maybe_mapping
|
|
32
|
-
from .connector import Connector
|
|
33
|
-
from .connector import parse_connector
|
|
34
36
|
from .jobs import JobConfig
|
|
35
37
|
from .profile import ProfileConfig
|
|
36
|
-
from .utils import deep_substitute
|
|
37
38
|
|
|
38
39
|
# SECTION: EXPORTS ========================================================== #
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
__all__ = [
|
|
42
|
+
__all__ = [
|
|
43
|
+
# Data Classes
|
|
44
|
+
'PipelineConfig',
|
|
45
|
+
# Functions
|
|
46
|
+
'load_pipeline_config',
|
|
47
|
+
]
|
|
42
48
|
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _collect_parsed[T](
|
|
45
54
|
raw: StrAnyMap,
|
|
46
|
-
|
|
55
|
+
key: str,
|
|
56
|
+
parser: Callable[[Any], T | None],
|
|
57
|
+
) -> list[T]:
|
|
47
58
|
"""
|
|
48
|
-
|
|
59
|
+
Collect parsed items from ``raw[key]`` using a tolerant parser.
|
|
49
60
|
|
|
50
61
|
Parameters
|
|
51
62
|
----------
|
|
52
63
|
raw : StrAnyMap
|
|
53
64
|
Raw pipeline mapping.
|
|
65
|
+
key : str
|
|
66
|
+
Key pointing to a list-like payload.
|
|
67
|
+
parser : Callable[[Any], T | None]
|
|
68
|
+
Parser that returns an instance or ``None`` for invalid entries.
|
|
54
69
|
|
|
55
70
|
Returns
|
|
56
71
|
-------
|
|
57
|
-
list[
|
|
58
|
-
Parsed
|
|
72
|
+
list[T]
|
|
73
|
+
Parsed items, excluding invalid entries.
|
|
59
74
|
"""
|
|
60
|
-
|
|
61
|
-
for
|
|
62
|
-
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return jobs
|
|
75
|
+
items: list[T] = []
|
|
76
|
+
for entry in raw.get(key, []) or []:
|
|
77
|
+
parsed = parser(entry)
|
|
78
|
+
if parsed is not None:
|
|
79
|
+
items.append(parsed)
|
|
80
|
+
return items
|
|
67
81
|
|
|
68
82
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
) ->
|
|
83
|
+
def _parse_connector_entry(
|
|
84
|
+
obj: Any,
|
|
85
|
+
) -> Connector | None:
|
|
72
86
|
"""
|
|
73
|
-
|
|
87
|
+
Parse a connector mapping into a concrete connector instance.
|
|
74
88
|
|
|
75
89
|
Parameters
|
|
76
90
|
----------
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
obj : Any
|
|
92
|
+
Candidate connector mapping.
|
|
79
93
|
|
|
80
94
|
Returns
|
|
81
95
|
-------
|
|
82
|
-
|
|
83
|
-
Parsed
|
|
96
|
+
Connector | None
|
|
97
|
+
Parsed connector instance or ``None`` when invalid.
|
|
84
98
|
"""
|
|
85
|
-
|
|
99
|
+
if not (entry := maybe_mapping(obj)):
|
|
100
|
+
return None
|
|
101
|
+
try:
|
|
102
|
+
return parse_connector(entry)
|
|
103
|
+
except TypeError:
|
|
104
|
+
return None
|
|
86
105
|
|
|
87
106
|
|
|
88
|
-
def
|
|
107
|
+
def _build_sources(
|
|
89
108
|
raw: StrAnyMap,
|
|
90
109
|
) -> list[Connector]:
|
|
91
110
|
"""
|
|
92
|
-
Return a list of
|
|
111
|
+
Return a list of source connectors parsed from the mapping.
|
|
93
112
|
|
|
94
113
|
Parameters
|
|
95
114
|
----------
|
|
@@ -99,43 +118,32 @@ def _build_targets(
|
|
|
99
118
|
Returns
|
|
100
119
|
-------
|
|
101
120
|
list[Connector]
|
|
102
|
-
Parsed
|
|
121
|
+
Parsed source connectors.
|
|
103
122
|
"""
|
|
104
|
-
return
|
|
123
|
+
return list(
|
|
124
|
+
_collect_parsed(raw, 'sources', _parse_connector_entry),
|
|
125
|
+
)
|
|
105
126
|
|
|
106
127
|
|
|
107
|
-
def
|
|
128
|
+
def _build_targets(
|
|
108
129
|
raw: StrAnyMap,
|
|
109
|
-
key: str,
|
|
110
130
|
) -> list[Connector]:
|
|
111
131
|
"""
|
|
112
|
-
Return
|
|
113
|
-
|
|
114
|
-
Unknown or malformed entries are skipped to preserve permissiveness.
|
|
132
|
+
Return a list of target connectors parsed from the mapping.
|
|
115
133
|
|
|
116
134
|
Parameters
|
|
117
135
|
----------
|
|
118
136
|
raw : StrAnyMap
|
|
119
137
|
Raw pipeline mapping.
|
|
120
|
-
key : str
|
|
121
|
-
List-containing top-level key ("sources" or "targets").
|
|
122
138
|
|
|
123
139
|
Returns
|
|
124
140
|
-------
|
|
125
141
|
list[Connector]
|
|
126
|
-
|
|
142
|
+
Parsed target connectors.
|
|
127
143
|
"""
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
continue
|
|
132
|
-
try:
|
|
133
|
-
items.append(parse_connector(entry))
|
|
134
|
-
except TypeError:
|
|
135
|
-
# Skip unsupported types or malformed entries
|
|
136
|
-
continue
|
|
137
|
-
|
|
138
|
-
return items
|
|
144
|
+
return list(
|
|
145
|
+
_collect_parsed(raw, 'targets', _parse_connector_entry),
|
|
146
|
+
)
|
|
139
147
|
|
|
140
148
|
|
|
141
149
|
# SECTION: FUNCTIONS ======================================================== #
|
|
@@ -156,7 +164,7 @@ def load_pipeline_config(
|
|
|
156
164
|
return PipelineConfig.from_yaml(path, substitute=substitute, env=env)
|
|
157
165
|
|
|
158
166
|
|
|
159
|
-
# SECTION: CLASSES
|
|
167
|
+
# SECTION: DATA CLASSES ===================================================== #
|
|
160
168
|
|
|
161
169
|
|
|
162
170
|
@dataclass(kw_only=True, slots=True)
|
|
@@ -313,7 +321,7 @@ class PipelineConfig:
|
|
|
313
321
|
targets = _build_targets(raw)
|
|
314
322
|
|
|
315
323
|
# Jobs
|
|
316
|
-
jobs =
|
|
324
|
+
jobs = _collect_parsed(raw, 'jobs', JobConfig.from_obj)
|
|
317
325
|
|
|
318
326
|
# Table schemas (optional, tolerant pass-through structures).
|
|
319
327
|
table_schemas: list[dict[str, Any]] = []
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.profile` module.
|
|
3
3
|
|
|
4
4
|
Profile model for pipeline-level defaults and environment.
|
|
5
5
|
|
|
@@ -22,10 +22,13 @@ from ..utils import cast_str_dict
|
|
|
22
22
|
# SECTION: EXPORTS ========================================================== #
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
__all__ = [
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Data Classes
|
|
27
|
+
'ProfileConfig',
|
|
28
|
+
]
|
|
26
29
|
|
|
27
30
|
|
|
28
|
-
# SECTION: CLASSES
|
|
31
|
+
# SECTION: DATA CLASSES ===================================================== #
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
@dataclass(kw_only=True, slots=True)
|
|
@@ -53,7 +56,7 @@ class ProfileConfig:
|
|
|
53
56
|
cls,
|
|
54
57
|
obj: StrAnyMap | None,
|
|
55
58
|
) -> Self:
|
|
56
|
-
"""Parse a mapping into a
|
|
59
|
+
"""Parse a mapping into a :class:`ProfileConfig` instance.
|
|
57
60
|
|
|
58
61
|
Parameters
|
|
59
62
|
----------
|
|
@@ -64,7 +67,7 @@ class ProfileConfig:
|
|
|
64
67
|
-------
|
|
65
68
|
Self
|
|
66
69
|
Parsed profile configuration; non-mapping input yields a default
|
|
67
|
-
instance. All
|
|
70
|
+
instance. All :attr:`env` values are coerced to strings.
|
|
68
71
|
"""
|
|
69
72
|
if not isinstance(obj, Mapping):
|
|
70
73
|
return cls()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: etlplus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.0
|
|
4
4
|
Summary: A Swiss Army knife for simple ETL operations
|
|
5
5
|
Home-page: https://github.com/Dagitali/ETLPlus
|
|
6
6
|
Author: ETLPlus Team
|
|
@@ -805,12 +805,12 @@ Navigate to detailed documentation for each subpackage:
|
|
|
805
805
|
|
|
806
806
|
- [etlplus.api](etlplus/api/README.md): Lightweight HTTP client and paginated REST helpers
|
|
807
807
|
- [etlplus.file](etlplus/file/README.md): Unified file format support and helpers
|
|
808
|
-
- [etlplus.
|
|
809
|
-
and profiles
|
|
810
|
-
- [etlplus.cli](etlplus/cli/README.md): Command-line interface for ETLPlus workflows
|
|
808
|
+
- [etlplus.cli](etlplus/cli/README.md): Command-line interface definitions for `etlplus`
|
|
811
809
|
- [etlplus.database](etlplus/database/README.md): Database engine, schema, and ORM helpers
|
|
812
810
|
- [etlplus.templates](etlplus/templates/README.md): SQL and DDL template helpers
|
|
813
811
|
- [etlplus.validation](etlplus/validation/README.md): Data validation utilities and helpers
|
|
812
|
+
- [etlplus.workflow](etlplus/workflow/README.md): Helpers for data connectors, pipelines, jobs, and
|
|
813
|
+
profiles
|
|
814
814
|
|
|
815
815
|
### Community Health
|
|
816
816
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
etlplus/README.md,sha256=JaMSomnMsHrTruDnonHqe83Rv4K0-e7Wy46tMeVoleU,1468
|
|
2
|
+
etlplus/__init__.py,sha256=mgTP4PJmRmsEjTCAizzzdtzAmhuHtarmPzphzdjvLgM,277
|
|
3
|
+
etlplus/__main__.py,sha256=btoROneNiigyfBU7BSzPKZ1R9gzBMpxcpsbPwmuHwTM,479
|
|
4
|
+
etlplus/__version__.py,sha256=1E0GMK_yUWCMQFKxXjTvyMwofi0qT2k4CDNiHWiymWE,327
|
|
5
|
+
etlplus/enums.py,sha256=8-uUOKe68cPzlmUg-e7gavkC95kbTJXRpRzvXehIsRk,6841
|
|
6
|
+
etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
|
|
7
|
+
etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
etlplus/types.py,sha256=vB1Sh_5771eVv8-RClJatBQ7PcsScY6C2MvJqnI8IG8,6216
|
|
9
|
+
etlplus/utils.py,sha256=X-k_Y8i6oDjlE5aQu9sw3gPw7O2ikiSn4uoheVv_ERc,17091
|
|
10
|
+
etlplus/api/README.md,sha256=amxS_eIcsnNuVvD0x_w8nkyfedOTYbhlY0gGhaFg0DE,8705
|
|
11
|
+
etlplus/api/__init__.py,sha256=PK2lQv1FbsE7ZZS_ejevFZQSuOUHGApBc22YfHAzMqA,4615
|
|
12
|
+
etlplus/api/auth.py,sha256=wgNC9WDrb-5QjI81q5jl7pQHwNNHq7lVUF7uWw2jRP0,12154
|
|
13
|
+
etlplus/api/config.py,sha256=0Exfn208ijFep3gdww2KdqPxO4NKA4FG_rcn5x5dHM4,17635
|
|
14
|
+
etlplus/api/endpoint_client.py,sha256=Nekx-8mpU41EtbOyOcW4mjmch1jyPqtHu60_rfxF0Pw,30697
|
|
15
|
+
etlplus/api/enums.py,sha256=Tvkru6V8fzQh2JM2FDLcA_yaPENOKz5JgzxLhieqEvc,1141
|
|
16
|
+
etlplus/api/errors.py,sha256=XjI2xW-sypMUNUbqfc2S57-IGyWnH3oCDFhCmKYYI_Q,4648
|
|
17
|
+
etlplus/api/request_manager.py,sha256=fhzPV5x7DqpKqoLvfDR8GKhBX_QBMtvZsRXxVnQQElY,18674
|
|
18
|
+
etlplus/api/retry_manager.py,sha256=aq9iNCxt-Puy4rAgKNtNucxw2eP1yqAKZ2lfgMkzbCk,11302
|
|
19
|
+
etlplus/api/transport.py,sha256=abm-_WieBDSSbFanBwhmudBuVVm7LjYUb8vrlMXo7SA,9408
|
|
20
|
+
etlplus/api/types.py,sha256=Ng1b83RaJSHn4jl-M1f1dsTgjXizQtrW4yOXAYjwk_4,7377
|
|
21
|
+
etlplus/api/utils.py,sha256=lNBfJKz3fJ4RhvnnX3uxVZC__6-WKksYMSGGYi0RRqM,26247
|
|
22
|
+
etlplus/api/pagination/__init__.py,sha256=a4UX2J0AG8RMvmHt_CCofUm5vSmFo6GAfkb8XnSXypM,1395
|
|
23
|
+
etlplus/api/pagination/client.py,sha256=yMEpWqRxTCD4zRc9OYtEyUtShpGH5atiHFEAt95v2FE,5394
|
|
24
|
+
etlplus/api/pagination/config.py,sha256=u2hk8QmiQoIKe16sPUB6SvVqiLkuq9lUaY4Pl5-lejk,13577
|
|
25
|
+
etlplus/api/pagination/paginator.py,sha256=B0OK_0FVmUz3-lCDeKgDOqYJOoEQtjO6I5eSmK58tbY,24433
|
|
26
|
+
etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-Y_lbU6kI,1070
|
|
27
|
+
etlplus/api/rate_limiting/config.py,sha256=Byc_kmnwFmjjfDEFIdc_sHc7Wnjde1NGgsjPHgE9_xo,9765
|
|
28
|
+
etlplus/api/rate_limiting/rate_limiter.py,sha256=uYxn-l2qwLUKVclDQ3vJIIP3fozJx2JlHhz7_zyXVbA,7033
|
|
29
|
+
etlplus/cli/README.md,sha256=8H_G2d3HteYIU6ReX9K9DM485QjWDT5vHMQbGD_vv20,1237
|
|
30
|
+
etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
|
|
31
|
+
etlplus/cli/commands.py,sha256=HFlg29tO6Jwv1NXWAHmvniLCyRSlboL55Arn9B8nZAM,25028
|
|
32
|
+
etlplus/cli/constants.py,sha256=0F7dXIQKWUhhVu2Us527GJeknJIWpBqz7CK2e5OQgcE,1947
|
|
33
|
+
etlplus/cli/handlers.py,sha256=uvbAyF6Ux8_5C-obCWZOrOP0QP0oiT-Km1hPhE8tDx0,18558
|
|
34
|
+
etlplus/cli/io.py,sha256=tGGNQ4ecezqj-mD285fgBVrYdphdeqApsyV9VojOj1I,7836
|
|
35
|
+
etlplus/cli/main.py,sha256=68_uJwmWajhOC9o4R_ns8IQloC9BFmAKC_9GlQOxKWg,5239
|
|
36
|
+
etlplus/cli/options.py,sha256=vfXT3YLh7wG1iC-aTdSg6ItMC8l6n0Lozmy53XjqLbA,1199
|
|
37
|
+
etlplus/cli/state.py,sha256=3Dq5BKct0uAvRajtc2yHbsX7wqepZOwlAMKsyvQcnqk,7918
|
|
38
|
+
etlplus/cli/types.py,sha256=tclhKVJXDqHzlTQBYKARfqMgDOcuBJ-Zej2pvFy96WM,652
|
|
39
|
+
etlplus/connector/__init__.py,sha256=KMBUPBFU1xA4aVkA2RxrdZ3IR6yWHr-F86GYK05wyKg,1006
|
|
40
|
+
etlplus/connector/api.py,sha256=vkeoqR94cVtEW4uKeVzuXKji08ojzd0fyqhtLSQ0V1k,4524
|
|
41
|
+
etlplus/connector/connector.py,sha256=OQP4kK_1O6g9FnDBrE3L58LOSfImsK5EBHtKI4N71u8,574
|
|
42
|
+
etlplus/connector/core.py,sha256=0GeXjlZFnyS-4j7jR_AtclQtELE6x-vodHJ4rfjFLL8,2795
|
|
43
|
+
etlplus/connector/database.py,sha256=fK_b4r5NsFTTUv0GHoEen7sVX45wVy1JLKds0qxfVM8,3011
|
|
44
|
+
etlplus/connector/enums.py,sha256=43NziUOpol4YvBtM13WJJzY1EAQOjaWESxLl7J2ZT8U,1069
|
|
45
|
+
etlplus/connector/file.py,sha256=c8zBKRYnrXXqLZRpB_FRBMFlel5BU6GFEDFdfX6uk9c,2855
|
|
46
|
+
etlplus/connector/types.py,sha256=j8eet-a701VC6VQa1pIYLt4HdDcQp8druXtOCb7UMLw,945
|
|
47
|
+
etlplus/connector/utils.py,sha256=fS2hPAfuhKTg_L2xDxF5fJnsO1SuuDIiEWU7GuaJKUM,2933
|
|
48
|
+
etlplus/database/README.md,sha256=3Af5BEGLkBmMoGOLtS1GQuj4wKPh_CwSp5NEPMf2uaY,1435
|
|
49
|
+
etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
|
|
50
|
+
etlplus/database/ddl.py,sha256=-7EAbyLkU8m3eYlHSNLFOQr1I4fPEbF0hTyByzjyvsU,7909
|
|
51
|
+
etlplus/database/engine.py,sha256=eDFnp4vzhoKuyLJSeHYpndHLUr27neS7CgdvMw8mNok,4384
|
|
52
|
+
etlplus/database/orm.py,sha256=ZCHkeVEUns2eievlFzmLyVKA3YVPea1xs6vrcUBZ7Jw,10010
|
|
53
|
+
etlplus/database/schema.py,sha256=813C0Dd3WE53KTYot4dgjAxctgKXLXx-8_Rk_4r2e28,7022
|
|
54
|
+
etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
|
|
55
|
+
etlplus/file/README.md,sha256=ivU8svVs1fktQiW5ozvh1N-IOSLCAQ3oM9bW8DUFwIw,3630
|
|
56
|
+
etlplus/file/__init__.py,sha256=X03bosSM-uSd6dh3ur0un6_ozFRw2Tm4PE6kVUjtXK8,475
|
|
57
|
+
etlplus/file/_imports.py,sha256=Cozv7d5G2P9PNgy2M4vrz0Wzo7hx9FTC0WcGcuVqga0,3193
|
|
58
|
+
etlplus/file/_io.py,sha256=Z3aTujy0rpbMKJHvO2UZ6bA1ohO-6ZiemyxF4GsayRc,3951
|
|
59
|
+
etlplus/file/accdb.py,sha256=mMwnNXd3rB0Z_1uN6wpiF0jL4flUmZbMBoiIS3Fq5mg,1691
|
|
60
|
+
etlplus/file/arrow.py,sha256=nkSXiDCFOSbEJX387h-c91WeYPFZehj2r3_iznVy5HE,1704
|
|
61
|
+
etlplus/file/avro.py,sha256=GR9GbDNcQ7TpaPKhcC6JL79XicSmNDIhH3cP0QDmLJg,4447
|
|
62
|
+
etlplus/file/bson.py,sha256=CYpA1PSPzyM9Dtv5ik3RG0GUwLjucJGbrhyo7THcVa0,1623
|
|
63
|
+
etlplus/file/cbor.py,sha256=NNQkCRenh9YOYdeR-w-AbUfK9sjGkwoA0Idtr-dclm4,1677
|
|
64
|
+
etlplus/file/cfg.py,sha256=HuGuxNIKPWyg14mIBlVB5Ch_npf9zObRt2mgoymzB_A,1720
|
|
65
|
+
etlplus/file/conf.py,sha256=x6OH9in-VKkTIPtlwbYEsYDJ703oFJjUFHVCoFULhvk,1778
|
|
66
|
+
etlplus/file/core.py,sha256=IzcG4pQLq3QCQhswbgdWKxHSvmzvWzYweBrz7t6HDDo,8888
|
|
67
|
+
etlplus/file/csv.py,sha256=FTdxlVs3vsaj_t7vGY-unNiCpJI2TWuiaf2_8dJan6M,1754
|
|
68
|
+
etlplus/file/dat.py,sha256=o4FN-6_HCVXuD-If3aYfIqU4QTZMvECNZ5Yx4wr15ew,1652
|
|
69
|
+
etlplus/file/dta.py,sha256=7DuvvKSgbpBoKZHm7CWJ7NKstRasp0fUEj1XF02YgsE,1626
|
|
70
|
+
etlplus/file/duckdb.py,sha256=kCFxNUEI9gEeeMWJuCHyfKVsqvhij4Q7LJVpZ3AMDYw,1641
|
|
71
|
+
etlplus/file/enums.py,sha256=5cwIfcoYGEjaX0AzaJzY0PjztKykzFTQ1HBnb2NTEXs,11065
|
|
72
|
+
etlplus/file/feather.py,sha256=joOdQf_oIu__i8hwy5X4eK6MSqh6O0kKnub2VWD_Clg,2679
|
|
73
|
+
etlplus/file/fwf.py,sha256=ktXIKatOPUs6YeB30K7BNvfwwB4BkXoYEomsvCjk_ss,1599
|
|
74
|
+
etlplus/file/gz.py,sha256=NfiXiE37rS2YC7dk1YC1ELbbEpzJdypIy-no9cVoaco,2641
|
|
75
|
+
etlplus/file/hbs.py,sha256=jxAPY10R_2vhP-1QWGBmANCI1dJ9vetaLo5Mp8kMa3Y,1624
|
|
76
|
+
etlplus/file/hdf5.py,sha256=hjpBUue5ss6R8ljZ-gYxDxpOLGBKgNvmAvn97uNmYAw,1634
|
|
77
|
+
etlplus/file/ini.py,sha256=ZL_0Wq5-yNcveyKF-minH2AFh-RmspM06myHquAobao,1711
|
|
78
|
+
etlplus/file/ion.py,sha256=ZK0g-47GQi9fCDT4EeXCgiVbWmmj7SU1WHwt-CrLwCU,1720
|
|
79
|
+
etlplus/file/jinja2.py,sha256=TV-ZIzsJIg4hJyvWI7TWaMRjqL2s18Dg3ig3Hs50_pE,1640
|
|
80
|
+
etlplus/file/json.py,sha256=vY3UYjzQzmJUAY0avR2cm70hTTED4xEF38WEEj0DhDQ,2140
|
|
81
|
+
etlplus/file/log.py,sha256=6Te617oy8RWrzCa6hC0O65ygPIpfzkLBcP-x9glwM9g,1691
|
|
82
|
+
etlplus/file/mat.py,sha256=JbZ7SziCqYZR8LljyLoSn05_-bPSzNH16KNB-wvzdpM,1664
|
|
83
|
+
etlplus/file/mdb.py,sha256=Xlxx9RZlnErOrzYRFnzzOZltc99FalhoUY-oAiiQpPg,1667
|
|
84
|
+
etlplus/file/msgpack.py,sha256=GOYfgxyK2N8Opy8kLsz4uDhVrT1vyOcVQ4eJvdhrNdA,1668
|
|
85
|
+
etlplus/file/mustache.py,sha256=qOddtbtZwDyoXzHzqxpnUoO_lWKf3NPyfp-V21o0cRw,1657
|
|
86
|
+
etlplus/file/nc.py,sha256=a1_er7Fco0FVw3GtDrd_OzZAI7U_sP9x-wi9qRgebaw,1680
|
|
87
|
+
etlplus/file/ndjson.py,sha256=jpZgLZL-Vy4a6tsm5cxy0G9kW-x-X5kFSbnUn-XjIJw,2442
|
|
88
|
+
etlplus/file/numbers.py,sha256=HkG3ISk35FZnorwECuciIiI4HZWFp9E-mRhOfhYclx8,1620
|
|
89
|
+
etlplus/file/ods.py,sha256=MVEM1E2uLFmnTyCnyhljsrgdBC8psUGSVUGLmiuZTLM,1810
|
|
90
|
+
etlplus/file/orc.py,sha256=RdK4IERoLWnGSgpBclfDmwMmtac8VYyet3Z9AFCyj2E,2612
|
|
91
|
+
etlplus/file/parquet.py,sha256=6klk-GgNnFBbsRU0J8LiGm5c67A6FcX1AEZNf-cNSPM,2768
|
|
92
|
+
etlplus/file/pb.py,sha256=0OOSmuFUfmB-DZILSA9FO2JgXfJmt9TnigH3E2gW4AI,1625
|
|
93
|
+
etlplus/file/pbf.py,sha256=89fF58WkXmOBJh8Jh2zcA0cKBkB5neXMWyz95toPnXo,1630
|
|
94
|
+
etlplus/file/properties.py,sha256=4KSRGWo_-fthVdOnJKXqUvHA2I6igcGWW9fnWJK7oLA,1728
|
|
95
|
+
etlplus/file/proto.py,sha256=LjHFWVYEIyADwpd9Lzzd1FAcM8pFXN1pwoptxEDOvfQ,1678
|
|
96
|
+
etlplus/file/psv.py,sha256=J-fPTMgTZFHZWJL6kLKaFgLXhtEtK66hJW59YLaWe9E,1736
|
|
97
|
+
etlplus/file/rda.py,sha256=dSrHwLl_qu8bhnltWIuYqxpguP3JLH39JLOVxHsHLhE,1678
|
|
98
|
+
etlplus/file/rds.py,sha256=o1IKY6CmLXSp-Iv7kBDFkT8X5QVTy5uVAZMm-7sUo4Y,1635
|
|
99
|
+
etlplus/file/sas7bdat.py,sha256=yyX7YrnmZ91EY9WeTB_eZAqjCSEgD_9WTLGpD4Sv5AY,1716
|
|
100
|
+
etlplus/file/sav.py,sha256=ij02gnYOevRV9e8dbDwi3plulkw-qEQoxccvydHXZK4,1607
|
|
101
|
+
etlplus/file/sqlite.py,sha256=NRb93RuIDlu1eRMZnYDjDBhFO88gkDT7tvpmfgA4hQE,1662
|
|
102
|
+
etlplus/file/stub.py,sha256=4vlqjIo_bBy69dIU2y2iy5fqnezt_bOuS218zyijQfs,1749
|
|
103
|
+
etlplus/file/sylk.py,sha256=QwciGJVWxEKz3TH4s7hshHHLAfKiCqBxyV4umg9EVQI,1667
|
|
104
|
+
etlplus/file/tab.py,sha256=Y9u5J-efoKIMIYnARhrPXA2KEF0pJ7deZF4zkz7ZID0,1994
|
|
105
|
+
etlplus/file/toml.py,sha256=0kGJEF2353kNQWFbYd8G4XWjuPHvdl7vPILCWglsyk0,1636
|
|
106
|
+
etlplus/file/tsv.py,sha256=SUe-UeL3zExX2idvpHiMWuRm5VQ8AyqqlNJckc9CDCU,1776
|
|
107
|
+
etlplus/file/txt.py,sha256=Ys7MmKBOiHQnRK0Bb3VuIIny2_1Ejr-V_YdPlUfOEjc,2321
|
|
108
|
+
etlplus/file/vm.py,sha256=K3EE-4TpXECcHxS1EEVmLOidkqj-MJeeztTRTRUaQ1o,1599
|
|
109
|
+
etlplus/file/wks.py,sha256=dZtf_6ObJVUj_m7XIUAzuTDQkmYk0mhA9LF3rVltatI,1665
|
|
110
|
+
etlplus/file/xls.py,sha256=dNfhaDSbSy-xQrE3qriCrmfXRQY7HqAtog91b-7RmpA,1782
|
|
111
|
+
etlplus/file/xlsm.py,sha256=pZHybknmipUHEwjQBHWiU4HfVMLkGG7Zxe3rxBrxPT0,1748
|
|
112
|
+
etlplus/file/xlsx.py,sha256=UOwz-IIeRU2GyAI8Upx2hn7v3KPZalpIaGO9hXuaOGo,2192
|
|
113
|
+
etlplus/file/xml.py,sha256=lor8KsclDSy1tdOExL1GmYpaQVcZGRNEwOd2liiPPbk,4389
|
|
114
|
+
etlplus/file/xpt.py,sha256=L70o8cIGWPeBdbfVJk-b9I9PE2MICtLkwhJjzqIeBgU,1712
|
|
115
|
+
etlplus/file/yaml.py,sha256=b_SxDSEQPVXQv9a9Ih4wAcI940pE5Ksy5pQE6K6ckhw,2062
|
|
116
|
+
etlplus/file/zip.py,sha256=8wnmnGW_pGTx65736CzAG67XIi5y98KxucRT8sNDeuQ,4195
|
|
117
|
+
etlplus/file/zsav.py,sha256=5hMuBjYeHw--UL2ZCCDn6TzJkr_YNhdQhvKI6nr3WW0,1674
|
|
118
|
+
etlplus/ops/README.md,sha256=8omi7DYZhelc26JKk8Cm8QR8I3OGwziysPj1ivx41iQ,1380
|
|
119
|
+
etlplus/ops/__init__.py,sha256=NIIr2f-AZj5B0piBt6gjv46Yn0SzGYxEe6BPoopRh38,1702
|
|
120
|
+
etlplus/ops/extract.py,sha256=xNHcKdx8LrnvIMWUFG-SmszpAlclz2jhvKFfX-sFGLI,5994
|
|
121
|
+
etlplus/ops/load.py,sha256=sxADrZ0CE4NfJ7T4Chwa6ckTkK3gSjYRdJW4qL-B61k,8536
|
|
122
|
+
etlplus/ops/run.py,sha256=x7bYrokpBE4XRfl6Rq7xeD3s-5A9P4HUVlufj_PxoTw,13526
|
|
123
|
+
etlplus/ops/transform.py,sha256=H9DxAA-c9kwklAnmiZK21w7FMJnHfu0bLcguXfIGE-k,25417
|
|
124
|
+
etlplus/ops/utils.py,sha256=lJmrO1KDob-xZU8Gc2SvZvMgdYLsVoaz-fTV42KkLVo,10835
|
|
125
|
+
etlplus/ops/validate.py,sha256=-OLAwQNNCmmDbmj0SB7zzYXDkJfcyBP_z9nTpqImLP0,13271
|
|
126
|
+
etlplus/templates/README.md,sha256=IfPXlj1TGVA-uFWosHJhE2rabFW-znxOlOMazO9Z5cE,1361
|
|
127
|
+
etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
|
|
128
|
+
etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
|
|
129
|
+
etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
|
|
130
|
+
etlplus/workflow/README.md,sha256=D1oloiJCOHiqpqgv3m3qpRSIUOMIQcWtIsOPv7KkNI0,1652
|
|
131
|
+
etlplus/workflow/__init__.py,sha256=PWWAv4Bb94hls8YDZXU5DY5aXMbtSYOLYHqGRThxoA4,668
|
|
132
|
+
etlplus/workflow/dag.py,sha256=kp31dORgk0GHbct_bipU5hu_0elwBtwLsXGjMWuhFHI,2503
|
|
133
|
+
etlplus/workflow/jobs.py,sha256=onvzsZpTpZUWIivIGR4SjbUPSZ1X0nXpxAW0O0VumhQ,8945
|
|
134
|
+
etlplus/workflow/pipeline.py,sha256=7bGvMywBu_H6XLsYjeNBS_NmVH7Bo7BmuzkWm-BAXMI,9633
|
|
135
|
+
etlplus/workflow/profile.py,sha256=dZ6P50k_ZqXnrbgrbODUqgVkymbchcEqfZR-ExjTd3M,1935
|
|
136
|
+
etlplus-0.16.0.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
|
|
137
|
+
etlplus-0.16.0.dist-info/METADATA,sha256=soyAK0MDUoH-U0-WQt40XS6zH8L8Q5dywyHtEO3cUGo,28114
|
|
138
|
+
etlplus-0.16.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
139
|
+
etlplus-0.16.0.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
|
|
140
|
+
etlplus-0.16.0.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
|
|
141
|
+
etlplus-0.16.0.dist-info/RECORD,,
|