etlplus 0.14.3__py3-none-any.whl → 0.16.0__py3-none-any.whl

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