etlplus 0.14.3__py3-none-any.whl → 0.15.2__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/config.py +3 -8
- etlplus/api/types.py +89 -0
- etlplus/api/utils.py +5 -1
- etlplus/cli/README.md +2 -2
- etlplus/cli/commands.py +75 -42
- etlplus/cli/handlers.py +32 -14
- etlplus/cli/main.py +1 -1
- etlplus/cli/state.py +4 -7
- etlplus/database/README.md +2 -2
- etlplus/database/engine.py +18 -2
- etlplus/database/orm.py +2 -0
- etlplus/file/README.md +2 -2
- etlplus/file/_io.py +39 -0
- etlplus/file/json.py +2 -14
- etlplus/file/yaml.py +2 -14
- etlplus/ops/run.py +14 -9
- etlplus/ops/utils.py +4 -33
- etlplus/ops/validate.py +3 -3
- etlplus/templates/README.md +2 -2
- etlplus/types.py +3 -2
- etlplus/utils.py +136 -2
- etlplus/{config → workflow}/README.md +6 -6
- etlplus/{config → workflow}/__init__.py +10 -23
- etlplus/{config → workflow}/connector.py +58 -44
- etlplus/{dag.py → workflow/dag.py} +6 -4
- etlplus/{config → workflow}/jobs.py +101 -38
- etlplus/{config → workflow}/pipeline.py +57 -49
- etlplus/{config → workflow}/profile.py +8 -5
- etlplus/workflow/types.py +115 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.2.dist-info}/METADATA +4 -4
- {etlplus-0.14.3.dist-info → etlplus-0.15.2.dist-info}/RECORD +37 -38
- {etlplus-0.14.3.dist-info → etlplus-0.15.2.dist-info}/WHEEL +1 -1
- etlplus/config/types.py +0 -204
- etlplus/config/utils.py +0 -120
- {etlplus-0.14.3.dist-info → etlplus-0.15.2.dist-info}/entry_points.txt +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.2.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.2.dist-info}/top_level.txt +0 -0
|
@@ -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()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.workflow.types` module.
|
|
3
|
+
|
|
4
|
+
Type aliases and editor-only :class:`TypedDict`s for :mod:`etlplus.workflow`.
|
|
5
|
+
|
|
6
|
+
These types improve IDE autocomplete and static analysis while the runtime
|
|
7
|
+
parsers remain permissive.
|
|
8
|
+
|
|
9
|
+
Notes
|
|
10
|
+
-----
|
|
11
|
+
- TypedDicts in this module are intentionally ``total=False`` and are not
|
|
12
|
+
enforced at runtime.
|
|
13
|
+
- :meth:`*.from_obj` constructors accept :class:`Mapping[str, Any]` and perform
|
|
14
|
+
tolerant parsing and light casting. This keeps the runtime permissive while
|
|
15
|
+
improving autocomplete and static analysis for contributors.
|
|
16
|
+
|
|
17
|
+
Examples
|
|
18
|
+
--------
|
|
19
|
+
>>> from etlplus.workflow import Connector
|
|
20
|
+
>>> src: Connector = {
|
|
21
|
+
>>> "type": "file",
|
|
22
|
+
>>> "path": "/data/input.csv",
|
|
23
|
+
>>> }
|
|
24
|
+
>>> tgt: Connector = {
|
|
25
|
+
>>> "type": "database",
|
|
26
|
+
>>> "connection_string": "postgresql://user:pass@localhost/db",
|
|
27
|
+
>>> }
|
|
28
|
+
>>> from etlplus.api import RetryPolicy
|
|
29
|
+
>>> rp: RetryPolicy = {"max_attempts": 3, "backoff": 0.5}
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from typing import Literal
|
|
35
|
+
from typing import TypedDict
|
|
36
|
+
|
|
37
|
+
from ..api import PaginationConfigMap
|
|
38
|
+
from ..api import RateLimitConfigMap
|
|
39
|
+
from ..types import StrAnyMap
|
|
40
|
+
|
|
41
|
+
# SECTION: EXPORTS ========================================================= #
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Type Aliases
|
|
46
|
+
'ConnectorType',
|
|
47
|
+
# Typed Dicts
|
|
48
|
+
'ConnectorApiConfigMap',
|
|
49
|
+
'ConnectorDbConfigMap',
|
|
50
|
+
'ConnectorFileConfigMap',
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# SECTION: TYPE ALIASES ===================================================== #
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Literal type for supported connector kinds
|
|
58
|
+
type ConnectorType = Literal['api', 'database', 'file']
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# SECTION: TYPED DICTS ====================================================== #
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ConnectorApiConfigMap(TypedDict, total=False):
|
|
65
|
+
"""
|
|
66
|
+
Shape accepted by :meth:`ConnectorApi.from_obj` (all keys optional).
|
|
67
|
+
|
|
68
|
+
See Also
|
|
69
|
+
--------
|
|
70
|
+
- :meth:`etlplus.workflow.connector.ConnectorApi.from_obj`
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
name: str
|
|
74
|
+
type: ConnectorType
|
|
75
|
+
url: str
|
|
76
|
+
method: str
|
|
77
|
+
headers: StrAnyMap
|
|
78
|
+
query_params: StrAnyMap
|
|
79
|
+
pagination: PaginationConfigMap
|
|
80
|
+
rate_limit: RateLimitConfigMap
|
|
81
|
+
api: str
|
|
82
|
+
endpoint: str
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ConnectorDbConfigMap(TypedDict, total=False):
|
|
86
|
+
"""
|
|
87
|
+
Shape accepted by :meth:`ConnectorDb.from_obj` (all keys optional).
|
|
88
|
+
|
|
89
|
+
See Also
|
|
90
|
+
--------
|
|
91
|
+
- :meth:`etlplus.workflow.connector.ConnectorDb.from_obj`
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
name: str
|
|
95
|
+
type: ConnectorType
|
|
96
|
+
connection_string: str
|
|
97
|
+
query: str
|
|
98
|
+
table: str
|
|
99
|
+
mode: str
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ConnectorFileConfigMap(TypedDict, total=False):
|
|
103
|
+
"""
|
|
104
|
+
Shape accepted by :meth:`ConnectorFile.from_obj` (all keys optional).
|
|
105
|
+
|
|
106
|
+
See Also
|
|
107
|
+
--------
|
|
108
|
+
- :meth:`etlplus.workflow.connector.ConnectorFile.from_obj`
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
name: str
|
|
112
|
+
type: ConnectorType
|
|
113
|
+
format: str
|
|
114
|
+
path: str
|
|
115
|
+
options: StrAnyMap
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: etlplus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.2
|
|
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
|
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
etlplus/README.md,sha256=
|
|
1
|
+
etlplus/README.md,sha256=JaMSomnMsHrTruDnonHqe83Rv4K0-e7Wy46tMeVoleU,1468
|
|
2
2
|
etlplus/__init__.py,sha256=mgTP4PJmRmsEjTCAizzzdtzAmhuHtarmPzphzdjvLgM,277
|
|
3
3
|
etlplus/__main__.py,sha256=btoROneNiigyfBU7BSzPKZ1R9gzBMpxcpsbPwmuHwTM,479
|
|
4
4
|
etlplus/__version__.py,sha256=1E0GMK_yUWCMQFKxXjTvyMwofi0qT2k4CDNiHWiymWE,327
|
|
5
|
-
etlplus/dag.py,sha256=4EYmBsJax3y4clHv10jjdp3GrBBD_WblvtxUb_JxGCQ,2464
|
|
6
5
|
etlplus/enums.py,sha256=ZxObavNilITJNT4Mf2hBy1s4kKtTFUTmrc8_whkxKNg,7541
|
|
7
6
|
etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
|
|
8
7
|
etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
etlplus/types.py,sha256
|
|
10
|
-
etlplus/utils.py,sha256=
|
|
11
|
-
etlplus/api/README.md,sha256=
|
|
8
|
+
etlplus/types.py,sha256=-knM5bWRq49k4o-KlW-GRsENcj2P-h6oPnEmuz3EJso,6214
|
|
9
|
+
etlplus/utils.py,sha256=JVOBPyjIv1C9EZWZuYdqCISS-Dpa-PHrFvvruu1R0HA,17171
|
|
10
|
+
etlplus/api/README.md,sha256=amxS_eIcsnNuVvD0x_w8nkyfedOTYbhlY0gGhaFg0DE,8705
|
|
12
11
|
etlplus/api/__init__.py,sha256=PK2lQv1FbsE7ZZS_ejevFZQSuOUHGApBc22YfHAzMqA,4615
|
|
13
12
|
etlplus/api/auth.py,sha256=GOO5on-LoMS1GXTAhtK9rFcfpjbBcNeA6NE5UZwIq0g,12158
|
|
14
|
-
etlplus/api/config.py,sha256=
|
|
13
|
+
etlplus/api/config.py,sha256=_8_iXpaHXQN6RQ1z0e3q0LTY936IYsc1SVcE6_sPT6E,17639
|
|
15
14
|
etlplus/api/endpoint_client.py,sha256=OPAvw3NkpSzeITqgRP1reyqX9Nc_XC8lAj6Yp7nV4Tw,30705
|
|
16
15
|
etlplus/api/enums.py,sha256=Tvkru6V8fzQh2JM2FDLcA_yaPENOKz5JgzxLhieqEvc,1141
|
|
17
16
|
etlplus/api/errors.py,sha256=XjI2xW-sypMUNUbqfc2S57-IGyWnH3oCDFhCmKYYI_Q,4648
|
|
18
17
|
etlplus/api/request_manager.py,sha256=fhzPV5x7DqpKqoLvfDR8GKhBX_QBMtvZsRXxVnQQElY,18674
|
|
19
18
|
etlplus/api/retry_manager.py,sha256=0GDhJVyIlb1Ww35JUWlYoa8QYUPjKLBtxQeZj3TdLbY,11306
|
|
20
19
|
etlplus/api/transport.py,sha256=DVWzWFZLfQQXaTJ60w-SzdXRxiOE8llPprpUB5CtSyg,9410
|
|
21
|
-
etlplus/api/types.py,sha256=
|
|
22
|
-
etlplus/api/utils.py,sha256=
|
|
20
|
+
etlplus/api/types.py,sha256=geuKmnf6C2P9_tpRpOF90Uu7J160OL3RQpQ_CgJsSrM,6695
|
|
21
|
+
etlplus/api/utils.py,sha256=U39OXcrPiLD9m0Y03pACXIIOyuuDPO6RRKuTcwEayBE,26249
|
|
23
22
|
etlplus/api/pagination/__init__.py,sha256=a4UX2J0AG8RMvmHt_CCofUm5vSmFo6GAfkb8XnSXypM,1395
|
|
24
23
|
etlplus/api/pagination/client.py,sha256=yMEpWqRxTCD4zRc9OYtEyUtShpGH5atiHFEAt95v2FE,5394
|
|
25
24
|
etlplus/api/pagination/config.py,sha256=3dXDJ-nMbO9Zk6i344n4roBFbUlHsa294D1_plPmm6E,13579
|
|
@@ -27,35 +26,27 @@ etlplus/api/pagination/paginator.py,sha256=wtdY_er4yfjx5yTUQJ1gPq-IuWmpLAHeG5buB
|
|
|
27
26
|
etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-Y_lbU6kI,1070
|
|
28
27
|
etlplus/api/rate_limiting/config.py,sha256=eB-dSOG-c5awYeEpnDsQJ5pO8BJtEpGYxOunBetWiB0,9775
|
|
29
28
|
etlplus/api/rate_limiting/rate_limiter.py,sha256=heAasm1KLeBFmoJKPz6W-eQbNuahMgG1E9-z8zGEUro,7035
|
|
30
|
-
etlplus/cli/README.md,sha256=
|
|
29
|
+
etlplus/cli/README.md,sha256=8H_G2d3HteYIU6ReX9K9DM485QjWDT5vHMQbGD_vv20,1237
|
|
31
30
|
etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
|
|
32
|
-
etlplus/cli/commands.py,sha256=
|
|
31
|
+
etlplus/cli/commands.py,sha256=HFlg29tO6Jwv1NXWAHmvniLCyRSlboL55Arn9B8nZAM,25028
|
|
33
32
|
etlplus/cli/constants.py,sha256=E6Uy4WauLa_0zkzxqImXh-bb1gKdb9sBZQVc8QOzr2Q,1943
|
|
34
|
-
etlplus/cli/handlers.py,sha256=
|
|
33
|
+
etlplus/cli/handlers.py,sha256=hXsgG3c6n8JDN4U3Svq6i2r21vetw5NcmXE1bGSwb9w,18560
|
|
35
34
|
etlplus/cli/io.py,sha256=EFaBPYaBOyOllfMQWXgTjy-MPiGfNejicpD7ROrPyAE,7840
|
|
36
|
-
etlplus/cli/main.py,sha256=
|
|
35
|
+
etlplus/cli/main.py,sha256=d5yhCS6aNo_TKMvhdTS7qeJm4yb1S58gGBrUoBV7SCs,5241
|
|
37
36
|
etlplus/cli/options.py,sha256=vfXT3YLh7wG1iC-aTdSg6ItMC8l6n0Lozmy53XjqLbA,1199
|
|
38
|
-
etlplus/cli/state.py,sha256=
|
|
37
|
+
etlplus/cli/state.py,sha256=3Dq5BKct0uAvRajtc2yHbsX7wqepZOwlAMKsyvQcnqk,7918
|
|
39
38
|
etlplus/cli/types.py,sha256=tclhKVJXDqHzlTQBYKARfqMgDOcuBJ-Zej2pvFy96WM,652
|
|
40
|
-
etlplus/
|
|
41
|
-
etlplus/config/__init__.py,sha256=VZWzOg7d2YR9NT6UwKTv44yf2FRUMjTHynkm1Dl5Qzo,1486
|
|
42
|
-
etlplus/config/connector.py,sha256=0-TIwevHbKRHVmucvyGpPd-3tB1dKHB-dj0yJ6kq5eY,9809
|
|
43
|
-
etlplus/config/jobs.py,sha256=oa2rNwacy2b_1HP_iFDLarGnn812ZVP2k5cHt4eqBIg,7843
|
|
44
|
-
etlplus/config/pipeline.py,sha256=m4Jh0ctFcKrIx6zR7LEC0sYY5wq0o8NqOruWPlz6qmA,9494
|
|
45
|
-
etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
|
|
46
|
-
etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
|
|
47
|
-
etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
|
|
48
|
-
etlplus/database/README.md,sha256=nUQVwLwGzmpA5_u4oYJYkPIhIuJAIdWsoeRgr4joAIE,1431
|
|
39
|
+
etlplus/database/README.md,sha256=3Af5BEGLkBmMoGOLtS1GQuj4wKPh_CwSp5NEPMf2uaY,1435
|
|
49
40
|
etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
|
|
50
41
|
etlplus/database/ddl.py,sha256=0dEM9SJMMabkhI_h-Fc0j9a1Sl5lSyZdI0bIeBVGm10,7913
|
|
51
|
-
etlplus/database/engine.py,sha256=
|
|
52
|
-
etlplus/database/orm.py,sha256=
|
|
42
|
+
etlplus/database/engine.py,sha256=gWgTjGoo1HYHrBuYX5l2P8avAR3XithzAiqUOpLHP-8,4386
|
|
43
|
+
etlplus/database/orm.py,sha256=ZCHkeVEUns2eievlFzmLyVKA3YVPea1xs6vrcUBZ7Jw,10010
|
|
53
44
|
etlplus/database/schema.py,sha256=813C0Dd3WE53KTYot4dgjAxctgKXLXx-8_Rk_4r2e28,7022
|
|
54
45
|
etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
|
|
55
|
-
etlplus/file/README.md,sha256=
|
|
46
|
+
etlplus/file/README.md,sha256=ivU8svVs1fktQiW5ozvh1N-IOSLCAQ3oM9bW8DUFwIw,3630
|
|
56
47
|
etlplus/file/__init__.py,sha256=X03bosSM-uSd6dh3ur0un6_ozFRw2Tm4PE6kVUjtXK8,475
|
|
57
48
|
etlplus/file/_imports.py,sha256=9e8CWjyNIRcmiULEPuwtnJELUOXd4EvVv_vDnDYiB9c,3121
|
|
58
|
-
etlplus/file/_io.py,sha256=
|
|
49
|
+
etlplus/file/_io.py,sha256=2Dj7MH6r-yp_SsCe5GmuNN7Ta70qYzF_jKoMgnD3rME,3925
|
|
59
50
|
etlplus/file/accdb.py,sha256=xdBLrXHDonVJ1Z6-qZRrLBXpUwqw5nZYxDuxYMJHzVs,1681
|
|
60
51
|
etlplus/file/arrow.py,sha256=rYPqoCAI3cBHETYN3c0Xi7R5Nq7_prIdyESm3ll3Zos,1694
|
|
61
52
|
etlplus/file/avro.py,sha256=yFQMnWPcvC0CbDCyagoB9SHRIuvl2SXIoQJTBIlw4dA,4437
|
|
@@ -77,7 +68,7 @@ etlplus/file/hdf5.py,sha256=SZ-UbXTJGOYA82hdma7AFulWo9emH5Kih_RXC7f-Bfk,1624
|
|
|
77
68
|
etlplus/file/ini.py,sha256=HlvQyQC00oBD8KFBfznPjBx9wF1ZwXH7Yo1JaXqCq8I,1701
|
|
78
69
|
etlplus/file/ion.py,sha256=9q938jROTAcuUT5sRYvS1qIeoz9p8KkVWYDDS2-2A_4,1710
|
|
79
70
|
etlplus/file/jinja2.py,sha256=FFqGQjBnRgPQ-oJ3WqiKspJMj6g_J0fDKGwsoOJAEzk,1630
|
|
80
|
-
etlplus/file/json.py,sha256=
|
|
71
|
+
etlplus/file/json.py,sha256=m4eM8cPGHYAEppwLqBdq-t9gC2GtKPLptRTBeBfRimY,2130
|
|
81
72
|
etlplus/file/log.py,sha256=OmsGijXZn1VhN7BUklF8saoktxkmHh1MPLI_BbGDLyc,1681
|
|
82
73
|
etlplus/file/mat.py,sha256=u3jWMK8c4k9L0uVRiQSd7JgVQF-efoJj3QiKYtt1arA,1654
|
|
83
74
|
etlplus/file/mdb.py,sha256=hSCaxBbc_b9dGTBpR-Gb0UTY2r3zYUxoEiKuwpnx0kI,1657
|
|
@@ -112,24 +103,32 @@ etlplus/file/xlsm.py,sha256=6meoukAvpEIm_mvVomIo2oefPYfmHHXfE1XmFfeFpvY,1738
|
|
|
112
103
|
etlplus/file/xlsx.py,sha256=vtiAS8Ng9FV1vCWYTd1YO2ORKIJG3HDfmqy3NkVpt0A,2182
|
|
113
104
|
etlplus/file/xml.py,sha256=p5P60DiV6hyiz-t970d0d0ZXB41gVvAm3MT7dTMULa8,4360
|
|
114
105
|
etlplus/file/xpt.py,sha256=JLqOkZ60awNsPXSqLKcPUwqZLPhPR05zk4EVRdEfvoU,1702
|
|
115
|
-
etlplus/file/yaml.py,sha256=
|
|
106
|
+
etlplus/file/yaml.py,sha256=P9xzginPVVyvIPbFAp6MYWwARhK50GzgMbjZB7HwOrs,2052
|
|
116
107
|
etlplus/file/zip.py,sha256=nd26V3S0edklriKnKOGDTLlO8RBXTda_zLLEQrJgKL4,4185
|
|
117
108
|
etlplus/file/zsav.py,sha256=2WxjXamvzj0adDbWGMWM3-cj_LsCRjyZz4J907lNkPk,1664
|
|
118
109
|
etlplus/ops/README.md,sha256=8omi7DYZhelc26JKk8Cm8QR8I3OGwziysPj1ivx41iQ,1380
|
|
119
110
|
etlplus/ops/__init__.py,sha256=NIIr2f-AZj5B0piBt6gjv46Yn0SzGYxEe6BPoopRh38,1702
|
|
120
111
|
etlplus/ops/extract.py,sha256=OJozX25qTjME7m9aTdVPJScT3GHHgopGM8uHo_rHm1Y,5783
|
|
121
112
|
etlplus/ops/load.py,sha256=RgEKnVygRN2cPDpzihU8UsIhd4eVoj0cPe0jBuNC0u4,8328
|
|
122
|
-
etlplus/ops/run.py,sha256=
|
|
113
|
+
etlplus/ops/run.py,sha256=JOPQxrciRUQ67FJhUcZ-pW1aiYGZUdxHLwhzWLNCaDo,13528
|
|
123
114
|
etlplus/ops/transform.py,sha256=1P43WYUaw872JDU86FhbbbkP8xnBWpgEPn2Q-z4ywls,25421
|
|
124
|
-
etlplus/ops/utils.py,sha256=
|
|
125
|
-
etlplus/ops/validate.py,sha256
|
|
126
|
-
etlplus/templates/README.md,sha256=
|
|
115
|
+
etlplus/ops/utils.py,sha256=mNUcbnLl3M6rA_BPoUqG-IFPqT1zrp_EkQTs7uvzBIQ,10839
|
|
116
|
+
etlplus/ops/validate.py,sha256=-OLAwQNNCmmDbmj0SB7zzYXDkJfcyBP_z9nTpqImLP0,13271
|
|
117
|
+
etlplus/templates/README.md,sha256=IfPXlj1TGVA-uFWosHJhE2rabFW-znxOlOMazO9Z5cE,1361
|
|
127
118
|
etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
|
|
128
119
|
etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
|
|
129
120
|
etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
|
|
130
|
-
etlplus
|
|
131
|
-
etlplus
|
|
132
|
-
etlplus
|
|
133
|
-
etlplus
|
|
134
|
-
etlplus
|
|
135
|
-
etlplus
|
|
121
|
+
etlplus/workflow/README.md,sha256=D1oloiJCOHiqpqgv3m3qpRSIUOMIQcWtIsOPv7KkNI0,1652
|
|
122
|
+
etlplus/workflow/__init__.py,sha256=LxI9VGlDBUc9ADoK8JNn3oVsGeaz1Uhjonn4Y5KIJdM,967
|
|
123
|
+
etlplus/workflow/connector.py,sha256=vnzq9-qcU4ThWeI3ZJcL8iCoaKWrSEtZ9Jixn4il2KE,9995
|
|
124
|
+
etlplus/workflow/dag.py,sha256=kp31dORgk0GHbct_bipU5hu_0elwBtwLsXGjMWuhFHI,2503
|
|
125
|
+
etlplus/workflow/jobs.py,sha256=5_jhPoT2hCGTgsbK_lIPKrB94f7tzOEUJxtU3tpvTjg,8947
|
|
126
|
+
etlplus/workflow/pipeline.py,sha256=eF6OYMXZ_YdDPZ5FJC6OTCAM-zsb8HxhX3IHpbb_N8w,9631
|
|
127
|
+
etlplus/workflow/profile.py,sha256=dZ6P50k_ZqXnrbgrbODUqgVkymbchcEqfZR-ExjTd3M,1935
|
|
128
|
+
etlplus/workflow/types.py,sha256=Lv1MLyM45Ob5AsMQQBqzbUaD8g7HMVxu_HoQGPeiugU,2771
|
|
129
|
+
etlplus-0.15.2.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
|
|
130
|
+
etlplus-0.15.2.dist-info/METADATA,sha256=Vu5cWh8OqcpKUfvMur07I7k5QlUIIFgs5tzmKv2eGG4,28114
|
|
131
|
+
etlplus-0.15.2.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
132
|
+
etlplus-0.15.2.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
|
|
133
|
+
etlplus-0.15.2.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
|
|
134
|
+
etlplus-0.15.2.dist-info/RECORD,,
|
etlplus/config/types.py
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
:mod:`etlplus.config.types` module.
|
|
3
|
-
|
|
4
|
-
Type aliases and editor-only TypedDicts for :mod:`etlplus.config`.
|
|
5
|
-
|
|
6
|
-
These types improve IDE autocomplete and static analysis while the runtime
|
|
7
|
-
parsers remain permissive.
|
|
8
|
-
|
|
9
|
-
Notes
|
|
10
|
-
-----
|
|
11
|
-
- TypedDicts in this module are intentionally ``total=False`` and are not
|
|
12
|
-
enforced at runtime.
|
|
13
|
-
- ``*.from_obj`` constructors accept ``Mapping[str, Any]`` and perform
|
|
14
|
-
tolerant parsing and light casting. This keeps the runtime permissive while
|
|
15
|
-
improving autocomplete and static analysis for contributors.
|
|
16
|
-
|
|
17
|
-
Examples
|
|
18
|
-
--------
|
|
19
|
-
>>> from etlplus.config import Connector
|
|
20
|
-
>>> src: Connector = {
|
|
21
|
-
>>> "type": "file",
|
|
22
|
-
>>> "path": "/data/input.csv",
|
|
23
|
-
>>> }
|
|
24
|
-
>>> tgt: Connector = {
|
|
25
|
-
>>> "type": "database",
|
|
26
|
-
>>> "connection_string": "postgresql://user:pass@localhost/db",
|
|
27
|
-
>>> }
|
|
28
|
-
>>> from etlplus.api import RetryPolicy
|
|
29
|
-
>>> rp: RetryPolicy = {"max_attempts": 3, "backoff": 0.5}
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
from __future__ import annotations
|
|
33
|
-
|
|
34
|
-
from collections.abc import Mapping
|
|
35
|
-
from typing import Any
|
|
36
|
-
from typing import Literal
|
|
37
|
-
from typing import TypedDict
|
|
38
|
-
|
|
39
|
-
from ..api import PaginationConfigMap
|
|
40
|
-
from ..api import RateLimitConfigMap
|
|
41
|
-
from ..types import StrAnyMap
|
|
42
|
-
|
|
43
|
-
# SECTION: EXPORTS ========================================================= #
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
__all__ = [
|
|
47
|
-
# Type aliases
|
|
48
|
-
'ConnectorType',
|
|
49
|
-
# 'PaginationType',
|
|
50
|
-
# TypedDicts
|
|
51
|
-
'ApiProfileDefaultsMap',
|
|
52
|
-
'ApiProfileConfigMap',
|
|
53
|
-
'ApiConfigMap',
|
|
54
|
-
'EndpointMap',
|
|
55
|
-
'ConnectorApiConfigMap',
|
|
56
|
-
'ConnectorDbConfigMap',
|
|
57
|
-
'ConnectorFileConfigMap',
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# SECTION: TYPE ALIASES ===================================================== #
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# Literal type for supported connector kinds
|
|
65
|
-
type ConnectorType = Literal['api', 'database', 'file']
|
|
66
|
-
|
|
67
|
-
# Literal type for supported pagination kinds
|
|
68
|
-
# type PaginationType = Literal['page', 'offset', 'cursor']
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# SECTION: TYPED DICTS ====================================================== #
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class ApiConfigMap(TypedDict, total=False):
|
|
75
|
-
"""
|
|
76
|
-
Top-level API config shape parsed by ApiConfig.from_obj.
|
|
77
|
-
|
|
78
|
-
Either provide a 'base_url' with optional 'headers' and 'endpoints', or
|
|
79
|
-
provide 'profiles' with at least one profile having a 'base_url'.
|
|
80
|
-
|
|
81
|
-
See Also
|
|
82
|
-
--------
|
|
83
|
-
- etlplus.config.api.ApiConfig.from_obj: parses this mapping
|
|
84
|
-
"""
|
|
85
|
-
|
|
86
|
-
base_url: str
|
|
87
|
-
headers: StrAnyMap
|
|
88
|
-
endpoints: Mapping[str, EndpointMap | str]
|
|
89
|
-
profiles: Mapping[str, ApiProfileConfigMap]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
class ApiProfileConfigMap(TypedDict, total=False):
|
|
93
|
-
"""
|
|
94
|
-
Shape accepted for a profile entry under ApiConfigMap.profiles.
|
|
95
|
-
|
|
96
|
-
Notes
|
|
97
|
-
-----
|
|
98
|
-
`base_url` is required at runtime when profiles are provided.
|
|
99
|
-
|
|
100
|
-
See Also
|
|
101
|
-
--------
|
|
102
|
-
- etlplus.config.api.ApiProfileConfig.from_obj: parses this mapping
|
|
103
|
-
"""
|
|
104
|
-
|
|
105
|
-
base_url: str
|
|
106
|
-
headers: StrAnyMap
|
|
107
|
-
base_path: str
|
|
108
|
-
auth: StrAnyMap
|
|
109
|
-
defaults: ApiProfileDefaultsMap
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class ApiProfileDefaultsMap(TypedDict, total=False):
|
|
113
|
-
"""
|
|
114
|
-
Defaults block available under a profile (all keys optional).
|
|
115
|
-
|
|
116
|
-
Notes
|
|
117
|
-
-----
|
|
118
|
-
Runtime expects header values to be str; typing remains permissive.
|
|
119
|
-
|
|
120
|
-
See Also
|
|
121
|
-
--------
|
|
122
|
-
- etlplus.config.api.ApiProfileConfig.from_obj: consumes this block
|
|
123
|
-
- etlplus.config.pagination.PaginationConfig.from_obj: parses pagination
|
|
124
|
-
- etlplus.api.rate_limiting.RateLimitConfig.from_obj: parses rate_limit
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
headers: StrAnyMap
|
|
128
|
-
pagination: PaginationConfigMap | StrAnyMap
|
|
129
|
-
rate_limit: RateLimitConfigMap | StrAnyMap
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class ConnectorApiConfigMap(TypedDict, total=False):
|
|
133
|
-
"""
|
|
134
|
-
Shape accepted by ConnectorApi.from_obj (all keys optional).
|
|
135
|
-
|
|
136
|
-
See Also
|
|
137
|
-
--------
|
|
138
|
-
- etlplus.config.connector.ConnectorApi.from_obj
|
|
139
|
-
"""
|
|
140
|
-
|
|
141
|
-
name: str
|
|
142
|
-
type: ConnectorType
|
|
143
|
-
url: str
|
|
144
|
-
method: str
|
|
145
|
-
headers: StrAnyMap
|
|
146
|
-
query_params: StrAnyMap
|
|
147
|
-
pagination: PaginationConfigMap
|
|
148
|
-
rate_limit: RateLimitConfigMap
|
|
149
|
-
api: str
|
|
150
|
-
endpoint: str
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class ConnectorDbConfigMap(TypedDict, total=False):
|
|
154
|
-
"""
|
|
155
|
-
Shape accepted by ConnectorDb.from_obj (all keys optional).
|
|
156
|
-
|
|
157
|
-
See Also
|
|
158
|
-
--------
|
|
159
|
-
- etlplus.config.connector.ConnectorDb.from_obj
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
name: str
|
|
163
|
-
type: ConnectorType
|
|
164
|
-
connection_string: str
|
|
165
|
-
query: str
|
|
166
|
-
table: str
|
|
167
|
-
mode: str
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class ConnectorFileConfigMap(TypedDict, total=False):
|
|
171
|
-
"""
|
|
172
|
-
Shape accepted by ConnectorFile.from_obj (all keys optional).
|
|
173
|
-
|
|
174
|
-
See Also
|
|
175
|
-
--------
|
|
176
|
-
- etlplus.config.connector.ConnectorFile.from_obj
|
|
177
|
-
"""
|
|
178
|
-
|
|
179
|
-
name: str
|
|
180
|
-
type: ConnectorType
|
|
181
|
-
format: str
|
|
182
|
-
path: str
|
|
183
|
-
options: StrAnyMap
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
class EndpointMap(TypedDict, total=False):
|
|
187
|
-
"""
|
|
188
|
-
Shape accepted by EndpointConfig.from_obj.
|
|
189
|
-
|
|
190
|
-
One of 'path' or 'url' should be provided.
|
|
191
|
-
|
|
192
|
-
See Also
|
|
193
|
-
--------
|
|
194
|
-
- etlplus.config.api.EndpointConfig.from_obj: parses this mapping
|
|
195
|
-
"""
|
|
196
|
-
|
|
197
|
-
path: str
|
|
198
|
-
url: str
|
|
199
|
-
method: str
|
|
200
|
-
path_params: StrAnyMap
|
|
201
|
-
query_params: StrAnyMap
|
|
202
|
-
body: Any
|
|
203
|
-
pagination: PaginationConfigMap
|
|
204
|
-
rate_limit: RateLimitConfigMap
|
etlplus/config/utils.py
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
:mod:`etlplus.config.utils` module.
|
|
3
|
-
|
|
4
|
-
A module defining utility helpers for ETL pipeline configuration.
|
|
5
|
-
|
|
6
|
-
Notes
|
|
7
|
-
-----
|
|
8
|
-
- Inputs to parsers favor ``Mapping[str, Any]`` to remain permissive and
|
|
9
|
-
avoid unnecessary copies; normalization returns concrete types.
|
|
10
|
-
- Substitution is shallow for strings and recursive for containers.
|
|
11
|
-
- Numeric coercion helpers are intentionally forgiving: invalid values
|
|
12
|
-
become ``None`` rather than raising.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
from collections.abc import Iterable
|
|
18
|
-
from collections.abc import Mapping
|
|
19
|
-
from typing import Any
|
|
20
|
-
|
|
21
|
-
from ..types import StrAnyMap
|
|
22
|
-
|
|
23
|
-
# SECTION: EXPORTS ========================================================== #
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
__all__ = [
|
|
27
|
-
# Functions
|
|
28
|
-
'deep_substitute',
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# SECTION: FUNCTIONS ======================================================== #
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def deep_substitute(
|
|
36
|
-
value: Any,
|
|
37
|
-
vars_map: StrAnyMap | None,
|
|
38
|
-
env_map: Mapping[str, str] | None,
|
|
39
|
-
) -> Any:
|
|
40
|
-
"""
|
|
41
|
-
Recursively substitute ``${VAR}`` tokens in nested structures.
|
|
42
|
-
|
|
43
|
-
Only strings are substituted; other types are returned as-is.
|
|
44
|
-
|
|
45
|
-
Parameters
|
|
46
|
-
----------
|
|
47
|
-
value : Any
|
|
48
|
-
The value to perform substitutions on.
|
|
49
|
-
vars_map : StrAnyMap | None
|
|
50
|
-
Mapping of variable names to replacement values (lower precedence).
|
|
51
|
-
env_map : Mapping[str, str] | None
|
|
52
|
-
Mapping of environment variables overriding ``vars_map`` values (higher
|
|
53
|
-
precedence).
|
|
54
|
-
|
|
55
|
-
Returns
|
|
56
|
-
-------
|
|
57
|
-
Any
|
|
58
|
-
New structure with substitutions applied where tokens were found.
|
|
59
|
-
"""
|
|
60
|
-
substitutions = _prepare_substitutions(vars_map, env_map)
|
|
61
|
-
|
|
62
|
-
def _apply(node: Any) -> Any:
|
|
63
|
-
match node:
|
|
64
|
-
case str():
|
|
65
|
-
return _replace_tokens(node, substitutions)
|
|
66
|
-
case Mapping():
|
|
67
|
-
return {k: _apply(v) for k, v in node.items()}
|
|
68
|
-
case list() | tuple() as seq:
|
|
69
|
-
apply = [_apply(item) for item in seq]
|
|
70
|
-
return apply if isinstance(seq, list) else tuple(apply)
|
|
71
|
-
case set():
|
|
72
|
-
return {_apply(item) for item in node}
|
|
73
|
-
case frozenset():
|
|
74
|
-
return frozenset(_apply(item) for item in node)
|
|
75
|
-
case _:
|
|
76
|
-
return node
|
|
77
|
-
|
|
78
|
-
return _apply(value)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
# SECTION: INTERNAL FUNCTIONS ============================================== #
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def _prepare_substitutions(
|
|
85
|
-
vars_map: StrAnyMap | None,
|
|
86
|
-
env_map: Mapping[str, Any] | None,
|
|
87
|
-
) -> tuple[tuple[str, Any], ...]:
|
|
88
|
-
"""Merge variable and environment maps into an ordered substitutions list.
|
|
89
|
-
|
|
90
|
-
Parameters
|
|
91
|
-
----------
|
|
92
|
-
vars_map : StrAnyMap | None
|
|
93
|
-
Mapping of variable names to replacement values (lower precedence).
|
|
94
|
-
env_map : Mapping[str, Any] | None
|
|
95
|
-
Environment-backed values that override entries from ``vars_map``.
|
|
96
|
-
|
|
97
|
-
Returns
|
|
98
|
-
-------
|
|
99
|
-
tuple[tuple[str, Any], ...]
|
|
100
|
-
Immutable sequence of ``(name, value)`` pairs suitable for token
|
|
101
|
-
replacement.
|
|
102
|
-
"""
|
|
103
|
-
if not vars_map and not env_map:
|
|
104
|
-
return ()
|
|
105
|
-
merged: dict[str, Any] = {**(vars_map or {}), **(env_map or {})}
|
|
106
|
-
return tuple(merged.items())
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def _replace_tokens(
|
|
110
|
-
text: str,
|
|
111
|
-
substitutions: Iterable[tuple[str, Any]],
|
|
112
|
-
) -> str:
|
|
113
|
-
if not substitutions:
|
|
114
|
-
return text
|
|
115
|
-
out = text
|
|
116
|
-
for name, replacement in substitutions:
|
|
117
|
-
token = f'${{{name}}}'
|
|
118
|
-
if token in out:
|
|
119
|
-
out = out.replace(token, str(replacement))
|
|
120
|
-
return out
|
|
File without changes
|
|
File without changes
|
|
File without changes
|