etlplus 0.15.5__py3-none-any.whl → 0.16.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.
@@ -1,386 +0,0 @@
1
- """
2
- :mod:`etlplus.workflow.connector` module.
3
-
4
- A module defining configuration types for data source/target connectors in ETL
5
- pipelines. A "connector" is any I/O endpoint:
6
-
7
- - file (local/remote file systems)
8
- - database
9
- - REST API service/endpoint
10
- - (future) queues, streams, etc.
11
-
12
- Examples
13
- --------
14
- - Use :class:`ConnectorApi`/:class:`ConnectorFile`/:class:`ConnectorDb` when
15
- you want the concrete dataclasses.
16
- - Use the :class:`Connector` union for typing a value that can be any
17
- connector.
18
- - Use :func:`parse_connector(obj)` to construct a connector instance from a
19
- generic mapping that includes a *type* key.
20
-
21
- Notes
22
- -----
23
- - TypedDict shapes are editor hints; runtime parsing remains permissive
24
- (from_obj accepts Mapping[str, Any]).
25
- - TypedDicts referenced in :mod:`etlplus.workflow.types` remain editor hints.
26
- Runtime parsing stays permissive and tolerant.
27
-
28
- See Also
29
- --------
30
- - TypedDict shapes for editor hints (not enforced at runtime):
31
- :mod:`etlplus.workflow.types.ConnectorApiConfigMap`,
32
- :mod:`etlplus.workflow.types.ConnectorDbConfigMap`,
33
- :mod:`etlplus.workflow.types.ConnectorFileConfigMap`.
34
- """
35
-
36
- from __future__ import annotations
37
-
38
- from collections.abc import Mapping
39
- from dataclasses import dataclass
40
- from dataclasses import field
41
- from typing import TYPE_CHECKING
42
- from typing import Any
43
- from typing import Self
44
- from typing import overload
45
-
46
- from ..api import PaginationConfig
47
- from ..api import RateLimitConfig
48
- from ..types import StrAnyMap
49
- from ..utils import cast_str_dict
50
- from ..utils import coerce_dict
51
-
52
- if TYPE_CHECKING: # Editor-only typing hints to avoid runtime imports
53
- from .types import ConnectorApiConfigMap
54
- from .types import ConnectorDbConfigMap
55
- from .types import ConnectorFileConfigMap
56
- from .types import ConnectorType
57
-
58
-
59
- # SECTION: EXPORTS ========================================================== #
60
-
61
-
62
- __all__ = [
63
- # Data Classes
64
- 'ConnectorApi',
65
- 'ConnectorDb',
66
- 'ConnectorFile',
67
- # Functions
68
- 'parse_connector',
69
- # Type aliases
70
- 'Connector',
71
- ]
72
-
73
-
74
- # SECTION: INTERNAL FUNCTIONS ============================================== #
75
-
76
-
77
- def _require_name(
78
- obj: StrAnyMap,
79
- *,
80
- kind: str,
81
- ) -> str:
82
- """
83
- Extract and validate the ``name`` field from connector mappings.
84
-
85
- Parameters
86
- ----------
87
- obj : StrAnyMap
88
- Connector mapping with a ``name`` entry.
89
- kind : str
90
- Connector kind used in the error message.
91
-
92
- Returns
93
- -------
94
- str
95
- Valid connector name.
96
-
97
- Raises
98
- ------
99
- TypeError
100
- If ``name`` is missing or not a string.
101
- """
102
- name = obj.get('name')
103
- if not isinstance(name, str):
104
- raise TypeError(f'Connector{kind} requires a "name" (str)')
105
- return name
106
-
107
-
108
- # SECTION: DATA CLASSES ===================================================== #
109
-
110
-
111
- @dataclass(kw_only=True, slots=True)
112
- class ConnectorApi:
113
- """
114
- Configuration for an API-based data connector.
115
-
116
- Attributes
117
- ----------
118
- name : str
119
- Unique connector name.
120
- type : ConnectorType
121
- Connector kind literal, always ``'api'``.
122
- url : str | None
123
- Direct absolute URL (when not using ``service``/``endpoint`` refs).
124
- method : str | None
125
- Optional HTTP method; typically omitted for sources (defaults to
126
- GET) and used for targets (e.g., ``'post'``).
127
- headers : dict[str, str]
128
- Additional request headers.
129
- query_params : dict[str, Any]
130
- Default query parameters.
131
- pagination : PaginationConfig | None
132
- Pagination settings (optional).
133
- rate_limit : RateLimitConfig | None
134
- Rate limiting settings (optional).
135
- api : str | None
136
- Service reference into the pipeline ``apis`` block (a.k.a.
137
- ``service``).
138
- endpoint : str | None
139
- Endpoint name within the referenced service.
140
- """
141
-
142
- # -- Attributes -- #
143
-
144
- name: str
145
- type: ConnectorType = 'api'
146
-
147
- # Direct form
148
- url: str | None = None
149
- # Optional HTTP method; typically omitted for sources (defaults to GET)
150
- # at runtime) and used for targets (e.g., 'post', 'put').
151
- method: str | None = None
152
- headers: dict[str, str] = field(default_factory=dict)
153
- query_params: dict[str, Any] = field(default_factory=dict)
154
- pagination: PaginationConfig | None = None
155
- rate_limit: RateLimitConfig | None = None
156
-
157
- # Reference form (to top-level APIs/endpoints)
158
- api: str | None = None
159
- endpoint: str | None = None
160
-
161
- # -- Class Methods -- #
162
-
163
- @classmethod
164
- @overload
165
- def from_obj(cls, obj: ConnectorApiConfigMap) -> Self: ...
166
-
167
- @classmethod
168
- @overload
169
- def from_obj(cls, obj: StrAnyMap) -> Self: ...
170
-
171
- @classmethod
172
- def from_obj(
173
- cls,
174
- obj: StrAnyMap,
175
- ) -> Self:
176
- """
177
- Parse a mapping into a ``ConnectorApi`` instance.
178
-
179
- Parameters
180
- ----------
181
- obj : StrAnyMap
182
- Mapping with at least ``name``.
183
-
184
- Returns
185
- -------
186
- Self
187
- Parsed connector instance.
188
- """
189
- name = _require_name(obj, kind='Api')
190
- headers = cast_str_dict(obj.get('headers'))
191
-
192
- return cls(
193
- name=name,
194
- type='api',
195
- url=obj.get('url'),
196
- method=obj.get('method'),
197
- headers=headers,
198
- query_params=coerce_dict(obj.get('query_params')),
199
- pagination=PaginationConfig.from_obj(obj.get('pagination')),
200
- rate_limit=RateLimitConfig.from_obj(obj.get('rate_limit')),
201
- api=obj.get('api') or obj.get('service'),
202
- endpoint=obj.get('endpoint'),
203
- )
204
-
205
-
206
- @dataclass(kw_only=True, slots=True)
207
- class ConnectorDb:
208
- """
209
- Configuration for a database-based data connector.
210
-
211
- Attributes
212
- ----------
213
- name : str
214
- Unique connector name.
215
- type : ConnectorType
216
- Connector kind literal, always ``'database'``.
217
- connection_string : str | None
218
- Connection string/DSN for the database.
219
- query : str | None
220
- Query to execute for extraction (optional).
221
- table : str | None
222
- Target/source table name (optional).
223
- mode : str | None
224
- Load mode hint (e.g., ``'append'``, ``'replace'``) — future use.
225
- """
226
-
227
- # -- Attributes -- #
228
-
229
- name: str
230
- type: ConnectorType = 'database'
231
- connection_string: str | None = None
232
- query: str | None = None
233
- table: str | None = None
234
- mode: str | None = None # append|replace|upsert (future)
235
-
236
- # -- Class Methods -- #
237
-
238
- @classmethod
239
- @overload
240
- def from_obj(cls, obj: ConnectorDbConfigMap) -> Self: ...
241
-
242
- @classmethod
243
- @overload
244
- def from_obj(cls, obj: StrAnyMap) -> Self: ...
245
-
246
- @classmethod
247
- def from_obj(
248
- cls,
249
- obj: StrAnyMap,
250
- ) -> Self:
251
- """
252
- Parse a mapping into a ``ConnectorDb`` instance.
253
-
254
- Parameters
255
- ----------
256
- obj : StrAnyMap
257
- Mapping with at least ``name``.
258
-
259
- Returns
260
- -------
261
- Self
262
- Parsed connector instance.
263
- """
264
- name = _require_name(obj, kind='Db')
265
-
266
- return cls(
267
- name=name,
268
- type='database',
269
- connection_string=obj.get('connection_string'),
270
- query=obj.get('query'),
271
- table=obj.get('table'),
272
- mode=obj.get('mode'),
273
- )
274
-
275
-
276
- @dataclass(kw_only=True, slots=True)
277
- class ConnectorFile:
278
- """
279
- Configuration for a file-based data connector.
280
-
281
- Attributes
282
- ----------
283
- name : str
284
- Unique connector name.
285
- type : ConnectorType
286
- Connector kind literal, always ``'file'``.
287
- format : str | None
288
- File format (e.g., ``'json'``, ``'csv'``).
289
- path : str | None
290
- File path or URI.
291
- options : dict[str, Any]
292
- Reader/writer format options.
293
- """
294
-
295
- # -- Attributes -- #
296
-
297
- name: str
298
- type: ConnectorType = 'file'
299
- format: str | None = None
300
- path: str | None = None
301
- options: dict[str, Any] = field(default_factory=dict)
302
-
303
- # -- Class Methods -- #
304
-
305
- @classmethod
306
- @overload
307
- def from_obj(cls, obj: ConnectorFileConfigMap) -> Self: ...
308
-
309
- @classmethod
310
- @overload
311
- def from_obj(cls, obj: StrAnyMap) -> Self: ...
312
-
313
- @classmethod
314
- def from_obj(
315
- cls,
316
- obj: StrAnyMap,
317
- ) -> Self:
318
- """
319
- Parse a mapping into a ``ConnectorFile`` instance.
320
-
321
- Parameters
322
- ----------
323
- obj : StrAnyMap
324
- Mapping with at least ``name``.
325
-
326
- Returns
327
- -------
328
- Self
329
- Parsed connector instance.
330
- """
331
- name = _require_name(obj, kind='File')
332
-
333
- return cls(
334
- name=name,
335
- type='file',
336
- format=obj.get('format'),
337
- path=obj.get('path'),
338
- options=coerce_dict(obj.get('options')),
339
- )
340
-
341
-
342
- # SECTION: FUNCTIONS ======================================================== #
343
-
344
-
345
- def parse_connector(obj: Mapping[str, Any]) -> Connector:
346
- """
347
- Dispatch to a concrete connector constructor based on ``type``.
348
-
349
- Parameters
350
- ----------
351
- obj : Mapping[str, Any]
352
- Mapping with at least ``name`` and ``type``.
353
-
354
- Returns
355
- -------
356
- Connector
357
- Concrete connector instance.
358
-
359
- Raises
360
- ------
361
- TypeError
362
- If ``type`` is unsupported or missing.
363
-
364
- Notes
365
- -----
366
- Delegates to the tolerant ``from_obj`` constructors for each connector
367
- kind.
368
- """
369
- match str(obj.get('type', '')).casefold():
370
- case 'file':
371
- return ConnectorFile.from_obj(obj)
372
- case 'database':
373
- return ConnectorDb.from_obj(obj)
374
- case 'api':
375
- return ConnectorApi.from_obj(obj)
376
- case _:
377
- raise TypeError(
378
- 'Unsupported connector type; '
379
- 'expected one of {file, database, api}',
380
- )
381
-
382
-
383
- # SECTION: TYPED ALIASES (post-class definitions) ========================= #
384
-
385
- # Type alias representing any supported connector
386
- type Connector = ConnectorApi | ConnectorDb | ConnectorFile
etlplus/workflow/types.py DELETED
@@ -1,115 +0,0 @@
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