etlplus 0.16.0__py3-none-any.whl → 0.16.7__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 +24 -2
- etlplus/__init__.py +2 -0
- etlplus/api/__init__.py +14 -14
- etlplus/api/auth.py +9 -6
- etlplus/api/config.py +6 -6
- etlplus/api/endpoint_client.py +16 -16
- etlplus/api/enums.py +2 -2
- etlplus/api/errors.py +4 -4
- etlplus/api/pagination/__init__.py +6 -6
- etlplus/api/pagination/config.py +11 -9
- etlplus/api/rate_limiting/__init__.py +2 -2
- etlplus/api/rate_limiting/config.py +10 -10
- etlplus/api/rate_limiting/rate_limiter.py +2 -2
- etlplus/api/request_manager.py +4 -4
- etlplus/api/retry_manager.py +6 -6
- etlplus/api/transport.py +10 -10
- etlplus/api/types.py +47 -26
- etlplus/api/utils.py +49 -49
- etlplus/cli/README.md +9 -7
- etlplus/cli/commands.py +22 -22
- etlplus/cli/handlers.py +12 -13
- etlplus/cli/main.py +1 -1
- etlplus/{workflow/pipeline.py → config.py} +54 -91
- etlplus/connector/__init__.py +6 -6
- etlplus/connector/api.py +7 -7
- etlplus/connector/database.py +3 -3
- etlplus/connector/file.py +3 -3
- etlplus/connector/types.py +2 -2
- etlplus/database/README.md +7 -7
- etlplus/enums.py +35 -167
- etlplus/file/README.md +7 -5
- etlplus/file/accdb.py +2 -1
- etlplus/file/arrow.py +2 -1
- etlplus/file/bson.py +2 -1
- etlplus/file/cbor.py +2 -1
- etlplus/file/cfg.py +1 -1
- etlplus/file/conf.py +1 -1
- etlplus/file/dat.py +1 -1
- etlplus/file/dta.py +1 -1
- etlplus/file/duckdb.py +2 -1
- etlplus/file/enums.py +1 -1
- etlplus/file/fwf.py +2 -1
- etlplus/file/hbs.py +2 -1
- etlplus/file/hdf5.py +2 -1
- etlplus/file/ini.py +2 -1
- etlplus/file/ion.py +1 -1
- etlplus/file/jinja2.py +2 -1
- etlplus/file/log.py +1 -1
- etlplus/file/mat.py +1 -1
- etlplus/file/mdb.py +2 -1
- etlplus/file/msgpack.py +2 -1
- etlplus/file/mustache.py +2 -1
- etlplus/file/nc.py +1 -1
- etlplus/file/numbers.py +2 -1
- etlplus/file/ods.py +2 -1
- etlplus/file/pb.py +2 -1
- etlplus/file/pbf.py +2 -1
- etlplus/file/properties.py +2 -1
- etlplus/file/proto.py +2 -1
- etlplus/file/psv.py +2 -1
- etlplus/file/rda.py +2 -1
- etlplus/file/rds.py +1 -1
- etlplus/file/sas7bdat.py +2 -1
- etlplus/file/sav.py +1 -1
- etlplus/file/sqlite.py +2 -1
- etlplus/file/sylk.py +2 -1
- etlplus/file/tab.py +2 -1
- etlplus/file/toml.py +2 -1
- etlplus/file/vm.py +2 -1
- etlplus/file/wks.py +2 -1
- etlplus/file/xls.py +1 -1
- etlplus/file/xlsm.py +2 -2
- etlplus/file/xpt.py +2 -1
- etlplus/file/zsav.py +2 -1
- etlplus/ops/README.md +10 -9
- etlplus/ops/__init__.py +1 -0
- etlplus/ops/enums.py +173 -0
- etlplus/ops/extract.py +209 -22
- etlplus/ops/load.py +140 -34
- etlplus/ops/run.py +88 -103
- etlplus/ops/transform.py +46 -27
- etlplus/ops/types.py +147 -0
- etlplus/ops/utils.py +5 -5
- etlplus/ops/validate.py +13 -13
- etlplus/templates/README.md +11 -9
- etlplus/types.py +5 -102
- etlplus/workflow/README.md +0 -24
- etlplus/workflow/__init__.py +2 -4
- etlplus/workflow/dag.py +23 -1
- etlplus/workflow/jobs.py +15 -28
- etlplus/workflow/profile.py +4 -2
- {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/METADATA +32 -28
- etlplus-0.16.7.dist-info/RECORD +143 -0
- etlplus-0.16.0.dist-info/RECORD +0 -141
- {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/WHEEL +0 -0
- {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/entry_points.txt +0 -0
- {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/top_level.txt +0 -0
etlplus/api/transport.py
CHANGED
|
@@ -41,8 +41,8 @@ from ..utils import to_positive_int
|
|
|
41
41
|
|
|
42
42
|
__all__ = [
|
|
43
43
|
# Classes
|
|
44
|
-
'
|
|
45
|
-
'
|
|
44
|
+
'HTTPAdapterMountConfigDict',
|
|
45
|
+
'HTTPAdapterRetryConfigDict',
|
|
46
46
|
# Functions
|
|
47
47
|
'build_http_adapter',
|
|
48
48
|
'build_session_with_adapters',
|
|
@@ -52,7 +52,7 @@ __all__ = [
|
|
|
52
52
|
# SECTION: TYPED DICTS ====================================================== #
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
class
|
|
55
|
+
class HTTPAdapterRetryConfigDict(TypedDict, total=False):
|
|
56
56
|
"""
|
|
57
57
|
Retry configuration for urllib3 ``Retry``.
|
|
58
58
|
|
|
@@ -89,7 +89,7 @@ class HTTPAdapterRetryConfig(TypedDict, total=False):
|
|
|
89
89
|
|
|
90
90
|
Examples
|
|
91
91
|
--------
|
|
92
|
-
>>> retry_cfg:
|
|
92
|
+
>>> retry_cfg: HTTPAdapterRetryConfigDict = {
|
|
93
93
|
... 'total': 5,
|
|
94
94
|
... 'backoff_factor': 0.5,
|
|
95
95
|
... 'status_forcelist': [429, 503],
|
|
@@ -111,7 +111,7 @@ class HTTPAdapterRetryConfig(TypedDict, total=False):
|
|
|
111
111
|
respect_retry_after_header: bool
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
class
|
|
114
|
+
class HTTPAdapterMountConfigDict(TypedDict, total=False):
|
|
115
115
|
"""
|
|
116
116
|
Configuration mapping for mounting an ``HTTPAdapter`` on a ``Session``.
|
|
117
117
|
|
|
@@ -132,13 +132,13 @@ class HTTPAdapterMountConfig(TypedDict, total=False):
|
|
|
132
132
|
pool_block : bool
|
|
133
133
|
Whether the pool should block for connections instead of creating new
|
|
134
134
|
ones.
|
|
135
|
-
max_retries : int |
|
|
135
|
+
max_retries : int | HTTPAdapterRetryConfigDict
|
|
136
136
|
Retry configuration passed to ``HTTPAdapter`` (int) or converted to
|
|
137
137
|
``Retry``.
|
|
138
138
|
|
|
139
139
|
Examples
|
|
140
140
|
--------
|
|
141
|
-
>>> adapter_cfg:
|
|
141
|
+
>>> adapter_cfg: HTTPAdapterMountConfigDict = {
|
|
142
142
|
... 'prefix': 'https://',
|
|
143
143
|
... 'pool_connections': 10,
|
|
144
144
|
... 'pool_maxsize': 10,
|
|
@@ -156,7 +156,7 @@ class HTTPAdapterMountConfig(TypedDict, total=False):
|
|
|
156
156
|
pool_connections: int
|
|
157
157
|
pool_maxsize: int
|
|
158
158
|
pool_block: bool
|
|
159
|
-
max_retries: int |
|
|
159
|
+
max_retries: int | HTTPAdapterRetryConfigDict
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
# SECTION: INTERNAL FUNCTIONS ============================================== #
|
|
@@ -306,7 +306,7 @@ def build_http_adapter(
|
|
|
306
306
|
|
|
307
307
|
|
|
308
308
|
def build_session_with_adapters(
|
|
309
|
-
adapters_cfg: Sequence[
|
|
309
|
+
adapters_cfg: Sequence[HTTPAdapterMountConfigDict],
|
|
310
310
|
) -> requests.Session:
|
|
311
311
|
"""
|
|
312
312
|
Mount adapters described by *adapters_cfg* onto a new session.
|
|
@@ -316,7 +316,7 @@ def build_session_with_adapters(
|
|
|
316
316
|
|
|
317
317
|
Parameters
|
|
318
318
|
----------
|
|
319
|
-
adapters_cfg : Sequence[
|
|
319
|
+
adapters_cfg : Sequence[HTTPAdapterMountConfigDict]
|
|
320
320
|
Configuration mappings describing the adapter prefix, pooling
|
|
321
321
|
values, and retry policy for each mounted adapter.
|
|
322
322
|
|
etlplus/api/types.py
CHANGED
|
@@ -43,23 +43,47 @@ __all__ = [
|
|
|
43
43
|
'Params',
|
|
44
44
|
'Url',
|
|
45
45
|
# Typed Dicts
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
46
|
+
'ApiConfigDict',
|
|
47
|
+
'ApiProfileConfigDict',
|
|
48
|
+
'ApiProfileDefaultsDict',
|
|
49
|
+
'EndpointConfigDict',
|
|
50
50
|
]
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
# SECTION: CONSTANTS ======================================================== #
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
_UNSET = object()
|
|
56
|
+
_UNSET: object = object()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _to_dict(
|
|
63
|
+
value: Mapping[str, Any] | object | None,
|
|
64
|
+
) -> dict[str, Any] | None:
|
|
65
|
+
"""
|
|
66
|
+
Return a defensive ``dict`` copy for mapping inputs.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
value : Mapping[str, Any] | object | None
|
|
71
|
+
Mapping to copy, or ``None``.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
dict[str, Any] | None
|
|
76
|
+
New ``dict`` instance or ``None`` when the input is ``None``.
|
|
77
|
+
"""
|
|
78
|
+
if value is None:
|
|
79
|
+
return None
|
|
80
|
+
return cast(dict[str, Any], value)
|
|
57
81
|
|
|
58
82
|
|
|
59
83
|
# SECTION: TYPED DICTS ====================================================== #
|
|
60
84
|
|
|
61
85
|
|
|
62
|
-
class
|
|
86
|
+
class ApiConfigDict(TypedDict, total=False):
|
|
63
87
|
"""
|
|
64
88
|
Top-level API config shape parsed by
|
|
65
89
|
:meth:`etlplus.api.config.ApiConfig.from_obj`.
|
|
@@ -75,11 +99,11 @@ class ApiConfigMap(TypedDict, total=False):
|
|
|
75
99
|
|
|
76
100
|
base_url: str
|
|
77
101
|
headers: StrAnyMap
|
|
78
|
-
endpoints: Mapping[str,
|
|
79
|
-
profiles: Mapping[str,
|
|
102
|
+
endpoints: Mapping[str, EndpointConfigDict | str]
|
|
103
|
+
profiles: Mapping[str, ApiProfileConfigDict]
|
|
80
104
|
|
|
81
105
|
|
|
82
|
-
class
|
|
106
|
+
class ApiProfileConfigDict(TypedDict, total=False):
|
|
83
107
|
"""
|
|
84
108
|
Shape accepted for a profile entry under
|
|
85
109
|
:meth:`etlplus.api.config.ApiConfig.from_obj`.
|
|
@@ -99,10 +123,10 @@ class ApiProfileConfigMap(TypedDict, total=False):
|
|
|
99
123
|
headers: StrAnyMap
|
|
100
124
|
base_path: str
|
|
101
125
|
auth: StrAnyMap
|
|
102
|
-
defaults:
|
|
126
|
+
defaults: ApiProfileDefaultsDict
|
|
103
127
|
|
|
104
128
|
|
|
105
|
-
class
|
|
129
|
+
class ApiProfileDefaultsDict(TypedDict, total=False):
|
|
106
130
|
"""
|
|
107
131
|
Defaults block available under a profile (all keys optional).
|
|
108
132
|
|
|
@@ -123,11 +147,11 @@ class ApiProfileDefaultsMap(TypedDict, total=False):
|
|
|
123
147
|
"""
|
|
124
148
|
|
|
125
149
|
headers: StrAnyMap
|
|
126
|
-
pagination: StrAnyMap #
|
|
127
|
-
rate_limit: StrAnyMap #
|
|
150
|
+
pagination: StrAnyMap # PaginationConfigDict | StrAnyMap
|
|
151
|
+
rate_limit: StrAnyMap # RateLimitConfigDict | StrAnyMap
|
|
128
152
|
|
|
129
153
|
|
|
130
|
-
class
|
|
154
|
+
class EndpointConfigDict(TypedDict, total=False):
|
|
131
155
|
"""
|
|
132
156
|
Shape accepted by :meth:`etlplus.api.config.EndpointConfig.from_obj`.
|
|
133
157
|
|
|
@@ -144,8 +168,8 @@ class EndpointMap(TypedDict, total=False):
|
|
|
144
168
|
path_params: StrAnyMap
|
|
145
169
|
query_params: StrAnyMap
|
|
146
170
|
body: Any
|
|
147
|
-
pagination: StrAnyMap #
|
|
148
|
-
rate_limit: StrAnyMap #
|
|
171
|
+
pagination: StrAnyMap # PaginationConfigDict | StrAnyMap
|
|
172
|
+
rate_limit: StrAnyMap # RateLimitConfigDict | StrAnyMap
|
|
149
173
|
|
|
150
174
|
|
|
151
175
|
# SECTION: DATA CLASSES ===================================================== #
|
|
@@ -176,9 +200,9 @@ class RequestOptions:
|
|
|
176
200
|
|
|
177
201
|
def __post_init__(self) -> None:
|
|
178
202
|
if self.params is not None:
|
|
179
|
-
object.__setattr__(self, 'params',
|
|
203
|
+
object.__setattr__(self, 'params', _to_dict(self.params))
|
|
180
204
|
if self.headers is not None:
|
|
181
|
-
object.__setattr__(self, 'headers',
|
|
205
|
+
object.__setattr__(self, 'headers', _to_dict(self.headers))
|
|
182
206
|
|
|
183
207
|
# -- Instance Methods -- #
|
|
184
208
|
|
|
@@ -224,23 +248,20 @@ class RequestOptions:
|
|
|
224
248
|
|
|
225
249
|
Returns
|
|
226
250
|
-------
|
|
227
|
-
|
|
251
|
+
Self
|
|
228
252
|
New snapshot reflecting the provided overrides.
|
|
229
253
|
"""
|
|
230
254
|
if params is _UNSET:
|
|
231
255
|
next_params = self.params
|
|
232
|
-
elif params is None:
|
|
233
|
-
next_params = None
|
|
234
256
|
else:
|
|
235
|
-
next_params =
|
|
257
|
+
# next_params = _to_dict(params) if params is not None else None
|
|
258
|
+
next_params = _to_dict(params)
|
|
236
259
|
|
|
237
260
|
if headers is _UNSET:
|
|
238
261
|
next_headers = self.headers
|
|
239
|
-
elif headers is None:
|
|
240
|
-
next_headers = None
|
|
241
262
|
else:
|
|
242
|
-
next_headers =
|
|
243
|
-
|
|
263
|
+
# next_headers = _to_dict(headers) if headers is not None else None
|
|
264
|
+
next_headers = _to_dict(headers)
|
|
244
265
|
if timeout is _UNSET:
|
|
245
266
|
next_timeout = self.timeout
|
|
246
267
|
else:
|
etlplus/api/utils.py
CHANGED
|
@@ -22,11 +22,11 @@ from .config import EndpointConfig
|
|
|
22
22
|
from .endpoint_client import EndpointClient
|
|
23
23
|
from .enums import HttpMethod
|
|
24
24
|
from .pagination import PaginationConfig
|
|
25
|
-
from .pagination import
|
|
25
|
+
from .pagination import PaginationConfigDict
|
|
26
26
|
from .rate_limiting import RateLimitConfig
|
|
27
|
-
from .rate_limiting import
|
|
27
|
+
from .rate_limiting import RateLimitConfigDict
|
|
28
28
|
from .rate_limiting import RateLimiter
|
|
29
|
-
from .retry_manager import
|
|
29
|
+
from .retry_manager import RetryPolicyDict
|
|
30
30
|
from .types import Headers
|
|
31
31
|
from .types import Params
|
|
32
32
|
from .types import Url
|
|
@@ -53,16 +53,16 @@ __all__ = [
|
|
|
53
53
|
'paginate_with_client',
|
|
54
54
|
'resolve_request',
|
|
55
55
|
# Typed Dicts
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
56
|
+
'ApiRequestEnvDict',
|
|
57
|
+
'ApiTargetEnvDict',
|
|
58
|
+
'SessionConfigDict',
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
# SECTION: TYPED DICTS ====================================================== #
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
class
|
|
65
|
+
class BaseApiHttpEnvDict(TypedDict, total=False):
|
|
66
66
|
"""
|
|
67
67
|
Common HTTP request environment for API interactions.
|
|
68
68
|
|
|
@@ -78,7 +78,7 @@ class BaseApiHttpEnv(TypedDict, total=False):
|
|
|
78
78
|
session: requests.Session | None
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
class
|
|
81
|
+
class ApiRequestEnvDict(BaseApiHttpEnvDict, total=False):
|
|
82
82
|
"""
|
|
83
83
|
Composed HTTP request environment configuration for REST API sources.
|
|
84
84
|
|
|
@@ -96,15 +96,15 @@ class ApiRequestEnv(BaseApiHttpEnv, total=False):
|
|
|
96
96
|
|
|
97
97
|
# Request
|
|
98
98
|
params: dict[str, Any]
|
|
99
|
-
pagination:
|
|
99
|
+
pagination: PaginationConfigDict | None
|
|
100
100
|
sleep_seconds: float
|
|
101
101
|
|
|
102
102
|
# Reliability
|
|
103
|
-
retry:
|
|
103
|
+
retry: RetryPolicyDict | None
|
|
104
104
|
retry_network_errors: bool
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
class
|
|
107
|
+
class ApiTargetEnvDict(BaseApiHttpEnvDict, total=False):
|
|
108
108
|
"""
|
|
109
109
|
Composed HTTP request environment configuration for REST API targets.
|
|
110
110
|
|
|
@@ -126,7 +126,7 @@ class ApiTargetEnv(BaseApiHttpEnv, total=False):
|
|
|
126
126
|
method: str | None
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
class
|
|
129
|
+
class SessionConfigDict(TypedDict, total=False):
|
|
130
130
|
"""
|
|
131
131
|
Minimal session configuration schema accepted by the
|
|
132
132
|
:class:`requests.Session` runner.
|
|
@@ -148,14 +148,14 @@ class SessionConfig(TypedDict, total=False):
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def _build_session_optional(
|
|
151
|
-
cfg:
|
|
151
|
+
cfg: SessionConfigDict | None,
|
|
152
152
|
) -> requests.Session | None:
|
|
153
153
|
"""
|
|
154
154
|
Return a configured session when *cfg* is a mapping.
|
|
155
155
|
|
|
156
156
|
Parameters
|
|
157
157
|
----------
|
|
158
|
-
cfg :
|
|
158
|
+
cfg : SessionConfigDict | None
|
|
159
159
|
Session configuration mapping.
|
|
160
160
|
|
|
161
161
|
Returns
|
|
@@ -164,7 +164,7 @@ def _build_session_optional(
|
|
|
164
164
|
Configured session or ``None``.
|
|
165
165
|
"""
|
|
166
166
|
if isinstance(cfg, Mapping):
|
|
167
|
-
return build_session(cast(
|
|
167
|
+
return build_session(cast(SessionConfigDict, cfg))
|
|
168
168
|
return None
|
|
169
169
|
|
|
170
170
|
|
|
@@ -233,9 +233,9 @@ def _inherit_http_from_api_endpoint(
|
|
|
233
233
|
ep: EndpointConfig,
|
|
234
234
|
url: Url | None,
|
|
235
235
|
headers: dict[str, str],
|
|
236
|
-
session_cfg:
|
|
236
|
+
session_cfg: SessionConfigDict | None,
|
|
237
237
|
force_url: bool = False,
|
|
238
|
-
) -> tuple[Url | None, dict[str, str],
|
|
238
|
+
) -> tuple[Url | None, dict[str, str], SessionConfigDict | None]:
|
|
239
239
|
"""
|
|
240
240
|
Return HTTP settings inherited from API + endpoint definitions.
|
|
241
241
|
|
|
@@ -249,14 +249,14 @@ def _inherit_http_from_api_endpoint(
|
|
|
249
249
|
Existing URL to use when not forcing endpoint URL.
|
|
250
250
|
headers : dict[str, str]
|
|
251
251
|
Existing headers to augment.
|
|
252
|
-
session_cfg :
|
|
252
|
+
session_cfg : SessionConfigDict | None
|
|
253
253
|
Existing session configuration to augment.
|
|
254
254
|
force_url : bool, optional
|
|
255
255
|
Whether to always use the endpoint URL.
|
|
256
256
|
|
|
257
257
|
Returns
|
|
258
258
|
-------
|
|
259
|
-
tuple[Url | None, dict[str, str],
|
|
259
|
+
tuple[Url | None, dict[str, str], SessionConfigDict | None]
|
|
260
260
|
Resolved URL, headers, and session configuration.
|
|
261
261
|
"""
|
|
262
262
|
if force_url or not url:
|
|
@@ -269,8 +269,8 @@ def _inherit_http_from_api_endpoint(
|
|
|
269
269
|
def _merge_session_cfg_three(
|
|
270
270
|
api_cfg: ApiConfig,
|
|
271
271
|
ep: EndpointConfig,
|
|
272
|
-
source_session_cfg:
|
|
273
|
-
) ->
|
|
272
|
+
source_session_cfg: SessionConfigDict | None,
|
|
273
|
+
) -> SessionConfigDict | None:
|
|
274
274
|
"""
|
|
275
275
|
Merge session configurations from API, endpoint, and source.
|
|
276
276
|
|
|
@@ -280,12 +280,12 @@ def _merge_session_cfg_three(
|
|
|
280
280
|
API configuration.
|
|
281
281
|
ep : EndpointConfig
|
|
282
282
|
Endpoint configuration.
|
|
283
|
-
source_session_cfg :
|
|
283
|
+
source_session_cfg : SessionConfigDict | None
|
|
284
284
|
Source session configuration.
|
|
285
285
|
|
|
286
286
|
Returns
|
|
287
287
|
-------
|
|
288
|
-
|
|
288
|
+
SessionConfigDict | None
|
|
289
289
|
Merged session configuration.
|
|
290
290
|
"""
|
|
291
291
|
api_sess = getattr(api_cfg, 'session', None)
|
|
@@ -297,7 +297,7 @@ def _merge_session_cfg_three(
|
|
|
297
297
|
merged.update(ep_sess)
|
|
298
298
|
if isinstance(source_session_cfg, Mapping):
|
|
299
299
|
merged.update(source_session_cfg)
|
|
300
|
-
return cast(
|
|
300
|
+
return cast(SessionConfigDict | None, (merged or None))
|
|
301
301
|
|
|
302
302
|
|
|
303
303
|
def _update_mapping(
|
|
@@ -361,7 +361,7 @@ def compose_api_request_env(
|
|
|
361
361
|
cfg: Any,
|
|
362
362
|
source_obj: Any,
|
|
363
363
|
ex_opts: Mapping[str, Any] | None,
|
|
364
|
-
) ->
|
|
364
|
+
) -> ApiRequestEnvDict:
|
|
365
365
|
"""
|
|
366
366
|
Compose the API request environment.
|
|
367
367
|
|
|
@@ -376,7 +376,7 @@ def compose_api_request_env(
|
|
|
376
376
|
|
|
377
377
|
Returns
|
|
378
378
|
-------
|
|
379
|
-
|
|
379
|
+
ApiRequestEnvDict
|
|
380
380
|
The composed API request environment.
|
|
381
381
|
"""
|
|
382
382
|
ex_opts = ex_opts or {}
|
|
@@ -393,13 +393,13 @@ def compose_api_request_env(
|
|
|
393
393
|
headers: dict[str, str] = cast(dict[str, str], coerce_dict(source_headers))
|
|
394
394
|
pagination = getattr(source_obj, 'pagination', None)
|
|
395
395
|
rate_limit = getattr(source_obj, 'rate_limit', None)
|
|
396
|
-
retry:
|
|
397
|
-
|
|
396
|
+
retry: RetryPolicyDict | None = cast(
|
|
397
|
+
RetryPolicyDict | None,
|
|
398
398
|
getattr(source_obj, 'retry', None),
|
|
399
399
|
)
|
|
400
400
|
retry_network_errors = getattr(source_obj, 'retry_network_errors', None)
|
|
401
401
|
session_cfg = cast(
|
|
402
|
-
|
|
402
|
+
SessionConfigDict | None,
|
|
403
403
|
getattr(source_obj, 'session', None),
|
|
404
404
|
)
|
|
405
405
|
api_name = getattr(source_obj, 'api', None)
|
|
@@ -435,7 +435,7 @@ def compose_api_request_env(
|
|
|
435
435
|
api_cfg.effective_rate_limit_defaults(),
|
|
436
436
|
)
|
|
437
437
|
retry = cast(
|
|
438
|
-
|
|
438
|
+
RetryPolicyDict | None,
|
|
439
439
|
_coalesce(
|
|
440
440
|
retry,
|
|
441
441
|
getattr(ep, 'retry', None),
|
|
@@ -465,8 +465,8 @@ def compose_api_request_env(
|
|
|
465
465
|
timeout: Timeout = ex_opts.get('timeout')
|
|
466
466
|
pag_ov = ex_opts.get('pagination', {})
|
|
467
467
|
rl_ov = ex_opts.get('rate_limit', {})
|
|
468
|
-
rty_ov:
|
|
469
|
-
|
|
468
|
+
rty_ov: RetryPolicyDict | None = cast(
|
|
469
|
+
RetryPolicyDict | None,
|
|
470
470
|
(ex_opts.get('retry') if 'retry' in ex_opts else None),
|
|
471
471
|
)
|
|
472
472
|
rne_ov = (
|
|
@@ -474,7 +474,7 @@ def compose_api_request_env(
|
|
|
474
474
|
if 'retry_network_errors' in ex_opts
|
|
475
475
|
else None
|
|
476
476
|
)
|
|
477
|
-
sess_ov = cast(
|
|
477
|
+
sess_ov = cast(SessionConfigDict | None, ex_opts.get('session'))
|
|
478
478
|
sleep_s = compute_rl_sleep_seconds(rate_limit, rl_ov) or 0.0
|
|
479
479
|
if rty_ov is not None:
|
|
480
480
|
retry = rty_ov
|
|
@@ -485,8 +485,8 @@ def compose_api_request_env(
|
|
|
485
485
|
cast(Mapping[str, Any], session_cfg or {}),
|
|
486
486
|
)
|
|
487
487
|
base_cfg.update(sess_ov)
|
|
488
|
-
session_cfg = cast(
|
|
489
|
-
pag_cfg:
|
|
488
|
+
session_cfg = cast(SessionConfigDict, base_cfg)
|
|
489
|
+
pag_cfg: PaginationConfigDict | None = build_pagination_cfg(
|
|
490
490
|
pagination,
|
|
491
491
|
pag_ov,
|
|
492
492
|
)
|
|
@@ -513,7 +513,7 @@ def compose_api_target_env(
|
|
|
513
513
|
cfg: Any,
|
|
514
514
|
target_obj: Any,
|
|
515
515
|
overrides: Mapping[str, Any] | None,
|
|
516
|
-
) ->
|
|
516
|
+
) -> ApiTargetEnvDict:
|
|
517
517
|
"""
|
|
518
518
|
Compose the API target environment.
|
|
519
519
|
|
|
@@ -528,7 +528,7 @@ def compose_api_target_env(
|
|
|
528
528
|
|
|
529
529
|
Returns
|
|
530
530
|
-------
|
|
531
|
-
|
|
531
|
+
ApiTargetEnvDict
|
|
532
532
|
Composed API target environment.
|
|
533
533
|
"""
|
|
534
534
|
ov = overrides or {}
|
|
@@ -553,8 +553,8 @@ def compose_api_target_env(
|
|
|
553
553
|
timeout: Timeout = (
|
|
554
554
|
cast(Timeout, ov.get('timeout')) if 'timeout' in ov else None
|
|
555
555
|
)
|
|
556
|
-
sess_cfg:
|
|
557
|
-
|
|
556
|
+
sess_cfg: SessionConfigDict | None = cast(
|
|
557
|
+
SessionConfigDict | None,
|
|
558
558
|
ov.get('session'),
|
|
559
559
|
)
|
|
560
560
|
api_name = getattr(target_obj, 'api', None)
|
|
@@ -583,7 +583,7 @@ def compose_api_target_env(
|
|
|
583
583
|
def build_pagination_cfg(
|
|
584
584
|
pagination: PaginationConfig | None,
|
|
585
585
|
overrides: Mapping[str, Any] | None,
|
|
586
|
-
) ->
|
|
586
|
+
) -> PaginationConfigDict | None:
|
|
587
587
|
"""
|
|
588
588
|
Build pagination configuration.
|
|
589
589
|
|
|
@@ -596,7 +596,7 @@ def build_pagination_cfg(
|
|
|
596
596
|
|
|
597
597
|
Returns
|
|
598
598
|
-------
|
|
599
|
-
|
|
599
|
+
PaginationConfigDict | None
|
|
600
600
|
Pagination configuration.
|
|
601
601
|
"""
|
|
602
602
|
ptype: str | None = None
|
|
@@ -683,7 +683,7 @@ def build_pagination_cfg(
|
|
|
683
683
|
case _:
|
|
684
684
|
pass
|
|
685
685
|
|
|
686
|
-
return cast(
|
|
686
|
+
return cast(PaginationConfigDict, cfg)
|
|
687
687
|
|
|
688
688
|
|
|
689
689
|
def paginate_with_client(
|
|
@@ -692,7 +692,7 @@ def paginate_with_client(
|
|
|
692
692
|
params: Params | None,
|
|
693
693
|
headers: Headers | None,
|
|
694
694
|
timeout: Timeout,
|
|
695
|
-
pagination:
|
|
695
|
+
pagination: PaginationConfigDict | None,
|
|
696
696
|
sleep_seconds: float | None,
|
|
697
697
|
) -> Any:
|
|
698
698
|
"""
|
|
@@ -710,7 +710,7 @@ def paginate_with_client(
|
|
|
710
710
|
Headers to include in the API request.
|
|
711
711
|
timeout : Timeout
|
|
712
712
|
Timeout configuration for the API request.
|
|
713
|
-
pagination :
|
|
713
|
+
pagination : PaginationConfigDict | None
|
|
714
714
|
Pagination configuration for the API request.
|
|
715
715
|
sleep_seconds : float | None
|
|
716
716
|
Sleep duration between API requests.
|
|
@@ -771,9 +771,9 @@ def compute_rl_sleep_seconds(
|
|
|
771
771
|
else:
|
|
772
772
|
rl_map = cast(Mapping[str, Any] | None, rate_limit)
|
|
773
773
|
|
|
774
|
-
rl_mapping = cast(
|
|
774
|
+
rl_mapping = cast(RateLimitConfigDict | None, rl_map)
|
|
775
775
|
|
|
776
|
-
typed_override:
|
|
776
|
+
typed_override: RateLimitConfigDict | None = None
|
|
777
777
|
if overrides:
|
|
778
778
|
filtered: dict[str, float | None] = {}
|
|
779
779
|
if 'sleep_seconds' in overrides:
|
|
@@ -787,7 +787,7 @@ def compute_rl_sleep_seconds(
|
|
|
787
787
|
overrides.get('max_per_sec'),
|
|
788
788
|
)
|
|
789
789
|
if filtered:
|
|
790
|
-
typed_override = cast(
|
|
790
|
+
typed_override = cast(RateLimitConfigDict, filtered)
|
|
791
791
|
|
|
792
792
|
return RateLimiter.resolve_sleep_seconds(
|
|
793
793
|
rate_limit=rl_mapping,
|
|
@@ -796,14 +796,14 @@ def compute_rl_sleep_seconds(
|
|
|
796
796
|
|
|
797
797
|
|
|
798
798
|
def build_session(
|
|
799
|
-
cfg:
|
|
799
|
+
cfg: SessionConfigDict | None,
|
|
800
800
|
) -> requests.Session:
|
|
801
801
|
"""
|
|
802
802
|
Build a requests.Session object with the given configuration.
|
|
803
803
|
|
|
804
804
|
Parameters
|
|
805
805
|
----------
|
|
806
|
-
cfg :
|
|
806
|
+
cfg : SessionConfigDict | None
|
|
807
807
|
Session configuration.
|
|
808
808
|
|
|
809
809
|
Returns
|
etlplus/cli/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Documentation for the `etlplus.cli` subpackage: command-line interface for ETLPlus workflows.
|
|
4
4
|
|
|
5
5
|
- Provides a CLI for running ETL pipelines, jobs, and utilities
|
|
6
|
-
- Supports commands for
|
|
6
|
+
- Supports commands for extracting, transforming, validating data, and running pipelines
|
|
7
7
|
- Includes options for configuration, state, and output control
|
|
8
8
|
- Exposes handlers for custom command integration
|
|
9
9
|
|
|
@@ -17,20 +17,22 @@ Back to project overview: see the top-level [README](../../README.md).
|
|
|
17
17
|
|
|
18
18
|
## Available Commands
|
|
19
19
|
|
|
20
|
+
- **check**: Inspect pipeline configuration (jobs, sources, targets)
|
|
21
|
+
- **extract**: Extract data from files/APIs/databases
|
|
22
|
+
- **transform**: Transform records
|
|
23
|
+
- **load**: Load data to files/APIs/databases
|
|
24
|
+
- **render**: Render SQL DDL from table specs
|
|
25
|
+
- **validate**: Validate data against rules
|
|
20
26
|
- **run**: Execute a pipeline or job
|
|
21
|
-
- **validate**: Validate pipeline or config files
|
|
22
|
-
- **inspect**: Show pipeline/job details
|
|
23
27
|
|
|
24
28
|
## Command Options
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
- `--state`: Path to state file
|
|
28
|
-
- `--output`: Output file or format
|
|
30
|
+
Use `etlplus <command> --help` for the exact options supported by each command.
|
|
29
31
|
|
|
30
32
|
## Example: Running a Pipeline
|
|
31
33
|
|
|
32
34
|
```bash
|
|
33
|
-
etlplus run --config configs/pipeline.yml --
|
|
35
|
+
etlplus run --config configs/pipeline.yml --job file_to_file_customers
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
## See Also
|