etlplus 0.11.8__py3-none-any.whl → 0.11.9__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/cli/handlers.py +1 -1
- etlplus/database/ddl.py +1 -1
- etlplus/file/core.py +49 -45
- {etlplus-0.11.8.dist-info → etlplus-0.11.9.dist-info}/METADATA +1 -1
- {etlplus-0.11.8.dist-info → etlplus-0.11.9.dist-info}/RECORD +9 -9
- {etlplus-0.11.8.dist-info → etlplus-0.11.9.dist-info}/WHEEL +0 -0
- {etlplus-0.11.8.dist-info → etlplus-0.11.9.dist-info}/entry_points.txt +0 -0
- {etlplus-0.11.8.dist-info → etlplus-0.11.9.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.11.8.dist-info → etlplus-0.11.9.dist-info}/top_level.txt +0 -0
etlplus/cli/handlers.py
CHANGED
|
@@ -570,7 +570,7 @@ def transform_handler(
|
|
|
570
570
|
data = transform(payload, cast(TransformOperations, operations_payload))
|
|
571
571
|
|
|
572
572
|
if target and target != '-':
|
|
573
|
-
File
|
|
573
|
+
File(target, file_format=target_format).write(data)
|
|
574
574
|
print(f'Data transformed and saved to {target}')
|
|
575
575
|
return 0
|
|
576
576
|
|
etlplus/database/ddl.py
CHANGED
|
@@ -203,7 +203,7 @@ def load_table_spec(
|
|
|
203
203
|
raise ValueError('Spec must be .json, .yml, or .yaml')
|
|
204
204
|
|
|
205
205
|
try:
|
|
206
|
-
spec = File
|
|
206
|
+
spec = File(spec_path).read()
|
|
207
207
|
except ImportError as e:
|
|
208
208
|
if suffix in {'.yml', '.yaml'}:
|
|
209
209
|
raise RuntimeError(
|
etlplus/file/core.py
CHANGED
|
@@ -11,7 +11,6 @@ from dataclasses import dataclass
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
13
|
from ..types import JSONData
|
|
14
|
-
from ..types import StrPath
|
|
15
14
|
from . import csv
|
|
16
15
|
from . import json
|
|
17
16
|
from . import xml
|
|
@@ -43,7 +42,15 @@ class File:
|
|
|
43
42
|
Path to the file on disk.
|
|
44
43
|
file_format : FileFormat | None, optional
|
|
45
44
|
Explicit format. If omitted, the format is inferred from the file
|
|
46
|
-
extension (``.csv``, ``.json``,
|
|
45
|
+
extension (``.csv``, ``.json``, etc.).
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
path : StrPath
|
|
50
|
+
Path to the file on disk.
|
|
51
|
+
file_format : FileFormat | str | None, optional
|
|
52
|
+
Explicit format. If omitted, the format is inferred from the file
|
|
53
|
+
extension (``.csv``, ``.json``, etc.).
|
|
47
54
|
"""
|
|
48
55
|
|
|
49
56
|
# -- Attributes -- #
|
|
@@ -62,16 +69,10 @@ class File:
|
|
|
62
69
|
extension is unknown, the attribute is left as ``None`` and will be
|
|
63
70
|
validated later by :meth:`_ensure_format`.
|
|
64
71
|
"""
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
self.path = Path(self.path)
|
|
68
|
-
|
|
72
|
+
self.path = Path(self.path)
|
|
73
|
+
self.file_format = self._coerce_format(self.file_format)
|
|
69
74
|
if self.file_format is None:
|
|
70
|
-
|
|
71
|
-
self.file_format = self._guess_format()
|
|
72
|
-
except ValueError:
|
|
73
|
-
# Leave as None; _ensure_format() will raise on use if needed.
|
|
74
|
-
pass
|
|
75
|
+
self.file_format = self._maybe_guess_format()
|
|
75
76
|
|
|
76
77
|
# -- Internal Instance Methods -- #
|
|
77
78
|
|
|
@@ -84,6 +85,28 @@ class File:
|
|
|
84
85
|
if not self.path.exists():
|
|
85
86
|
raise FileNotFoundError(f'File not found: {self.path}')
|
|
86
87
|
|
|
88
|
+
def _coerce_format(
|
|
89
|
+
self,
|
|
90
|
+
file_format: FileFormat | str | None,
|
|
91
|
+
) -> FileFormat | None:
|
|
92
|
+
"""
|
|
93
|
+
Normalize the file format input.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
file_format : FileFormat | str | None
|
|
98
|
+
File format specifier. Strings are coerced into
|
|
99
|
+
:class:`FileFormat`.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
FileFormat | None
|
|
104
|
+
A normalized file format, or ``None`` when unspecified.
|
|
105
|
+
"""
|
|
106
|
+
if file_format is None or isinstance(file_format, FileFormat):
|
|
107
|
+
return file_format
|
|
108
|
+
return FileFormat.coerce(file_format)
|
|
109
|
+
|
|
87
110
|
def _ensure_format(self) -> FileFormat:
|
|
88
111
|
"""
|
|
89
112
|
Resolve the active format, guessing from extension if needed.
|
|
@@ -125,6 +148,21 @@ class File:
|
|
|
125
148
|
f'Cannot infer file format from extension {self.path.suffix!r}',
|
|
126
149
|
)
|
|
127
150
|
|
|
151
|
+
def _maybe_guess_format(self) -> FileFormat | None:
|
|
152
|
+
"""
|
|
153
|
+
Try to infer the format, returning ``None`` if it cannot be inferred.
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
FileFormat | None
|
|
158
|
+
The inferred format, or ``None`` if inference fails.
|
|
159
|
+
"""
|
|
160
|
+
try:
|
|
161
|
+
return self._guess_format()
|
|
162
|
+
except ValueError:
|
|
163
|
+
# Leave as None; _ensure_format() will raise on use if needed.
|
|
164
|
+
return None
|
|
165
|
+
|
|
128
166
|
# -- Instance Methods -- #
|
|
129
167
|
|
|
130
168
|
def read(self) -> JSONData:
|
|
@@ -192,37 +230,3 @@ class File:
|
|
|
192
230
|
case FileFormat.YAML:
|
|
193
231
|
return yaml.write(self.path, data)
|
|
194
232
|
raise ValueError(f'Unsupported format: {fmt}')
|
|
195
|
-
|
|
196
|
-
# -- Class Methods -- #
|
|
197
|
-
|
|
198
|
-
@classmethod
|
|
199
|
-
def from_path(
|
|
200
|
-
cls,
|
|
201
|
-
path: StrPath,
|
|
202
|
-
*,
|
|
203
|
-
file_format: FileFormat | str | None = None,
|
|
204
|
-
) -> File:
|
|
205
|
-
"""
|
|
206
|
-
Create a :class:`File` from any path-like and optional format.
|
|
207
|
-
|
|
208
|
-
Parameters
|
|
209
|
-
----------
|
|
210
|
-
path : StrPath
|
|
211
|
-
Path to the file on disk.
|
|
212
|
-
file_format : FileFormat | str | None, optional
|
|
213
|
-
Explicit format. If omitted, the format is inferred from the file
|
|
214
|
-
extension (``.csv``, ``.json``, or ``.xml``).
|
|
215
|
-
|
|
216
|
-
Returns
|
|
217
|
-
-------
|
|
218
|
-
File
|
|
219
|
-
The constructed :class:`File` instance.
|
|
220
|
-
"""
|
|
221
|
-
resolved = Path(path)
|
|
222
|
-
ff: FileFormat | None
|
|
223
|
-
if isinstance(file_format, str):
|
|
224
|
-
ff = FileFormat.coerce(file_format)
|
|
225
|
-
else:
|
|
226
|
-
ff = file_format
|
|
227
|
-
|
|
228
|
-
return cls(resolved, ff)
|
|
@@ -32,7 +32,7 @@ etlplus/api/rate_limiting/rate_limiter.py,sha256=Uxozqd_Ej5Lsj-M-mLT2WexChgWh7x3
|
|
|
32
32
|
etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
|
|
33
33
|
etlplus/cli/commands.py,sha256=g8_m3A8HEMyTRu2HctNiRoi2gtB5oSZCUEcyq-PIXos,24669
|
|
34
34
|
etlplus/cli/constants.py,sha256=E6Uy4WauLa_0zkzxqImXh-bb1gKdb9sBZQVc8QOzr2Q,1943
|
|
35
|
-
etlplus/cli/handlers.py,sha256=
|
|
35
|
+
etlplus/cli/handlers.py,sha256=FvnYWKRg4u7iCcIvAJtpjDEbqw2DHj3G34NXJbMwnHk,17754
|
|
36
36
|
etlplus/cli/io.py,sha256=EFaBPYaBOyOllfMQWXgTjy-MPiGfNejicpD7ROrPyAE,7840
|
|
37
37
|
etlplus/cli/main.py,sha256=IgeqxypixfwLHR-QcpgVMQ7vMZ865bXOh2oO9v-BWeM,5234
|
|
38
38
|
etlplus/cli/options.py,sha256=vfXT3YLh7wG1iC-aTdSg6ItMC8l6n0Lozmy53XjqLbA,1199
|
|
@@ -46,13 +46,13 @@ etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,189
|
|
|
46
46
|
etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
|
|
47
47
|
etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
|
|
48
48
|
etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
|
|
49
|
-
etlplus/database/ddl.py,sha256=
|
|
49
|
+
etlplus/database/ddl.py,sha256=0dEM9SJMMabkhI_h-Fc0j9a1Sl5lSyZdI0bIeBVGm10,7913
|
|
50
50
|
etlplus/database/engine.py,sha256=PUxXLvLPyc-KaxuW7fXe12wYci7EvUp-Ad1H3bGhUog,4058
|
|
51
51
|
etlplus/database/orm.py,sha256=gCSqH-CjQz6tV9133-VqgiwokK5ylun0BwXaIWfImAo,10008
|
|
52
52
|
etlplus/database/schema.py,sha256=813C0Dd3WE53KTYot4dgjAxctgKXLXx-8_Rk_4r2e28,7022
|
|
53
53
|
etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
|
|
54
54
|
etlplus/file/__init__.py,sha256=X03bosSM-uSd6dh3ur0un6_ozFRw2Tm4PE6kVUjtXK8,475
|
|
55
|
-
etlplus/file/core.py,sha256=
|
|
55
|
+
etlplus/file/core.py,sha256=oMEzvs2RKtE95TX-HWz__PavjHwlhcTk-D9Jti99AXU,6675
|
|
56
56
|
etlplus/file/csv.py,sha256=VbMW_NaqCw03HlfvYzb9MoAgCXI3cl9qc4dASkTHoyw,1880
|
|
57
57
|
etlplus/file/enums.py,sha256=rwrbwj6PejG0c5v6jzcsmeNu9cSqDyWB1foIuM5UyJo,6648
|
|
58
58
|
etlplus/file/json.py,sha256=xSV5PkZ_tZQuZNdLr1FQUwuCQXyL7Ch3WRJ3hkw0p68,1911
|
|
@@ -63,9 +63,9 @@ etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,
|
|
|
63
63
|
etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
|
|
64
64
|
etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
|
|
65
65
|
etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
|
|
66
|
-
etlplus-0.11.
|
|
67
|
-
etlplus-0.11.
|
|
68
|
-
etlplus-0.11.
|
|
69
|
-
etlplus-0.11.
|
|
70
|
-
etlplus-0.11.
|
|
71
|
-
etlplus-0.11.
|
|
66
|
+
etlplus-0.11.9.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
|
|
67
|
+
etlplus-0.11.9.dist-info/METADATA,sha256=G8vVH8Z8HJYymyRoBpcpm8SY4o7PkGkgjQsu2q_d_8M,21036
|
|
68
|
+
etlplus-0.11.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
69
|
+
etlplus-0.11.9.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
|
|
70
|
+
etlplus-0.11.9.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
|
|
71
|
+
etlplus-0.11.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|