etlplus 0.7.1__py3-none-any.whl → 0.7.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.
@@ -18,6 +18,7 @@ from .engine import engine
18
18
  from .engine import load_database_url_from_config
19
19
  from .engine import make_engine
20
20
  from .engine import session
21
+ from .orm import Base
21
22
  from .orm import build_models
22
23
  from .orm import load_and_build_models
23
24
  from .schema import load_table_specs
@@ -36,6 +37,7 @@ __all__ = [
36
37
  'render_table_sql',
37
38
  'render_tables',
38
39
  'render_tables_to_string',
40
+ 'Base',
39
41
  # Singletons
40
42
  'engine',
41
43
  'session',
etlplus/database/ddl.py CHANGED
@@ -15,7 +15,6 @@ import os
15
15
  from collections.abc import Iterable
16
16
  from collections.abc import Mapping
17
17
  from pathlib import Path
18
- from typing import Any
19
18
  from typing import Final
20
19
 
21
20
  from jinja2 import DictLoader
@@ -24,6 +23,9 @@ from jinja2 import FileSystemLoader
24
23
  from jinja2 import StrictUndefined
25
24
 
26
25
  from ..file import File
26
+ from ..types import StrAnyMap
27
+ from ..types import StrPath
28
+ from .types import TemplateKey
27
29
 
28
30
  # SECTION: EXPORTS ========================================================== #
29
31
 
@@ -52,7 +54,7 @@ _SUPPORTED_SPEC_SUFFIXES: Final[frozenset[str]] = frozenset(
52
54
  # SECTION: CONSTANTS ======================================================== #
53
55
 
54
56
 
55
- TEMPLATES: Final[dict[str, str]] = {
57
+ TEMPLATES: Final[dict[TemplateKey, str]] = {
56
58
  'ddl': 'ddl.sql.j2',
57
59
  'view': 'view.sql.j2',
58
60
  }
@@ -64,7 +66,8 @@ TEMPLATES: Final[dict[str, str]] = {
64
66
  def _load_template_text(
65
67
  filename: str,
66
68
  ) -> str:
67
- """Return the bundled template text.
69
+ """
70
+ Return the bundled template text.
68
71
 
69
72
  Parameters
70
73
  ----------
@@ -99,16 +102,17 @@ def _load_template_text(
99
102
 
100
103
  def _resolve_template(
101
104
  *,
102
- template_key: str | None,
103
- template_path: str | None,
105
+ template_key: TemplateKey | None,
106
+ template_path: StrPath | None,
104
107
  ) -> tuple[Environment, str]:
105
- """Return environment and template name for rendering.
108
+ """
109
+ Return environment and template name for rendering.
106
110
 
107
111
  Parameters
108
112
  ----------
109
- template_key : str | None
113
+ template_key : TemplateKey | None
110
114
  Named template key bundled with the package.
111
- template_path : str | None
115
+ template_path : StrPath | None
112
116
  Explicit template file override.
113
117
 
114
118
  Returns
@@ -123,7 +127,11 @@ def _resolve_template(
123
127
  ValueError
124
128
  If the template key is unknown.
125
129
  """
126
- file_override = template_path or os.environ.get('TEMPLATE_NAME')
130
+ file_override = (
131
+ str(template_path)
132
+ if template_path is not None
133
+ else os.environ.get('TEMPLATE_NAME')
134
+ )
127
135
  if file_override:
128
136
  path = Path(file_override)
129
137
  if not path.exists():
@@ -137,14 +145,14 @@ def _resolve_template(
137
145
  )
138
146
  return env, path.name
139
147
 
140
- key = (template_key or 'ddl').strip()
148
+ key: TemplateKey = template_key or 'ddl'
141
149
  if key not in TEMPLATES:
142
150
  choices = ', '.join(sorted(TEMPLATES))
143
151
  raise ValueError(
144
152
  f'Unknown template key "{key}". Choose from: {choices}',
145
153
  )
146
154
 
147
- # Load template from package data
155
+ # Load template from package data.
148
156
  template_filename = TEMPLATES[key]
149
157
  template_source = _load_template_text(template_filename)
150
158
 
@@ -161,19 +169,19 @@ def _resolve_template(
161
169
 
162
170
 
163
171
  def load_table_spec(
164
- path: Path | str,
165
- ) -> dict[str, Any]:
172
+ path: StrPath,
173
+ ) -> StrAnyMap:
166
174
  """
167
175
  Load a table specification from disk.
168
176
 
169
177
  Parameters
170
178
  ----------
171
- path : Path | str
179
+ path : StrPath
172
180
  Path to the JSON or YAML specification file.
173
181
 
174
182
  Returns
175
183
  -------
176
- dict[str, Any]
184
+ StrAnyMap
177
185
  Parsed table specification mapping.
178
186
 
179
187
  Raises
@@ -210,9 +218,9 @@ def load_table_spec(
210
218
 
211
219
 
212
220
  def render_table_sql(
213
- spec: Mapping[str, Any],
221
+ spec: StrAnyMap,
214
222
  *,
215
- template: str | None = 'ddl',
223
+ template: TemplateKey | None = 'ddl',
216
224
  template_path: str | None = None,
217
225
  ) -> str:
218
226
  """
@@ -220,9 +228,9 @@ def render_table_sql(
220
228
 
221
229
  Parameters
222
230
  ----------
223
- spec : Mapping[str, Any]
231
+ spec : StrAnyMap
224
232
  Table specification mapping.
225
- template : str | None, optional
233
+ template : TemplateKey | None, optional
226
234
  Template key to use (default: 'ddl').
227
235
  template_path : str | None, optional
228
236
  Path to a custom template file (overrides ``template``).
@@ -241,9 +249,9 @@ def render_table_sql(
241
249
 
242
250
 
243
251
  def render_tables(
244
- specs: Iterable[Mapping[str, Any]],
252
+ specs: Iterable[StrAnyMap],
245
253
  *,
246
- template: str | None = 'ddl',
254
+ template: TemplateKey | None = 'ddl',
247
255
  template_path: str | None = None,
248
256
  ) -> list[str]:
249
257
  """
@@ -251,9 +259,9 @@ def render_tables(
251
259
 
252
260
  Parameters
253
261
  ----------
254
- specs : Iterable[Mapping[str, Any]]
262
+ specs : Iterable[StrAnyMap]
255
263
  Table specification mappings.
256
- template : str | None, optional
264
+ template : TemplateKey | None, optional
257
265
  Template key to use (default: 'ddl').
258
266
  template_path : str | None, optional
259
267
  Path to a custom template file (overrides ``template``).
@@ -271,21 +279,21 @@ def render_tables(
271
279
 
272
280
 
273
281
  def render_tables_to_string(
274
- spec_paths: Iterable[Path | str],
282
+ spec_paths: Iterable[StrPath],
275
283
  *,
276
- template: str | None = 'ddl',
277
- template_path: Path | str | None = None,
284
+ template: TemplateKey | None = 'ddl',
285
+ template_path: StrPath | None = None,
278
286
  ) -> str:
279
287
  """
280
288
  Render one or more specs and concatenate the SQL payloads.
281
289
 
282
290
  Parameters
283
291
  ----------
284
- spec_paths : Iterable[Path | str]
292
+ spec_paths : Iterable[StrPath]
285
293
  Paths to table specification files.
286
- template : str | None, optional
294
+ template : TemplateKey | None, optional
287
295
  Template key bundled with ETLPlus. Defaults to ``'ddl'``.
288
- template_path : Path | str | None, optional
296
+ template_path : StrPath | None, optional
289
297
  Custom Jinja template to override the bundled templates.
290
298
 
291
299
  Returns
@@ -10,12 +10,15 @@ import os
10
10
  from collections.abc import Mapping
11
11
  from pathlib import Path
12
12
  from typing import Any
13
+ from typing import Final
13
14
 
14
15
  from sqlalchemy import create_engine
15
16
  from sqlalchemy.engine import Engine
16
17
  from sqlalchemy.orm import sessionmaker
17
18
 
18
19
  from ..file import File
20
+ from ..types import StrAnyMap
21
+ from ..types import StrPath
19
22
 
20
23
  # SECTION: EXPORTS ========================================================== #
21
24
 
@@ -33,7 +36,7 @@ __all__ = [
33
36
  # SECTION: INTERNAL CONSTANTS =============================================== #
34
37
 
35
38
 
36
- DATABASE_URL: str = (
39
+ DATABASE_URL: Final[str] = (
37
40
  os.getenv('DATABASE_URL')
38
41
  or os.getenv('DATABASE_DSN')
39
42
  or 'sqlite+pysqlite:///:memory:'
@@ -43,13 +46,15 @@ DATABASE_URL: str = (
43
46
  # SECTION: INTERNAL FUNCTIONS =============================================== #
44
47
 
45
48
 
46
- def _resolve_url_from_mapping(cfg: Mapping[str, Any]) -> str | None:
49
+ def _resolve_url_from_mapping(
50
+ cfg: StrAnyMap,
51
+ ) -> str | None:
47
52
  """
48
53
  Return a URL/DSN from a mapping if present.
49
54
 
50
55
  Parameters
51
56
  ----------
52
- cfg : Mapping[str, Any]
57
+ cfg : StrAnyMap
53
58
  Configuration mapping potentially containing connection fields.
54
59
 
55
60
  Returns
@@ -74,7 +79,7 @@ def _resolve_url_from_mapping(cfg: Mapping[str, Any]) -> str | None:
74
79
 
75
80
 
76
81
  def load_database_url_from_config(
77
- path: str | Path,
82
+ path: StrPath,
78
83
  *,
79
84
  name: str | None = None,
80
85
  ) -> str:
@@ -88,7 +93,7 @@ def load_database_url_from_config(
88
93
 
89
94
  Parameters
90
95
  ----------
91
- path : str | Path
96
+ path : StrPath
92
97
  Location of the configuration file.
93
98
  name : str | None, optional
94
99
  Named database entry under the ``databases`` map (default:
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
- _TYPE_MAPPING: dict[str, Callable[[list[int]], TypeEngine]] = {
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
- ) -> dict[str, type[DeclarativeBase]]:
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
- dict[str, type[DeclarativeBase]]
212
+ ModelRegistry
206
213
  Registry mapping fully qualified table names to ORM model classes.
207
214
  """
208
- registry: dict[str, type[DeclarativeBase]] = {}
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: str | Path,
312
+ path: StrPath,
306
313
  *,
307
314
  base: type[DeclarativeBase] = Base,
308
- ) -> dict[str, type[DeclarativeBase]]:
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 : str | 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
- dict[str, type[DeclarativeBase]]
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)
@@ -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: str | 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 : str | Path
255
+ path : StrPath
255
256
  Path to the YAML file containing table specifications.
256
257
 
257
258
  Returns
@@ -0,0 +1,38 @@
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
+ from typing import Literal
11
+
12
+ from sqlalchemy.orm import DeclarativeBase
13
+ from sqlalchemy.types import TypeEngine
14
+
15
+ # SECTION: EXPORTS ========================================================== #
16
+
17
+
18
+ __all__ = [
19
+ # Type Aliases
20
+ 'ModelRegistry',
21
+ 'TemplateKey',
22
+ 'TypeFactory',
23
+ ]
24
+
25
+
26
+ # SECTION: TYPE ALIASES ===================================================== #
27
+
28
+
29
+ # pylint: disable=invalid-name
30
+
31
+ # Registry mapping fully qualified table names to declarative classes.
32
+ type ModelRegistry = dict[str, type[DeclarativeBase]]
33
+
34
+ # Allowed template keys for bundled DDL rendering.
35
+ type TemplateKey = Literal['ddl', 'view']
36
+
37
+ # Callable producing a SQLAlchemy TypeEngine from parsed parameters.
38
+ type TypeFactory = Callable[[list[int]], TypeEngine]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.7.1
3
+ Version: 0.7.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
@@ -41,19 +41,20 @@ etlplus/config/pipeline.py,sha256=Va4MQY6KEyKqHGMKPmh09ZcGpx95br-iNUjpkqtzVbw,95
41
41
  etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
42
42
  etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
43
43
  etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
44
- etlplus/database/__init__.py,sha256=0gWnMlQiVHS6SVUxIT9zklQUHU36y-2RF_gN1cx7icg,1018
45
- etlplus/database/ddl.py,sha256=lIar9KIOoBRslp_P0DnpoMDXzkjt64J5-iVV7CeSV_M,7747
46
- etlplus/database/engine.py,sha256=54f-XtNKIuogJhsLV9cX_xPoBwcl_HNJTL5HqMCi8kw,3986
47
- etlplus/database/orm.py,sha256=StjeguokM70oNKq7mNXLyc4_mYUZR-EKW3oGRlsd8QE,9962
48
- etlplus/database/schema.py,sha256=BmRP2wwX2xex1phLm0tnHrP6A2AQgguA-hSLnK0xwwc,7003
44
+ etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
45
+ etlplus/database/ddl.py,sha256=gpybAxs5xCukr9HkzNHm2sJo1_q42Qg70jiSEl60yaY,7915
46
+ etlplus/database/engine.py,sha256=7rr7YndA8LwyWJL8k1YhQbqxxmW4gWEUQjp0NwQcYtc,4061
47
+ etlplus/database/orm.py,sha256=gCSqH-CjQz6tV9133-VqgiwokK5ylun0BwXaIWfImAo,10008
48
+ etlplus/database/schema.py,sha256=HNTgglI8qvQLInr7gq--2lLmLKHzAZTL2MJUOIw9DlY,7025
49
+ etlplus/database/types.py,sha256=42d6MVc0x58T7xITus-vOiHmrL4SwwxI7rvZrsIhDl4,935
49
50
  etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
50
51
  etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
51
52
  etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
52
53
  etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
53
54
  etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
54
- etlplus-0.7.1.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
55
- etlplus-0.7.1.dist-info/METADATA,sha256=Y2MYm3-8rGosgvY29er_Pt_j9qxgBVndnRXK3k0xUBM,19383
56
- etlplus-0.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- etlplus-0.7.1.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
58
- etlplus-0.7.1.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
59
- etlplus-0.7.1.dist-info/RECORD,,
55
+ etlplus-0.7.2.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
56
+ etlplus-0.7.2.dist-info/METADATA,sha256=AHc-GAyaSgkfORQiT6Da2wsEtOtAxGMdPjO1jrZBNPg,19383
57
+ etlplus-0.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
58
+ etlplus-0.7.2.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
59
+ etlplus-0.7.2.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
60
+ etlplus-0.7.2.dist-info/RECORD,,