etlplus 0.10.4__py3-none-any.whl → 0.11.1__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/file/yaml.py ADDED
@@ -0,0 +1,125 @@
1
+ """
2
+ :mod:`etlplus.file.yaml` module.
3
+
4
+ Optional YAML read/write helpers.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ from typing import Any
11
+ from typing import cast
12
+
13
+ from ..types import JSONData
14
+ from ..types import JSONDict
15
+ from ..types import JSONList
16
+ from ..utils import count_records
17
+
18
+ # SECTION: INTERNAL CONSTANTS =============================================== #
19
+
20
+
21
+ # Optional YAML support (lazy-loaded to avoid hard dependency)
22
+ # Cached access function to avoid global statements.
23
+ _YAML_CACHE: dict[str, Any] = {}
24
+
25
+
26
+ # SECTION: INTERNAL FUNCTIONS =============================================== #
27
+
28
+
29
+ def _get_yaml() -> Any:
30
+ """
31
+ Return the PyYAML module, importing it on first use.
32
+
33
+ Raises an informative ImportError if the optional dependency is missing.
34
+ """
35
+ mod = _YAML_CACHE.get('mod')
36
+ if mod is not None: # pragma: no cover - tiny branch
37
+ return mod
38
+ try:
39
+ _yaml_mod = __import__('yaml') # type: ignore[assignment]
40
+ except ImportError as e: # pragma: no cover
41
+ raise ImportError(
42
+ 'YAML support requires optional dependency "PyYAML".\n'
43
+ 'Install with: pip install PyYAML',
44
+ ) from e
45
+ _YAML_CACHE['mod'] = _yaml_mod
46
+
47
+ return _yaml_mod
48
+
49
+
50
+ def _require_yaml() -> None:
51
+ """Ensure PyYAML is available or raise an informative error."""
52
+ _get_yaml()
53
+
54
+
55
+ # SECTION: FUNCTIONS ======================================================== #
56
+
57
+
58
+ def read(
59
+ path: Path,
60
+ ) -> JSONData:
61
+ """
62
+ Load and validate YAML payloads from ``path``.
63
+
64
+ Parameters
65
+ ----------
66
+ path : Path
67
+ Path to the YAML file on disk.
68
+
69
+ Returns
70
+ -------
71
+ JSONData
72
+ The structured data read from the YAML file.
73
+
74
+ Raises
75
+ ------
76
+ TypeError
77
+ If the YAML root is not an object or an array of objects.
78
+ """
79
+ _require_yaml()
80
+
81
+ with path.open('r', encoding='utf-8') as handle:
82
+ loaded = _get_yaml().safe_load(handle)
83
+
84
+ if isinstance(loaded, dict):
85
+ return cast(JSONDict, loaded)
86
+ if isinstance(loaded, list):
87
+ if all(isinstance(item, dict) for item in loaded):
88
+ return cast(JSONList, loaded)
89
+ raise TypeError(
90
+ 'YAML array must contain only objects (dicts) when loading',
91
+ )
92
+ raise TypeError(
93
+ 'YAML root must be an object or an array of objects when loading',
94
+ )
95
+
96
+
97
+ def write(
98
+ path: Path,
99
+ data: JSONData,
100
+ ) -> int:
101
+ """
102
+ Write ``data`` as YAML to ``path`` and return record count.
103
+
104
+ Parameters
105
+ ----------
106
+ path : Path
107
+ Path to the YAML file on disk.
108
+ data : JSONData
109
+ Data to write as YAML.
110
+
111
+ Returns
112
+ -------
113
+ int
114
+ The number of records written.
115
+ """
116
+ _require_yaml()
117
+ with path.open('w', encoding='utf-8') as handle:
118
+ _get_yaml().safe_dump(
119
+ data,
120
+ handle,
121
+ sort_keys=False,
122
+ allow_unicode=True,
123
+ default_flow_style=False,
124
+ )
125
+ return count_records(data)
etlplus/load.py CHANGED
@@ -15,12 +15,12 @@ from typing import cast
15
15
  import requests # type: ignore[import]
16
16
 
17
17
  from .enums import DataConnectorType
18
- from .enums import FileFormat
19
18
  from .enums import HttpMethod
20
19
  from .enums import coerce_data_connector_type
21
- from .enums import coerce_file_format
22
20
  from .enums import coerce_http_method
23
21
  from .file import File
22
+ from .file import FileFormat
23
+ from .file import coerce_file_format
24
24
  from .types import JSONData
25
25
  from .types import JSONDict
26
26
  from .types import JSONList
@@ -101,7 +101,7 @@ def load_data(
101
101
  return cast(JSONData, source)
102
102
 
103
103
  if isinstance(source, Path):
104
- return File(source, FileFormat.JSON).read_json()
104
+ return File(source, FileFormat.JSON).read()
105
105
 
106
106
  if isinstance(source, str):
107
107
  # Special case: '-' means read JSON from STDIN (Unix convention).
@@ -111,7 +111,7 @@ def load_data(
111
111
  candidate = Path(source)
112
112
  if candidate.exists():
113
113
  try:
114
- return File(candidate, FileFormat.JSON).read_json()
114
+ return File(candidate, FileFormat.JSON).read()
115
115
  except (OSError, json.JSONDecodeError, ValueError):
116
116
  # Fall back to treating the string as raw JSON content.
117
117
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.10.4
3
+ Version: 0.11.1
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
@@ -1,10 +1,9 @@
1
1
  etlplus/__init__.py,sha256=M2gScnyir6WOMAh_EuoQIiAzdcTls0_5hbd_Q6of8I0,1021
2
2
  etlplus/__main__.py,sha256=btoROneNiigyfBU7BSzPKZ1R9gzBMpxcpsbPwmuHwTM,479
3
3
  etlplus/__version__.py,sha256=1E0GMK_yUWCMQFKxXjTvyMwofi0qT2k4CDNiHWiymWE,327
4
- etlplus/enums.py,sha256=8hzprOLyeCCzlHaXpG4VfgmxPSEdlZeOnHLFzBneKNs,15969
5
- etlplus/extract.py,sha256=f44JdHhNTACxgn44USx05paKTwq7LQY-V4wANCW9hVM,6173
6
- etlplus/file.py,sha256=B-zebTrIFDKaaKzA9Fq5-L0JwDNYa2T--_6veR3N03s,17939
7
- etlplus/load.py,sha256=R_y0_vtsEo1bwxWVQu2bfhB5ZIJoIoWu2ycCdvY4RnE,8737
4
+ etlplus/enums.py,sha256=r_KhmzIY-PSjfKfkz8uQtb_B5clfeTokRwqXreSWlNI,9150
5
+ etlplus/extract.py,sha256=V1cifAktXW4BWyoaaJPgR6R_pAG6TPKq-XOzxb49_ic,6171
6
+ etlplus/load.py,sha256=t9VfEuWyVq7MukuaLWrjj7oGyi6VDnXkiBXGPNAh1zE,8725
8
7
  etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
9
8
  etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
9
  etlplus/run.py,sha256=X4kp5FQlIWVf1_d9oSrchKau7BFDCE1Zkscvu7WPaWw,12340
@@ -31,10 +30,10 @@ etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-
31
30
  etlplus/api/rate_limiting/config.py,sha256=2b4wIynblN-1EyMqI4aXa71SljzSjXYh5N1Nngr3jOg,9406
32
31
  etlplus/api/rate_limiting/rate_limiter.py,sha256=Uxozqd_Ej5Lsj-M-mLT2WexChgWh7x35_YP10yqYPQA,7159
33
32
  etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
34
- etlplus/cli/commands.py,sha256=BK2qmFsser6AXOgEvpiadrYMIiwviAzqkSxMlBhRXRw,24670
35
- etlplus/cli/constants.py,sha256=KIZj7J2tNf5mJbkqAdZmu5FXYW2FQmxwgeOKWc3-3Hg,1944
33
+ etlplus/cli/commands.py,sha256=g8_m3A8HEMyTRu2HctNiRoi2gtB5oSZCUEcyq-PIXos,24669
34
+ etlplus/cli/constants.py,sha256=E6Uy4WauLa_0zkzxqImXh-bb1gKdb9sBZQVc8QOzr2Q,1943
36
35
  etlplus/cli/handlers.py,sha256=K0GazvrPgocJ-63HZqF0xhyJk8TB1Gcj-eIbWltXKRU,17759
37
- etlplus/cli/io.py,sha256=7sldiZz4-Geomge5IO_XYykXPa6UiORfUWzLCdQePG8,7846
36
+ etlplus/cli/io.py,sha256=EFaBPYaBOyOllfMQWXgTjy-MPiGfNejicpD7ROrPyAE,7840
38
37
  etlplus/cli/main.py,sha256=IgeqxypixfwLHR-QcpgVMQ7vMZ865bXOh2oO9v-BWeM,5234
39
38
  etlplus/cli/options.py,sha256=vfXT3YLh7wG1iC-aTdSg6ItMC8l6n0Lozmy53XjqLbA,1199
40
39
  etlplus/cli/state.py,sha256=Pfd8ru0wYIN7eGp1_A0tioqs1LiCDZCuJ6AnjZb6yYQ,8027
@@ -42,7 +41,7 @@ etlplus/cli/types.py,sha256=tclhKVJXDqHzlTQBYKARfqMgDOcuBJ-Zej2pvFy96WM,652
42
41
  etlplus/config/__init__.py,sha256=VZWzOg7d2YR9NT6UwKTv44yf2FRUMjTHynkm1Dl5Qzo,1486
43
42
  etlplus/config/connector.py,sha256=0-TIwevHbKRHVmucvyGpPd-3tB1dKHB-dj0yJ6kq5eY,9809
44
43
  etlplus/config/jobs.py,sha256=hmzRCqt0OvCEZZR4ONKrd3lvSv0OmayjLc4yOBk3ug8,7399
45
- etlplus/config/pipeline.py,sha256=Va4MQY6KEyKqHGMKPmh09ZcGpx95br-iNUjpkqtzVbw,9500
44
+ etlplus/config/pipeline.py,sha256=m4Jh0ctFcKrIx6zR7LEC0sYY5wq0o8NqOruWPlz6qmA,9494
46
45
  etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
47
46
  etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
48
47
  etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
@@ -52,14 +51,21 @@ etlplus/database/engine.py,sha256=7rr7YndA8LwyWJL8k1YhQbqxxmW4gWEUQjp0NwQcYtc,40
52
51
  etlplus/database/orm.py,sha256=gCSqH-CjQz6tV9133-VqgiwokK5ylun0BwXaIWfImAo,10008
53
52
  etlplus/database/schema.py,sha256=HNTgglI8qvQLInr7gq--2lLmLKHzAZTL2MJUOIw9DlY,7025
54
53
  etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
54
+ etlplus/file/__init__.py,sha256=xd_Tvtzx7_PrGVb4Cjqp-v8p3P2qTPA3cZ14VzA1-0g,539
55
+ etlplus/file/core.py,sha256=NXTGSIKIo7HvLDlMtme37_d4NUhsf4RUNKp5mTj-wqU,8131
56
+ etlplus/file/csv.py,sha256=VbMW_NaqCw03HlfvYzb9MoAgCXI3cl9qc4dASkTHoyw,1880
57
+ etlplus/file/enums.py,sha256=NjgXQ0f53Xa1eyGKHvYkk58udjQI5TQfLVaoawfdXY0,7520
58
+ etlplus/file/json.py,sha256=xSV5PkZ_tZQuZNdLr1FQUwuCQXyL7Ch3WRJ3hkw0p68,1911
59
+ etlplus/file/xml.py,sha256=vjate5u9Z26LPlpvZsdzpqXsIUZRgen7oHa3ly-aIhs,3905
60
+ etlplus/file/yaml.py,sha256=6KaWoG7oYB26EHX2TZ7LOgigO11Hoq3MH--adFq_Eck,3004
55
61
  etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
56
62
  etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
57
63
  etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
58
64
  etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
59
65
  etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
60
- etlplus-0.10.4.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
61
- etlplus-0.10.4.dist-info/METADATA,sha256=M_lQUZ5o-JaD1KuZk_t0LeHbaOj_SdqqaJQSbDCW-zY,21036
62
- etlplus-0.10.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
- etlplus-0.10.4.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
64
- etlplus-0.10.4.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
65
- etlplus-0.10.4.dist-info/RECORD,,
66
+ etlplus-0.11.1.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
67
+ etlplus-0.11.1.dist-info/METADATA,sha256=zIJEvlxTNB13talthWkdGrbTWBNQZB8rkEn3Zwc9888,21036
68
+ etlplus-0.11.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
69
+ etlplus-0.11.1.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
70
+ etlplus-0.11.1.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
71
+ etlplus-0.11.1.dist-info/RECORD,,