dlt-utils-lib 1.2.5__tar.gz → 1.2.6__tar.gz
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.
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/main_cdc_utils.py +21 -12
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils_lib.egg-info/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/tests/test_main_cdc_utils.py +51 -83
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/__init__.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/dlt_autoloader_recovery_configuration.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/dlt_metadata_receiver.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/dlt_transformations.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils_lib.egg-info/SOURCES.txt +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils_lib.egg-info/dependency_links.txt +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils_lib.egg-info/requires.txt +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils_lib.egg-info/top_level.txt +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/setup.cfg +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/setup.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/tests/__init__.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/tests/conftest.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/tests/test_main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/tests/test_transformation_module.py +0 -0
|
@@ -139,6 +139,27 @@ def create_bronze_table_definition(spark,
|
|
|
139
139
|
):
|
|
140
140
|
table_properties = _build_table_properties(extra_table_properties)
|
|
141
141
|
|
|
142
|
+
# Resolve the schemaHints outside the @dlt.table body so that the
|
|
143
|
+
# collect() inside _pick_latest_source_file runs once at pipeline
|
|
144
|
+
# setup — Lakeflow Declarative Pipelines forbids eager Spark ops
|
|
145
|
+
# like collect() inside a decorated function.
|
|
146
|
+
#
|
|
147
|
+
# We take the schema from the newest source file so hints track the
|
|
148
|
+
# current source-of-truth type after upstream drift (e.g. DMS
|
|
149
|
+
# widening decimal(19,2) -> decimal(19,3)). Directory reads — with
|
|
150
|
+
# or without mergeSchema — fail for scale-changed decimals because
|
|
151
|
+
# Spark cannot merge them (integer-digit counts differ). Older
|
|
152
|
+
# files with narrower types cast up losslessly under this hint, and
|
|
153
|
+
# delta.enableTypeWidening handles the existing bronze table.
|
|
154
|
+
latest_file = _pick_latest_source_file(spark, files_path)
|
|
155
|
+
schema_source_path = latest_file if latest_file else files_path
|
|
156
|
+
inferred_df = spark.read.format(file_format).load(schema_source_path).transform(rename_columns)
|
|
157
|
+
schema_string = ', '.join([
|
|
158
|
+
f"{field.name} {field.dataType.simpleString()}"
|
|
159
|
+
for field in inferred_df.schema.fields
|
|
160
|
+
if field.name not in schema_exclude_columns
|
|
161
|
+
])
|
|
162
|
+
|
|
142
163
|
@dlt.table(
|
|
143
164
|
name=table_name,
|
|
144
165
|
comment=f"This is the bronze table for table {source_table_name}.",
|
|
@@ -146,18 +167,6 @@ def create_bronze_table_definition(spark,
|
|
|
146
167
|
table_properties=table_properties,
|
|
147
168
|
)
|
|
148
169
|
def transform_cdc_to_bronze():
|
|
149
|
-
# Read the schema from the newest source file so schemaHints tracks
|
|
150
|
-
# the current source-of-truth type after upstream drift (e.g. DMS
|
|
151
|
-
# widening decimal(19,2) -> decimal(19,3)). Directory reads —
|
|
152
|
-
# with or without mergeSchema — fail for scale-changed decimals
|
|
153
|
-
# because Spark can't merge them (integer-digit counts differ).
|
|
154
|
-
# Older files with narrower types cast up losslessly under this
|
|
155
|
-
# hint, and delta.enableTypeWidening handles the bronze table.
|
|
156
|
-
latest_file = _pick_latest_source_file(spark, files_path)
|
|
157
|
-
schema_source_path = latest_file if latest_file else files_path
|
|
158
|
-
df = spark.read.format(file_format).load(schema_source_path).transform(rename_columns)
|
|
159
|
-
fields = [field for field in df.schema.fields if field.name not in schema_exclude_columns]
|
|
160
|
-
schema_string = ', '.join([f"{field.name} {field.dataType.simpleString()}" for field in fields])
|
|
161
170
|
reader = spark \
|
|
162
171
|
.readStream \
|
|
163
172
|
.format('cloudFiles') \
|
|
@@ -33,9 +33,15 @@ def _build_cdc_table():
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _stub_bronze_setup(monkeypatch):
|
|
37
|
+
"""Bypass the setup-time parquet read used to infer schemaHints."""
|
|
38
|
+
monkeypatch.setattr(main_cdc_utils, "_pick_latest_source_file", lambda *_: None)
|
|
39
|
+
|
|
40
|
+
|
|
36
41
|
def test_base_cdc_replication_process_uses_pipeline_schema_configuration(monkeypatch):
|
|
37
42
|
dlt_spy = _DltSpy()
|
|
38
|
-
spark =
|
|
43
|
+
spark = _make_spark_with_reader([], {})
|
|
44
|
+
_stub_bronze_setup(monkeypatch)
|
|
39
45
|
|
|
40
46
|
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
41
47
|
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
@@ -58,7 +64,8 @@ def test_base_cdc_replication_process_uses_pipeline_schema_configuration(monkeyp
|
|
|
58
64
|
|
|
59
65
|
def test_base_cdc_replication_process_uses_legacy_names_without_both_schemas(monkeypatch):
|
|
60
66
|
dlt_spy = _DltSpy()
|
|
61
|
-
spark =
|
|
67
|
+
spark = _make_spark_with_reader([], {})
|
|
68
|
+
_stub_bronze_setup(monkeypatch)
|
|
62
69
|
|
|
63
70
|
monkeypatch.setattr(main_cdc_utils, "col", lambda value: value)
|
|
64
71
|
monkeypatch.setattr(main_cdc_utils, "expr", lambda value: value)
|
|
@@ -140,52 +147,53 @@ def test_silver_streaming_process_merges_extra_table_properties(monkeypatch):
|
|
|
140
147
|
assert table_properties["my.custom.prop"] == "42"
|
|
141
148
|
|
|
142
149
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
self._t = t
|
|
150
|
+
class _FakeField:
|
|
151
|
+
def __init__(self, name, type_str):
|
|
152
|
+
self.name = name
|
|
147
153
|
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
class _DT:
|
|
155
|
+
def simpleString(self_inner):
|
|
156
|
+
return type_str
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
def __init__(self, name, type_str):
|
|
153
|
-
self.name = name
|
|
154
|
-
self.dataType = _DT(type_str)
|
|
158
|
+
self.dataType = _DT()
|
|
155
159
|
|
|
156
|
-
class _DF:
|
|
157
|
-
def __init__(self, fields):
|
|
158
|
-
class _Schema:
|
|
159
|
-
pass
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
class _FakeDF:
|
|
162
|
+
def __init__(self, fields):
|
|
163
|
+
class _Schema:
|
|
164
|
+
pass
|
|
163
165
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
self.schema = _Schema()
|
|
167
|
+
self.schema.fields = fields
|
|
166
168
|
|
|
167
|
-
|
|
169
|
+
def transform(self, _fn):
|
|
170
|
+
return self
|
|
168
171
|
|
|
172
|
+
|
|
173
|
+
def _make_spark_with_reader(load_paths, fields_by_path):
|
|
169
174
|
class _Reader:
|
|
170
|
-
def format(self,
|
|
171
|
-
calls["format"] = fmt
|
|
175
|
+
def format(self, _fmt):
|
|
172
176
|
return self
|
|
173
177
|
|
|
174
178
|
def load(self, path):
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
# source-of-truth after upstream drift.
|
|
178
|
-
return _DF([_Field("pnl", "decimal(19,3)"), _Field("id", "bigint")])
|
|
179
|
+
load_paths.append(path)
|
|
180
|
+
return _FakeDF(fields_by_path.get(path, []))
|
|
179
181
|
|
|
180
182
|
spark = type("_Spark", (), {})()
|
|
181
183
|
spark.read = _Reader()
|
|
184
|
+
return spark
|
|
185
|
+
|
|
182
186
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
def test_create_bronze_table_definition_reads_schema_from_latest_file_at_setup_time(monkeypatch):
|
|
188
|
+
load_paths = []
|
|
189
|
+
latest_path = "s3://bucket/dms_bronze/artemis/settled_bet/20260701-090901614.parquet"
|
|
190
|
+
spark = _make_spark_with_reader(
|
|
191
|
+
load_paths,
|
|
192
|
+
{latest_path: [_FakeField("pnl", "decimal(19,3)"), _FakeField("id", "bigint")]},
|
|
187
193
|
)
|
|
188
194
|
|
|
195
|
+
monkeypatch.setattr(main_cdc_utils, "_pick_latest_source_file", lambda *_: latest_path)
|
|
196
|
+
|
|
189
197
|
captured = {}
|
|
190
198
|
|
|
191
199
|
class _Dlt:
|
|
@@ -212,61 +220,26 @@ def test_create_bronze_table_definition_reads_schema_from_latest_file_and_enable
|
|
|
212
220
|
use_managed_file_events=False,
|
|
213
221
|
)
|
|
214
222
|
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
|
|
219
|
-
captured["fn"]()
|
|
220
|
-
except AttributeError:
|
|
221
|
-
pass
|
|
222
|
-
|
|
223
|
-
assert calls["format"] == "parquet"
|
|
224
|
-
assert calls["load_paths"] == [
|
|
225
|
-
"s3://bucket/dms_bronze/artemis/settled_bet/20260701-090901614.parquet"
|
|
226
|
-
]
|
|
223
|
+
# The schema-inference read must happen at setup time, BEFORE the
|
|
224
|
+
# @dlt.table body ever runs. That means load_paths already contains
|
|
225
|
+
# the latest file just from calling create_bronze_table_definition.
|
|
226
|
+
assert load_paths == [latest_path]
|
|
227
227
|
assert captured["kwargs"]["table_properties"]["delta.enableTypeWidening"] == "true"
|
|
228
228
|
|
|
229
229
|
|
|
230
|
-
def
|
|
231
|
-
class _DT:
|
|
232
|
-
def simpleString(self):
|
|
233
|
-
return "bigint"
|
|
234
|
-
|
|
235
|
-
class _Field:
|
|
236
|
-
def __init__(self, name):
|
|
237
|
-
self.name = name
|
|
238
|
-
self.dataType = _DT()
|
|
239
|
-
|
|
240
|
-
class _DF:
|
|
241
|
-
def __init__(self):
|
|
242
|
-
class _Schema:
|
|
243
|
-
pass
|
|
244
|
-
|
|
245
|
-
self.schema = _Schema()
|
|
246
|
-
self.schema.fields = [_Field("id")]
|
|
247
|
-
|
|
248
|
-
def transform(self, _fn):
|
|
249
|
-
return self
|
|
250
|
-
|
|
230
|
+
def test_create_bronze_table_definition_falls_back_to_directory_when_listing_empty(monkeypatch):
|
|
251
231
|
load_paths = []
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def load(self, path):
|
|
258
|
-
load_paths.append(path)
|
|
259
|
-
return _DF()
|
|
260
|
-
|
|
261
|
-
spark = type("_Spark", (), {})()
|
|
262
|
-
spark.read = _Reader()
|
|
232
|
+
directory = "s3://bucket/dms_bronze/artemis/settled_bet/"
|
|
233
|
+
spark = _make_spark_with_reader(
|
|
234
|
+
load_paths,
|
|
235
|
+
{directory: [_FakeField("id", "bigint")]},
|
|
236
|
+
)
|
|
263
237
|
|
|
264
238
|
monkeypatch.setattr(main_cdc_utils, "_pick_latest_source_file", lambda *_: None)
|
|
265
239
|
|
|
266
240
|
class _Dlt:
|
|
267
241
|
def table(self, **_kwargs):
|
|
268
242
|
def _decorator(fn):
|
|
269
|
-
_Dlt.fn = staticmethod(fn)
|
|
270
243
|
return fn
|
|
271
244
|
|
|
272
245
|
return _decorator
|
|
@@ -276,7 +249,7 @@ def test_create_bronze_table_definition_falls_back_to_directory_read_when_listin
|
|
|
276
249
|
dlt=_Dlt(),
|
|
277
250
|
source_table_name="settled_bet",
|
|
278
251
|
table_name="apollo.artemis.bronze_settled_bet",
|
|
279
|
-
files_path=
|
|
252
|
+
files_path=directory,
|
|
280
253
|
file_format="parquet",
|
|
281
254
|
partitions=[],
|
|
282
255
|
schema_exclude_columns=[],
|
|
@@ -285,9 +258,4 @@ def test_create_bronze_table_definition_falls_back_to_directory_read_when_listin
|
|
|
285
258
|
use_managed_file_events=False,
|
|
286
259
|
)
|
|
287
260
|
|
|
288
|
-
|
|
289
|
-
_Dlt.fn()
|
|
290
|
-
except AttributeError:
|
|
291
|
-
pass
|
|
292
|
-
|
|
293
|
-
assert load_paths == ["s3://bucket/dms_bronze/artemis/settled_bet/"]
|
|
261
|
+
assert load_paths == [directory]
|
|
File without changes
|
{dlt_utils_lib-1.2.5 → dlt_utils_lib-1.2.6}/dlt_utils/dlt_autoloader_recovery_configuration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|