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.
etlplus/workflow/jobs.py CHANGED
@@ -14,6 +14,7 @@ Notes
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ from collections.abc import Sequence
17
18
  from dataclasses import dataclass
18
19
  from dataclasses import field
19
20
  from typing import Any
@@ -76,13 +77,15 @@ def _parse_depends_on(
76
77
  """
77
78
  if isinstance(value, str):
78
79
  return [value]
79
- if isinstance(value, list):
80
+ if isinstance(value, Sequence) and not isinstance(
81
+ value,
82
+ (str, bytes, bytearray),
83
+ ):
80
84
  return [entry for entry in value if isinstance(entry, str)]
81
85
  return []
82
86
 
83
87
 
84
88
  def _require_str(
85
- # data: dict[str, Any],
86
89
  data: StrAnyMap,
87
90
  key: str,
88
91
  ) -> str | None:
@@ -149,13 +152,9 @@ class ExtractRef:
149
152
  data = maybe_mapping(obj)
150
153
  if not data:
151
154
  return None
152
- source = _require_str(data, 'source')
153
- if source is None:
155
+ if (source := _require_str(data, 'source')) is None:
154
156
  return None
155
- return cls(
156
- source=source,
157
- options=coerce_dict(data.get('options')),
158
- )
157
+ return cls(source=source, options=coerce_dict(data.get('options')))
159
158
 
160
159
 
161
160
  @dataclass(kw_only=True, slots=True)
@@ -214,18 +213,13 @@ class JobConfig:
214
213
  data = maybe_mapping(obj)
215
214
  if not data:
216
215
  return None
217
- name = _require_str(data, 'name')
218
- if name is None:
216
+ if (name := _require_str(data, 'name')) is None:
219
217
  return None
220
218
 
221
- description = _coerce_optional_str(data.get('description'))
222
-
223
- depends_on = _parse_depends_on(data.get('depends_on'))
224
-
225
219
  return cls(
226
220
  name=name,
227
- description=description,
228
- depends_on=depends_on,
221
+ description=_coerce_optional_str(data.get('description')),
222
+ depends_on=_parse_depends_on(data.get('depends_on')),
229
223
  extract=ExtractRef.from_obj(data.get('extract')),
230
224
  validate=ValidationRef.from_obj(data.get('validate')),
231
225
  transform=TransformRef.from_obj(data.get('transform')),
@@ -274,8 +268,7 @@ class LoadRef:
274
268
  data = maybe_mapping(obj)
275
269
  if not data:
276
270
  return None
277
- target = _require_str(data, 'target')
278
- if target is None:
271
+ if (target := _require_str(data, 'target')) is None:
279
272
  return None
280
273
  return cls(
281
274
  target=target,
@@ -321,8 +314,7 @@ class TransformRef:
321
314
  data = maybe_mapping(obj)
322
315
  if not data:
323
316
  return None
324
- pipeline = _require_str(data, 'pipeline')
325
- if pipeline is None:
317
+ if (pipeline := _require_str(data, 'pipeline')) is None:
326
318
  return None
327
319
  return cls(pipeline=pipeline)
328
320
 
@@ -372,13 +364,10 @@ class ValidationRef:
372
364
  data = maybe_mapping(obj)
373
365
  if not data:
374
366
  return None
375
- ruleset = _require_str(data, 'ruleset')
376
- if ruleset is None:
367
+ if (ruleset := _require_str(data, 'ruleset')) is None:
377
368
  return None
378
- severity = _coerce_optional_str(data.get('severity'))
379
- phase = _coerce_optional_str(data.get('phase'))
380
369
  return cls(
381
370
  ruleset=ruleset,
382
- severity=severity,
383
- phase=phase,
371
+ severity=_coerce_optional_str(data.get('severity')),
372
+ phase=_coerce_optional_str(data.get('phase')),
384
373
  )
@@ -25,14 +25,14 @@ from typing import Any
25
25
  from typing import Self
26
26
 
27
27
  from ..api import ApiConfig
28
+ from ..connector import Connector
29
+ from ..connector import parse_connector
28
30
  from ..file import File
29
31
  from ..file import FileFormat
30
32
  from ..types import StrAnyMap
31
33
  from ..utils import coerce_dict
32
34
  from ..utils import deep_substitute
33
35
  from ..utils import maybe_mapping
34
- from .connector import Connector
35
- from .connector import parse_connector
36
36
  from .jobs import JobConfig
37
37
  from .profile import ProfileConfig
38
38
 
@@ -50,20 +50,42 @@ __all__ = [
50
50
  # SECTION: INTERNAL FUNCTIONS =============================================== #
51
51
 
52
52
 
53
- def _collect_parsed[T](
53
+ def _build_connectors(
54
54
  raw: StrAnyMap,
55
+ *,
55
56
  key: str,
56
- parser: Callable[[Any], T | None],
57
- ) -> list[T]:
57
+ ) -> list[Connector]:
58
58
  """
59
- Collect parsed items from ``raw[key]`` using a tolerant parser.
59
+ Parse connector entries from a list under ``raw[key]``.
60
60
 
61
61
  Parameters
62
62
  ----------
63
63
  raw : StrAnyMap
64
64
  Raw pipeline mapping.
65
65
  key : str
66
- Key pointing to a list-like payload.
66
+ Key pointing to connector entries (e.g., ``"sources"``).
67
+
68
+ Returns
69
+ -------
70
+ list[Connector]
71
+ Parsed connector instances.
72
+ """
73
+ return list(
74
+ _collect_parsed(raw.get(key, []) or [], _parse_connector_entry),
75
+ )
76
+
77
+
78
+ def _collect_parsed[T](
79
+ items: Any,
80
+ parser: Callable[[Any], T | None],
81
+ ) -> list[T]:
82
+ """
83
+ Collect parsed items from ``raw[key]`` using a tolerant parser.
84
+
85
+ Parameters
86
+ ----------
87
+ items : Any
88
+ List-like payload to parse.
67
89
  parser : Callable[[Any], T | None]
68
90
  Parser that returns an instance or ``None`` for invalid entries.
69
91
 
@@ -72,12 +94,12 @@ def _collect_parsed[T](
72
94
  list[T]
73
95
  Parsed items, excluding invalid entries.
74
96
  """
75
- items: list[T] = []
76
- for entry in raw.get(key, []) or []:
97
+ parsed_items: list[T] = []
98
+ for entry in items or []:
77
99
  parsed = parser(entry)
78
100
  if parsed is not None:
79
- items.append(parsed)
80
- return items
101
+ parsed_items.append(parsed)
102
+ return parsed_items
81
103
 
82
104
 
83
105
  def _parse_connector_entry(
@@ -104,48 +126,6 @@ def _parse_connector_entry(
104
126
  return None
105
127
 
106
128
 
107
- def _build_sources(
108
- raw: StrAnyMap,
109
- ) -> list[Connector]:
110
- """
111
- Return a list of source connectors parsed from the mapping.
112
-
113
- Parameters
114
- ----------
115
- raw : StrAnyMap
116
- Raw pipeline mapping.
117
-
118
- Returns
119
- -------
120
- list[Connector]
121
- Parsed source connectors.
122
- """
123
- return list(
124
- _collect_parsed(raw, 'sources', _parse_connector_entry),
125
- )
126
-
127
-
128
- def _build_targets(
129
- raw: StrAnyMap,
130
- ) -> list[Connector]:
131
- """
132
- Return a list of target connectors parsed from the mapping.
133
-
134
- Parameters
135
- ----------
136
- raw : StrAnyMap
137
- Raw pipeline mapping.
138
-
139
- Returns
140
- -------
141
- list[Connector]
142
- Parsed target connectors.
143
- """
144
- return list(
145
- _collect_parsed(raw, 'targets', _parse_connector_entry),
146
- )
147
-
148
-
149
129
  # SECTION: FUNCTIONS ======================================================== #
150
130
 
151
131
 
@@ -311,17 +291,20 @@ class PipelineConfig:
311
291
  file_systems = coerce_dict(raw.get('file_systems'))
312
292
 
313
293
  # Sources
314
- sources = _build_sources(raw)
294
+ sources = _build_connectors(raw, key='sources')
315
295
 
316
296
  # Validations/Transforms
317
297
  validations = coerce_dict(raw.get('validations'))
318
298
  transforms = coerce_dict(raw.get('transforms'))
319
299
 
320
300
  # Targets
321
- targets = _build_targets(raw)
301
+ targets = _build_connectors(raw, key='targets')
322
302
 
323
303
  # Jobs
324
- jobs = _collect_parsed(raw, 'jobs', JobConfig.from_obj)
304
+ jobs: list[JobConfig] = _collect_parsed(
305
+ raw.get('jobs', []) or [],
306
+ JobConfig.from_obj,
307
+ )
325
308
 
326
309
  # Table schemas (optional, tolerant pass-through structures).
327
310
  table_schemas: list[dict[str, Any]] = []
@@ -18,6 +18,7 @@ from typing import Self
18
18
 
19
19
  from ..types import StrAnyMap
20
20
  from ..utils import cast_str_dict
21
+ from ..utils import maybe_mapping
21
22
 
22
23
  # SECTION: EXPORTS ========================================================== #
23
24
 
@@ -56,7 +57,8 @@ class ProfileConfig:
56
57
  cls,
57
58
  obj: StrAnyMap | None,
58
59
  ) -> Self:
59
- """Parse a mapping into a :class:`ProfileConfig` instance.
60
+ """
61
+ Parse a mapping into a :class:`ProfileConfig` instance.
60
62
 
61
63
  Parameters
62
64
  ----------
@@ -73,7 +75,7 @@ class ProfileConfig:
73
75
  return cls()
74
76
 
75
77
  # Coerce all env values to strings using shared helper.
76
- env = cast_str_dict(obj.get('env'))
78
+ env = cast_str_dict(maybe_mapping(obj.get('env')))
77
79
 
78
80
  return cls(
79
81
  default_target=obj.get('default_target'),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.15.5
3
+ Version: 0.16.2
4
4
  Summary: A Swiss Army knife for simple ETL operations
5
5
  Home-page: https://github.com/Dagitali/ETLPlus
6
6
  Author: ETLPlus Team
@@ -2,10 +2,10 @@ etlplus/README.md,sha256=JaMSomnMsHrTruDnonHqe83Rv4K0-e7Wy46tMeVoleU,1468
2
2
  etlplus/__init__.py,sha256=mgTP4PJmRmsEjTCAizzzdtzAmhuHtarmPzphzdjvLgM,277
3
3
  etlplus/__main__.py,sha256=btoROneNiigyfBU7BSzPKZ1R9gzBMpxcpsbPwmuHwTM,479
4
4
  etlplus/__version__.py,sha256=1E0GMK_yUWCMQFKxXjTvyMwofi0qT2k4CDNiHWiymWE,327
5
- etlplus/enums.py,sha256=__i7UfaZJwxrrQboaqiuWTHtDAOL_qCdHVNTN-o-w8k,7539
5
+ etlplus/enums.py,sha256=8-uUOKe68cPzlmUg-e7gavkC95kbTJXRpRzvXehIsRk,6841
6
6
  etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
7
7
  etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- etlplus/types.py,sha256=-knM5bWRq49k4o-KlW-GRsENcj2P-h6oPnEmuz3EJso,6214
8
+ etlplus/types.py,sha256=J1ftMh0dmxe9ObFd3eXCbHiFvZI_5pV_hEHUpgeJQtY,6239
9
9
  etlplus/utils.py,sha256=X-k_Y8i6oDjlE5aQu9sw3gPw7O2ikiSn4uoheVv_ERc,17091
10
10
  etlplus/api/README.md,sha256=amxS_eIcsnNuVvD0x_w8nkyfedOTYbhlY0gGhaFg0DE,8705
11
11
  etlplus/api/__init__.py,sha256=PK2lQv1FbsE7ZZS_ejevFZQSuOUHGApBc22YfHAzMqA,4615
@@ -17,7 +17,7 @@ etlplus/api/errors.py,sha256=XjI2xW-sypMUNUbqfc2S57-IGyWnH3oCDFhCmKYYI_Q,4648
17
17
  etlplus/api/request_manager.py,sha256=fhzPV5x7DqpKqoLvfDR8GKhBX_QBMtvZsRXxVnQQElY,18674
18
18
  etlplus/api/retry_manager.py,sha256=aq9iNCxt-Puy4rAgKNtNucxw2eP1yqAKZ2lfgMkzbCk,11302
19
19
  etlplus/api/transport.py,sha256=abm-_WieBDSSbFanBwhmudBuVVm7LjYUb8vrlMXo7SA,9408
20
- etlplus/api/types.py,sha256=Ng1b83RaJSHn4jl-M1f1dsTgjXizQtrW4yOXAYjwk_4,7377
20
+ etlplus/api/types.py,sha256=UcTrB347So12l8NplY-_HDf2T5IwZL_2r8CJDUSAm5Q,7975
21
21
  etlplus/api/utils.py,sha256=lNBfJKz3fJ4RhvnnX3uxVZC__6-WKksYMSGGYi0RRqM,26247
22
22
  etlplus/api/pagination/__init__.py,sha256=a4UX2J0AG8RMvmHt_CCofUm5vSmFo6GAfkb8XnSXypM,1395
23
23
  etlplus/api/pagination/client.py,sha256=yMEpWqRxTCD4zRc9OYtEyUtShpGH5atiHFEAt95v2FE,5394
@@ -29,13 +29,22 @@ etlplus/api/rate_limiting/rate_limiter.py,sha256=uYxn-l2qwLUKVclDQ3vJIIP3fozJx2J
29
29
  etlplus/cli/README.md,sha256=8H_G2d3HteYIU6ReX9K9DM485QjWDT5vHMQbGD_vv20,1237
30
30
  etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
31
31
  etlplus/cli/commands.py,sha256=HFlg29tO6Jwv1NXWAHmvniLCyRSlboL55Arn9B8nZAM,25028
32
- etlplus/cli/constants.py,sha256=E6Uy4WauLa_0zkzxqImXh-bb1gKdb9sBZQVc8QOzr2Q,1943
32
+ etlplus/cli/constants.py,sha256=0F7dXIQKWUhhVu2Us527GJeknJIWpBqz7CK2e5OQgcE,1947
33
33
  etlplus/cli/handlers.py,sha256=uvbAyF6Ux8_5C-obCWZOrOP0QP0oiT-Km1hPhE8tDx0,18558
34
34
  etlplus/cli/io.py,sha256=tGGNQ4ecezqj-mD285fgBVrYdphdeqApsyV9VojOj1I,7836
35
35
  etlplus/cli/main.py,sha256=68_uJwmWajhOC9o4R_ns8IQloC9BFmAKC_9GlQOxKWg,5239
36
36
  etlplus/cli/options.py,sha256=vfXT3YLh7wG1iC-aTdSg6ItMC8l6n0Lozmy53XjqLbA,1199
37
37
  etlplus/cli/state.py,sha256=3Dq5BKct0uAvRajtc2yHbsX7wqepZOwlAMKsyvQcnqk,7918
38
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
39
48
  etlplus/database/README.md,sha256=3Af5BEGLkBmMoGOLtS1GQuj4wKPh_CwSp5NEPMf2uaY,1435
40
49
  etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
41
50
  etlplus/database/ddl.py,sha256=-7EAbyLkU8m3eYlHSNLFOQr1I4fPEbF0hTyByzjyvsU,7909
@@ -108,10 +117,10 @@ etlplus/file/zip.py,sha256=8wnmnGW_pGTx65736CzAG67XIi5y98KxucRT8sNDeuQ,4195
108
117
  etlplus/file/zsav.py,sha256=5hMuBjYeHw--UL2ZCCDn6TzJkr_YNhdQhvKI6nr3WW0,1674
109
118
  etlplus/ops/README.md,sha256=8omi7DYZhelc26JKk8Cm8QR8I3OGwziysPj1ivx41iQ,1380
110
119
  etlplus/ops/__init__.py,sha256=NIIr2f-AZj5B0piBt6gjv46Yn0SzGYxEe6BPoopRh38,1702
111
- etlplus/ops/extract.py,sha256=Z3ZCQxeXhTP08YBzKwF0mUsyTGhoxt8WACAjW6jPwpw,5990
112
- etlplus/ops/load.py,sha256=IHVqw4HhXo7k9MVnMRLKv74s-duUTGvGwPaOLZ1kS90,8532
113
- etlplus/ops/run.py,sha256=QFD_Yqdpz3oRArF962fZhYtYKDvgnCBbiBvPV-q8RCU,13522
114
- etlplus/ops/transform.py,sha256=H9DxAA-c9kwklAnmiZK21w7FMJnHfu0bLcguXfIGE-k,25417
120
+ etlplus/ops/extract.py,sha256=LOYiPrALRMF7JDBabnRF24_HKnnIcfTdfXesWdS3QZM,11020
121
+ etlplus/ops/load.py,sha256=yicciVwomUKkdbhuRqbavKBNpT2Hg813BnQzG6IgF4o,10811
122
+ etlplus/ops/run.py,sha256=FYb2W5pi2PXx5E-l5etFMUcr7UmQWfMNHew9-otWIYE,11294
123
+ etlplus/ops/transform.py,sha256=3qIJsy2lUSMPoTRWn8Yw4JocKV_ZTQx_fKRW0w73Cnc,25682
115
124
  etlplus/ops/utils.py,sha256=lJmrO1KDob-xZU8Gc2SvZvMgdYLsVoaz-fTV42KkLVo,10835
116
125
  etlplus/ops/validate.py,sha256=-OLAwQNNCmmDbmj0SB7zzYXDkJfcyBP_z9nTpqImLP0,13271
117
126
  etlplus/templates/README.md,sha256=IfPXlj1TGVA-uFWosHJhE2rabFW-znxOlOMazO9Z5cE,1361
@@ -119,16 +128,14 @@ etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM
119
128
  etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
120
129
  etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
121
130
  etlplus/workflow/README.md,sha256=D1oloiJCOHiqpqgv3m3qpRSIUOMIQcWtIsOPv7KkNI0,1652
122
- etlplus/workflow/__init__.py,sha256=LxI9VGlDBUc9ADoK8JNn3oVsGeaz1Uhjonn4Y5KIJdM,967
123
- etlplus/workflow/connector.py,sha256=vnzq9-qcU4ThWeI3ZJcL8iCoaKWrSEtZ9Jixn4il2KE,9995
124
- etlplus/workflow/dag.py,sha256=kp31dORgk0GHbct_bipU5hu_0elwBtwLsXGjMWuhFHI,2503
125
- etlplus/workflow/jobs.py,sha256=onvzsZpTpZUWIivIGR4SjbUPSZ1X0nXpxAW0O0VumhQ,8945
126
- etlplus/workflow/pipeline.py,sha256=eF6OYMXZ_YdDPZ5FJC6OTCAM-zsb8HxhX3IHpbb_N8w,9631
127
- etlplus/workflow/profile.py,sha256=dZ6P50k_ZqXnrbgrbODUqgVkymbchcEqfZR-ExjTd3M,1935
128
- etlplus/workflow/types.py,sha256=Lv1MLyM45Ob5AsMQQBqzbUaD8g7HMVxu_HoQGPeiugU,2771
129
- etlplus-0.15.5.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
130
- etlplus-0.15.5.dist-info/METADATA,sha256=mbVvp8bhCzfXrKAhOsy-t_b_cnfIVo7xAwBtLv5nwPk,28114
131
- etlplus-0.15.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
132
- etlplus-0.15.5.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
133
- etlplus-0.15.5.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
134
- etlplus-0.15.5.dist-info/RECORD,,
131
+ etlplus/workflow/__init__.py,sha256=ueothwpLruyLgr3-2hW8VT1unNyFJxdmT-l_3eB2ejc,724
132
+ etlplus/workflow/dag.py,sha256=-f1x8N1eb-PUuiOwEvFLmJwfR7JaMDJihlCHlhrFhgE,2937
133
+ etlplus/workflow/jobs.py,sha256=5DmAzmEZV6XXQ-xzowkLxFzplIh8Eno3wuCmjy79xHw,8818
134
+ etlplus/workflow/pipeline.py,sha256=PA5zhcfrk--pAg3b3x4oBf29WMj5HqR8zOozz4oEmg8,9387
135
+ etlplus/workflow/profile.py,sha256=FQU3bzBZ9_yjKC9kCXKN1FQDS9zjNUjtWB1r3UL95_Q,1993
136
+ etlplus-0.16.2.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
137
+ etlplus-0.16.2.dist-info/METADATA,sha256=QdFDSAYSrjZKyu5G8TWQSlC1Lobu8hA9qgXpnIOQ2eM,28114
138
+ etlplus-0.16.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
139
+ etlplus-0.16.2.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
140
+ etlplus-0.16.2.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
141
+ etlplus-0.16.2.dist-info/RECORD,,