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.
Files changed (98) hide show
  1. etlplus/README.md +24 -2
  2. etlplus/__init__.py +2 -0
  3. etlplus/api/__init__.py +14 -14
  4. etlplus/api/auth.py +9 -6
  5. etlplus/api/config.py +6 -6
  6. etlplus/api/endpoint_client.py +16 -16
  7. etlplus/api/enums.py +2 -2
  8. etlplus/api/errors.py +4 -4
  9. etlplus/api/pagination/__init__.py +6 -6
  10. etlplus/api/pagination/config.py +11 -9
  11. etlplus/api/rate_limiting/__init__.py +2 -2
  12. etlplus/api/rate_limiting/config.py +10 -10
  13. etlplus/api/rate_limiting/rate_limiter.py +2 -2
  14. etlplus/api/request_manager.py +4 -4
  15. etlplus/api/retry_manager.py +6 -6
  16. etlplus/api/transport.py +10 -10
  17. etlplus/api/types.py +47 -26
  18. etlplus/api/utils.py +49 -49
  19. etlplus/cli/README.md +9 -7
  20. etlplus/cli/commands.py +22 -22
  21. etlplus/cli/handlers.py +12 -13
  22. etlplus/cli/main.py +1 -1
  23. etlplus/{workflow/pipeline.py → config.py} +54 -91
  24. etlplus/connector/__init__.py +6 -6
  25. etlplus/connector/api.py +7 -7
  26. etlplus/connector/database.py +3 -3
  27. etlplus/connector/file.py +3 -3
  28. etlplus/connector/types.py +2 -2
  29. etlplus/database/README.md +7 -7
  30. etlplus/enums.py +35 -167
  31. etlplus/file/README.md +7 -5
  32. etlplus/file/accdb.py +2 -1
  33. etlplus/file/arrow.py +2 -1
  34. etlplus/file/bson.py +2 -1
  35. etlplus/file/cbor.py +2 -1
  36. etlplus/file/cfg.py +1 -1
  37. etlplus/file/conf.py +1 -1
  38. etlplus/file/dat.py +1 -1
  39. etlplus/file/dta.py +1 -1
  40. etlplus/file/duckdb.py +2 -1
  41. etlplus/file/enums.py +1 -1
  42. etlplus/file/fwf.py +2 -1
  43. etlplus/file/hbs.py +2 -1
  44. etlplus/file/hdf5.py +2 -1
  45. etlplus/file/ini.py +2 -1
  46. etlplus/file/ion.py +1 -1
  47. etlplus/file/jinja2.py +2 -1
  48. etlplus/file/log.py +1 -1
  49. etlplus/file/mat.py +1 -1
  50. etlplus/file/mdb.py +2 -1
  51. etlplus/file/msgpack.py +2 -1
  52. etlplus/file/mustache.py +2 -1
  53. etlplus/file/nc.py +1 -1
  54. etlplus/file/numbers.py +2 -1
  55. etlplus/file/ods.py +2 -1
  56. etlplus/file/pb.py +2 -1
  57. etlplus/file/pbf.py +2 -1
  58. etlplus/file/properties.py +2 -1
  59. etlplus/file/proto.py +2 -1
  60. etlplus/file/psv.py +2 -1
  61. etlplus/file/rda.py +2 -1
  62. etlplus/file/rds.py +1 -1
  63. etlplus/file/sas7bdat.py +2 -1
  64. etlplus/file/sav.py +1 -1
  65. etlplus/file/sqlite.py +2 -1
  66. etlplus/file/sylk.py +2 -1
  67. etlplus/file/tab.py +2 -1
  68. etlplus/file/toml.py +2 -1
  69. etlplus/file/vm.py +2 -1
  70. etlplus/file/wks.py +2 -1
  71. etlplus/file/xls.py +1 -1
  72. etlplus/file/xlsm.py +2 -2
  73. etlplus/file/xpt.py +2 -1
  74. etlplus/file/zsav.py +2 -1
  75. etlplus/ops/README.md +10 -9
  76. etlplus/ops/__init__.py +1 -0
  77. etlplus/ops/enums.py +173 -0
  78. etlplus/ops/extract.py +209 -22
  79. etlplus/ops/load.py +140 -34
  80. etlplus/ops/run.py +88 -103
  81. etlplus/ops/transform.py +46 -27
  82. etlplus/ops/types.py +147 -0
  83. etlplus/ops/utils.py +5 -5
  84. etlplus/ops/validate.py +13 -13
  85. etlplus/templates/README.md +11 -9
  86. etlplus/types.py +5 -102
  87. etlplus/workflow/README.md +0 -24
  88. etlplus/workflow/__init__.py +2 -4
  89. etlplus/workflow/dag.py +23 -1
  90. etlplus/workflow/jobs.py +15 -28
  91. etlplus/workflow/profile.py +4 -2
  92. {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/METADATA +32 -28
  93. etlplus-0.16.7.dist-info/RECORD +143 -0
  94. etlplus-0.16.0.dist-info/RECORD +0 -141
  95. {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/WHEEL +0 -0
  96. {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/entry_points.txt +0 -0
  97. {etlplus-0.16.0.dist-info → etlplus-0.16.7.dist-info}/licenses/LICENSE +0 -0
  98. {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
- 'HTTPAdapterMountConfig',
45
- 'HTTPAdapterRetryConfig',
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 HTTPAdapterRetryConfig(TypedDict, total=False):
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: HTTPAdapterRetryConfig = {
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 HTTPAdapterMountConfig(TypedDict, total=False):
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 | HTTPAdapterRetryConfig
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: HTTPAdapterMountConfig = {
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 | HTTPAdapterRetryConfig
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[HTTPAdapterMountConfig],
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[HTTPAdapterMountConfig]
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
- 'ApiConfigMap',
47
- 'ApiProfileConfigMap',
48
- 'ApiProfileDefaultsMap',
49
- 'EndpointMap',
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 ApiConfigMap(TypedDict, total=False):
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, EndpointMap | str]
79
- profiles: Mapping[str, ApiProfileConfigMap]
102
+ endpoints: Mapping[str, EndpointConfigDict | str]
103
+ profiles: Mapping[str, ApiProfileConfigDict]
80
104
 
81
105
 
82
- class ApiProfileConfigMap(TypedDict, total=False):
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: ApiProfileDefaultsMap
126
+ defaults: ApiProfileDefaultsDict
103
127
 
104
128
 
105
- class ApiProfileDefaultsMap(TypedDict, total=False):
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 # PaginationConfigMap | StrAnyMap
127
- rate_limit: StrAnyMap # RateLimitConfigMap | StrAnyMap
150
+ pagination: StrAnyMap # PaginationConfigDict | StrAnyMap
151
+ rate_limit: StrAnyMap # RateLimitConfigDict | StrAnyMap
128
152
 
129
153
 
130
- class EndpointMap(TypedDict, total=False):
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 # PaginationConfigMap | StrAnyMap
148
- rate_limit: StrAnyMap # RateLimitConfigMap | 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', dict(self.params))
203
+ object.__setattr__(self, 'params', _to_dict(self.params))
180
204
  if self.headers is not None:
181
- object.__setattr__(self, 'headers', dict(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
- RequestOptions
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 = cast(dict, 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 = cast(dict, 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 PaginationConfigMap
25
+ from .pagination import PaginationConfigDict
26
26
  from .rate_limiting import RateLimitConfig
27
- from .rate_limiting import RateLimitConfigMap
27
+ from .rate_limiting import RateLimitConfigDict
28
28
  from .rate_limiting import RateLimiter
29
- from .retry_manager import RetryPolicy
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
- 'ApiRequestEnv',
57
- 'ApiTargetEnv',
58
- 'SessionConfig',
56
+ 'ApiRequestEnvDict',
57
+ 'ApiTargetEnvDict',
58
+ 'SessionConfigDict',
59
59
  ]
60
60
 
61
61
 
62
62
  # SECTION: TYPED DICTS ====================================================== #
63
63
 
64
64
 
65
- class BaseApiHttpEnv(TypedDict, total=False):
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 ApiRequestEnv(BaseApiHttpEnv, total=False):
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: PaginationConfigMap | None
99
+ pagination: PaginationConfigDict | None
100
100
  sleep_seconds: float
101
101
 
102
102
  # Reliability
103
- retry: RetryPolicy | None
103
+ retry: RetryPolicyDict | None
104
104
  retry_network_errors: bool
105
105
 
106
106
 
107
- class ApiTargetEnv(BaseApiHttpEnv, total=False):
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 SessionConfig(TypedDict, total=False):
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: SessionConfig | None,
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 : SessionConfig | None
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(SessionConfig, cfg))
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: SessionConfig | None,
236
+ session_cfg: SessionConfigDict | None,
237
237
  force_url: bool = False,
238
- ) -> tuple[Url | None, dict[str, str], SessionConfig | None]:
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 : SessionConfig | None
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], SessionConfig | None]
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: SessionConfig | None,
273
- ) -> SessionConfig | None:
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 : SessionConfig | None
283
+ source_session_cfg : SessionConfigDict | None
284
284
  Source session configuration.
285
285
 
286
286
  Returns
287
287
  -------
288
- SessionConfig | None
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(SessionConfig | None, (merged or None))
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
- ) -> ApiRequestEnv:
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
- ApiRequestEnv
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: RetryPolicy | None = cast(
397
- RetryPolicy | None,
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
- SessionConfig | None,
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
- RetryPolicy | None,
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: RetryPolicy | None = cast(
469
- RetryPolicy | None,
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(SessionConfig | None, ex_opts.get('session'))
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(SessionConfig, base_cfg)
489
- pag_cfg: PaginationConfigMap | None = build_pagination_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
- ) -> ApiTargetEnv:
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
- ApiTargetEnv
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: SessionConfig | None = cast(
557
- SessionConfig | None,
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
- ) -> PaginationConfigMap | None:
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
- PaginationConfigMap | None
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(PaginationConfigMap, cfg)
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: PaginationConfigMap | None,
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 : PaginationConfigMap | None
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(RateLimitConfigMap | None, rl_map)
774
+ rl_mapping = cast(RateLimitConfigDict | None, rl_map)
775
775
 
776
- typed_override: RateLimitConfigMap | None = None
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(RateLimitConfigMap, filtered)
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: SessionConfig | None,
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 : SessionConfig | None
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 running, validating, and inspecting pipelines
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
- - `--config`: Path to config file
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 --output results.json
35
+ etlplus run --config configs/pipeline.yml --job file_to_file_customers
34
36
  ```
35
37
 
36
38
  ## See Also