etlplus 0.7.0__py3-none-any.whl → 0.8.3__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/api/README.md +24 -26
- etlplus/cli/commands.py +870 -0
- etlplus/cli/constants.py +65 -0
- etlplus/cli/handlers.py +300 -417
- etlplus/cli/io.py +320 -0
- etlplus/cli/main.py +14 -417
- etlplus/cli/options.py +49 -0
- etlplus/cli/state.py +335 -0
- etlplus/cli/types.py +33 -0
- etlplus/database/__init__.py +2 -0
- etlplus/database/ddl.py +37 -29
- etlplus/database/engine.py +10 -5
- etlplus/database/orm.py +18 -11
- etlplus/database/schema.py +3 -2
- etlplus/database/types.py +33 -0
- etlplus/types.py +5 -0
- etlplus/utils.py +0 -31
- {etlplus-0.7.0.dist-info → etlplus-0.8.3.dist-info}/METADATA +5 -4
- {etlplus-0.7.0.dist-info → etlplus-0.8.3.dist-info}/RECORD +23 -17
- etlplus/cli/app.py +0 -1367
- {etlplus-0.7.0.dist-info → etlplus-0.8.3.dist-info}/WHEEL +0 -0
- {etlplus-0.7.0.dist-info → etlplus-0.8.3.dist-info}/entry_points.txt +0 -0
- {etlplus-0.7.0.dist-info → etlplus-0.8.3.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.7.0.dist-info → etlplus-0.8.3.dist-info}/top_level.txt +0 -0
etlplus/database/orm.py
CHANGED
|
@@ -13,9 +13,8 @@ Usage
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
|
-
from collections.abc import Callable
|
|
17
|
-
from pathlib import Path
|
|
18
16
|
from typing import Any
|
|
17
|
+
from typing import Final
|
|
19
18
|
|
|
20
19
|
from sqlalchemy import Boolean
|
|
21
20
|
from sqlalchemy import CheckConstraint
|
|
@@ -41,11 +40,15 @@ from sqlalchemy.orm import DeclarativeBase
|
|
|
41
40
|
from sqlalchemy.orm import mapped_column
|
|
42
41
|
from sqlalchemy.types import TypeEngine
|
|
43
42
|
|
|
43
|
+
from ..types import StrPath
|
|
44
44
|
from .schema import ForeignKeySpec
|
|
45
45
|
from .schema import TableSpec
|
|
46
46
|
from .schema import load_table_specs
|
|
47
|
+
from .types import ModelRegistry
|
|
48
|
+
from .types import TypeFactory
|
|
49
|
+
|
|
50
|
+
# SECTION: EXPORTS ========================================================== #
|
|
47
51
|
|
|
48
|
-
# SECTION: INTERNAL CONSTANTS =============================================== #
|
|
49
52
|
|
|
50
53
|
__all__ = [
|
|
51
54
|
# Classes
|
|
@@ -57,7 +60,9 @@ __all__ = [
|
|
|
57
60
|
]
|
|
58
61
|
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
# SECTION: INTERNAL CONSTANTS =============================================== #
|
|
64
|
+
|
|
65
|
+
_TYPE_MAPPING: Final[dict[str, TypeFactory]] = {
|
|
61
66
|
'int': lambda _: Integer(),
|
|
62
67
|
'integer': lambda _: Integer(),
|
|
63
68
|
'bigint': lambda _: Integer(),
|
|
@@ -102,6 +107,8 @@ _TYPE_MAPPING: dict[str, Callable[[list[int]], TypeEngine]] = {
|
|
|
102
107
|
class Base(DeclarativeBase):
|
|
103
108
|
"""Base class for all ORM models."""
|
|
104
109
|
|
|
110
|
+
__abstract__ = True
|
|
111
|
+
|
|
105
112
|
|
|
106
113
|
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
107
114
|
|
|
@@ -191,7 +198,7 @@ def build_models(
|
|
|
191
198
|
specs: list[TableSpec],
|
|
192
199
|
*,
|
|
193
200
|
base: type[DeclarativeBase] = Base,
|
|
194
|
-
) ->
|
|
201
|
+
) -> ModelRegistry:
|
|
195
202
|
"""
|
|
196
203
|
Build SQLAlchemy ORM models from table specifications.
|
|
197
204
|
Parameters
|
|
@@ -202,10 +209,10 @@ def build_models(
|
|
|
202
209
|
Base class for the ORM models (default: :class:`Base`).
|
|
203
210
|
Returns
|
|
204
211
|
-------
|
|
205
|
-
|
|
212
|
+
ModelRegistry
|
|
206
213
|
Registry mapping fully qualified table names to ORM model classes.
|
|
207
214
|
"""
|
|
208
|
-
registry:
|
|
215
|
+
registry: ModelRegistry = {}
|
|
209
216
|
|
|
210
217
|
for spec in specs:
|
|
211
218
|
table_args: list[object] = []
|
|
@@ -302,23 +309,23 @@ def build_models(
|
|
|
302
309
|
|
|
303
310
|
|
|
304
311
|
def load_and_build_models(
|
|
305
|
-
path:
|
|
312
|
+
path: StrPath,
|
|
306
313
|
*,
|
|
307
314
|
base: type[DeclarativeBase] = Base,
|
|
308
|
-
) ->
|
|
315
|
+
) -> ModelRegistry:
|
|
309
316
|
"""
|
|
310
317
|
Load table specifications from a file and build SQLAlchemy models.
|
|
311
318
|
|
|
312
319
|
Parameters
|
|
313
320
|
----------
|
|
314
|
-
path :
|
|
321
|
+
path : StrPath
|
|
315
322
|
Path to the YAML file containing table specifications.
|
|
316
323
|
base : type[DeclarativeBase], optional
|
|
317
324
|
Base class for the ORM models (default: :class:`Base`).
|
|
318
325
|
|
|
319
326
|
Returns
|
|
320
327
|
-------
|
|
321
|
-
|
|
328
|
+
ModelRegistry
|
|
322
329
|
Registry mapping fully qualified table names to ORM model classes.
|
|
323
330
|
"""
|
|
324
331
|
return build_models(load_table_specs(path), base=base)
|
etlplus/database/schema.py
CHANGED
|
@@ -16,6 +16,7 @@ from pydantic import ConfigDict
|
|
|
16
16
|
from pydantic import Field
|
|
17
17
|
|
|
18
18
|
from ..file import File
|
|
19
|
+
from ..types import StrPath
|
|
19
20
|
|
|
20
21
|
# SECTION: EXPORTS ========================================================== #
|
|
21
22
|
|
|
@@ -244,14 +245,14 @@ class TableSpec(BaseModel):
|
|
|
244
245
|
|
|
245
246
|
|
|
246
247
|
def load_table_specs(
|
|
247
|
-
path:
|
|
248
|
+
path: StrPath,
|
|
248
249
|
) -> list[TableSpec]:
|
|
249
250
|
"""
|
|
250
251
|
Load table specifications from a YAML file.
|
|
251
252
|
|
|
252
253
|
Parameters
|
|
253
254
|
----------
|
|
254
|
-
path :
|
|
255
|
+
path : StrPath
|
|
255
256
|
Path to the YAML file containing table specifications.
|
|
256
257
|
|
|
257
258
|
Returns
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.database.types` module.
|
|
3
|
+
|
|
4
|
+
Shared type aliases leveraged across :mod:`etlplus.database` modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
|
|
11
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
12
|
+
from sqlalchemy.types import TypeEngine
|
|
13
|
+
|
|
14
|
+
# SECTION: EXPORTS ========================================================== #
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
# Type Aliases
|
|
19
|
+
'ModelRegistry',
|
|
20
|
+
'TypeFactory',
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# SECTION: TYPE ALIASES ===================================================== #
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# pylint: disable=invalid-name
|
|
28
|
+
|
|
29
|
+
# Registry mapping fully qualified table names to declarative classes.
|
|
30
|
+
type ModelRegistry = dict[str, type[DeclarativeBase]]
|
|
31
|
+
|
|
32
|
+
# Callable producing a SQLAlchemy TypeEngine from parsed parameters.
|
|
33
|
+
type TypeFactory = Callable[[list[int]], TypeEngine]
|
etlplus/types.py
CHANGED
|
@@ -225,3 +225,8 @@ type Sleeper = Callable[[float], None]
|
|
|
225
225
|
|
|
226
226
|
# Numeric timeout in seconds or ``None`` for no timeout.
|
|
227
227
|
type Timeout = float | None
|
|
228
|
+
|
|
229
|
+
# -- Templates -- #
|
|
230
|
+
|
|
231
|
+
# Allowed template keys for bundled DDL rendering.
|
|
232
|
+
type TemplateKey = Literal['ddl', 'view']
|
etlplus/utils.py
CHANGED
|
@@ -6,7 +6,6 @@ Small shared helpers used across modules.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
import argparse
|
|
10
9
|
import json
|
|
11
10
|
from collections.abc import Callable
|
|
12
11
|
from collections.abc import Mapping
|
|
@@ -22,7 +21,6 @@ from .types import StrAnyMap
|
|
|
22
21
|
__all__ = [
|
|
23
22
|
# Data utilities
|
|
24
23
|
'count_records',
|
|
25
|
-
'json_type',
|
|
26
24
|
'print_json',
|
|
27
25
|
# Mapping utilities
|
|
28
26
|
'cast_str_dict',
|
|
@@ -119,35 +117,6 @@ def count_records(
|
|
|
119
117
|
return len(data) if isinstance(data, list) else 1
|
|
120
118
|
|
|
121
119
|
|
|
122
|
-
def json_type(
|
|
123
|
-
option: str,
|
|
124
|
-
) -> Any:
|
|
125
|
-
"""
|
|
126
|
-
Argparse ``type=`` hook that parses a JSON string.
|
|
127
|
-
|
|
128
|
-
Parameters
|
|
129
|
-
----------
|
|
130
|
-
option : str
|
|
131
|
-
Raw CLI string to parse as JSON.
|
|
132
|
-
|
|
133
|
-
Returns
|
|
134
|
-
-------
|
|
135
|
-
Any
|
|
136
|
-
Parsed JSON value.
|
|
137
|
-
|
|
138
|
-
Raises
|
|
139
|
-
------
|
|
140
|
-
argparse.ArgumentTypeError
|
|
141
|
-
If the input cannot be parsed as JSON.
|
|
142
|
-
"""
|
|
143
|
-
try:
|
|
144
|
-
return json.loads(option)
|
|
145
|
-
except json.JSONDecodeError as e: # pragma: no cover - argparse path
|
|
146
|
-
raise argparse.ArgumentTypeError(
|
|
147
|
-
f'Invalid JSON: {e.msg} (pos {e.pos})',
|
|
148
|
-
) from e
|
|
149
|
-
|
|
150
|
-
|
|
151
120
|
def maybe_mapping(
|
|
152
121
|
value: Any,
|
|
153
122
|
) -> StrAnyMap | None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: etlplus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.3
|
|
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
|
|
@@ -64,7 +64,7 @@ package and command-line interface for data extraction, validation, transformati
|
|
|
64
64
|
- [Quickstart](#quickstart)
|
|
65
65
|
- [Usage](#usage)
|
|
66
66
|
- [Command Line Interface](#command-line-interface)
|
|
67
|
-
- [
|
|
67
|
+
- [Check Pipelines](#check-pipelines)
|
|
68
68
|
- [Render SQL DDL](#render-sql-ddl)
|
|
69
69
|
- [Extract Data](#extract-data)
|
|
70
70
|
- [Validate Data](#validate-data)
|
|
@@ -182,6 +182,9 @@ etlplus --help
|
|
|
182
182
|
etlplus --version
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
+
The CLI is implemented with Typer (Click-based). There is no argparse compatibility layer, so rely
|
|
186
|
+
on the documented commands/flags and run `etlplus <command> --help` for current options.
|
|
187
|
+
|
|
185
188
|
#### Check Pipelines
|
|
186
189
|
|
|
187
190
|
Use `etlplus check` to explore pipeline YAML definitions without running them. The command can print
|
|
@@ -366,8 +369,6 @@ etlplus check --config examples/configs/pipeline.yml --summary
|
|
|
366
369
|
|
|
367
370
|
# Run a job
|
|
368
371
|
etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
|
|
369
|
-
|
|
370
|
-
# Deprecated shim (will be removed): etlplus pipeline
|
|
371
372
|
```
|
|
372
373
|
|
|
373
374
|
### Complete ETL Pipeline Example
|
|
@@ -10,10 +10,10 @@ etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
10
10
|
etlplus/run.py,sha256=X4kp5FQlIWVf1_d9oSrchKau7BFDCE1Zkscvu7WPaWw,12340
|
|
11
11
|
etlplus/run_helpers.py,sha256=bj6MkaeFxjl3CeKG1HoXKx5DwAlXNERVW-GX-z1P_qQ,24373
|
|
12
12
|
etlplus/transform.py,sha256=uAUVDDHYCgx7GpVez9IK3OAZM-CnCuMa9iox3vwGGJA,25296
|
|
13
|
-
etlplus/types.py,sha256=
|
|
14
|
-
etlplus/utils.py,sha256=
|
|
13
|
+
etlplus/types.py,sha256=1hsDlnF6r76zAwaUYay-i6pCM-Y0IU5nP7Crj8PLCQ4,6157
|
|
14
|
+
etlplus/utils.py,sha256=GrRH6N9u_U-4gs6PYpO0_Y2RwsyD3Ju9cup0b0byUbk,13367
|
|
15
15
|
etlplus/validate.py,sha256=7rJoEI_SIILdPpoBqqh2UJqg9oeReDz34mYSlc3t7Qg,12989
|
|
16
|
-
etlplus/api/README.md,sha256=
|
|
16
|
+
etlplus/api/README.md,sha256=ZiyjxLz0LfFCzeYKXwtH8yY1OJ4hXCju7t2ICroFoU8,7215
|
|
17
17
|
etlplus/api/__init__.py,sha256=P2JUYFy6Ep4t6xnsBiCBfQCkQLHYYhA-yXPXCobS8Y0,4295
|
|
18
18
|
etlplus/api/auth.py,sha256=GOO5on-LoMS1GXTAhtK9rFcfpjbBcNeA6NE5UZwIq0g,12158
|
|
19
19
|
etlplus/api/config.py,sha256=wRpOaZ31sPReVzEMme0jKl_37nqgraESwuYSNxP_xDo,17397
|
|
@@ -31,9 +31,14 @@ etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-
|
|
|
31
31
|
etlplus/api/rate_limiting/config.py,sha256=2b4wIynblN-1EyMqI4aXa71SljzSjXYh5N1Nngr3jOg,9406
|
|
32
32
|
etlplus/api/rate_limiting/rate_limiter.py,sha256=Uxozqd_Ej5Lsj-M-mLT2WexChgWh7x35_YP10yqYPQA,7159
|
|
33
33
|
etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
|
|
34
|
-
etlplus/cli/
|
|
35
|
-
etlplus/cli/
|
|
36
|
-
etlplus/cli/
|
|
34
|
+
etlplus/cli/commands.py,sha256=-lO8XX2IeTvdfZLeI_UpT2kH4qZuNpfujArw3IKS2vY,22585
|
|
35
|
+
etlplus/cli/constants.py,sha256=NJ6IvNyYEI8IdB7eMcc-vteQiiIwqid5YvmUk-5DRHY,1839
|
|
36
|
+
etlplus/cli/handlers.py,sha256=WBzJZz7ESU4Hljog_ON4g9PLIW6U6io_F9op7daksKY,17781
|
|
37
|
+
etlplus/cli/io.py,sha256=hkmbDh0HFrZhDjfZbma0Lp4tc0wrTwdD9Snqa-wNAd4,7308
|
|
38
|
+
etlplus/cli/main.py,sha256=_ipwLgIzkf8ks8cbu2KkVBbY3gcdaqDR98WUBdwviiw,5191
|
|
39
|
+
etlplus/cli/options.py,sha256=dswqAdnQ4fRJGI3xEFUyMifp0ry3rm2cCaPyEqI_99c,1199
|
|
40
|
+
etlplus/cli/state.py,sha256=20uKdYddYvAlR-HUQJAV-ThyXMZaPiSk9cN-tevRIpw,7991
|
|
41
|
+
etlplus/cli/types.py,sha256=tclhKVJXDqHzlTQBYKARfqMgDOcuBJ-Zej2pvFy96WM,652
|
|
37
42
|
etlplus/config/__init__.py,sha256=VZWzOg7d2YR9NT6UwKTv44yf2FRUMjTHynkm1Dl5Qzo,1486
|
|
38
43
|
etlplus/config/connector.py,sha256=0-TIwevHbKRHVmucvyGpPd-3tB1dKHB-dj0yJ6kq5eY,9809
|
|
39
44
|
etlplus/config/jobs.py,sha256=hmzRCqt0OvCEZZR4ONKrd3lvSv0OmayjLc4yOBk3ug8,7399
|
|
@@ -41,19 +46,20 @@ etlplus/config/pipeline.py,sha256=Va4MQY6KEyKqHGMKPmh09ZcGpx95br-iNUjpkqtzVbw,95
|
|
|
41
46
|
etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
|
|
42
47
|
etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
|
|
43
48
|
etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
|
|
44
|
-
etlplus/database/__init__.py,sha256=
|
|
45
|
-
etlplus/database/ddl.py,sha256=
|
|
46
|
-
etlplus/database/engine.py,sha256=
|
|
47
|
-
etlplus/database/orm.py,sha256=
|
|
48
|
-
etlplus/database/schema.py,sha256=
|
|
49
|
+
etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
|
|
50
|
+
etlplus/database/ddl.py,sha256=z9KvHi1MPhPBLHxMDdqJgLTp3A2-lcz0gqhZ7HIE6kU,7916
|
|
51
|
+
etlplus/database/engine.py,sha256=7rr7YndA8LwyWJL8k1YhQbqxxmW4gWEUQjp0NwQcYtc,4061
|
|
52
|
+
etlplus/database/orm.py,sha256=gCSqH-CjQz6tV9133-VqgiwokK5ylun0BwXaIWfImAo,10008
|
|
53
|
+
etlplus/database/schema.py,sha256=HNTgglI8qvQLInr7gq--2lLmLKHzAZTL2MJUOIw9DlY,7025
|
|
54
|
+
etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
|
|
49
55
|
etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
|
|
50
56
|
etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
|
|
51
57
|
etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
|
|
52
58
|
etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
|
|
53
59
|
etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
|
|
54
|
-
etlplus-0.
|
|
55
|
-
etlplus-0.
|
|
56
|
-
etlplus-0.
|
|
57
|
-
etlplus-0.
|
|
58
|
-
etlplus-0.
|
|
59
|
-
etlplus-0.
|
|
60
|
+
etlplus-0.8.3.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
|
|
61
|
+
etlplus-0.8.3.dist-info/METADATA,sha256=fCmfB5aa9jgUEb2s_l-5j5b3vooe-l3M0pNYrrjhYAg,19513
|
|
62
|
+
etlplus-0.8.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
63
|
+
etlplus-0.8.3.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
|
|
64
|
+
etlplus-0.8.3.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
|
|
65
|
+
etlplus-0.8.3.dist-info/RECORD,,
|