dlt-utils-lib 1.2.4__tar.gz → 1.2.5__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.4 → dlt_utils_lib-1.2.5}/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils/main_cdc_utils.py +33 -6
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/tests/test_main_cdc_utils.py +81 -10
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils/__init__.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_autoloader_recovery_configuration.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_metadata_receiver.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils/dlt_transformations.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils/main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/SOURCES.txt +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/dependency_links.txt +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/requires.txt +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/dlt_utils_lib.egg-info/top_level.txt +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/setup.cfg +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/setup.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/tests/__init__.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/tests/conftest.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/tests/test_main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/tests/test_transformation_module.py +0 -0
|
@@ -33,6 +33,29 @@ def _build_table_properties(extra: Optional[dict] = None,
|
|
|
33
33
|
return props
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _pick_latest_source_file(spark, files_path: str) -> Optional[str]:
|
|
37
|
+
"""Return the path of the most recently modified source file, or None.
|
|
38
|
+
|
|
39
|
+
Spark's parquet reader cannot mergeSchema across decimal scales
|
|
40
|
+
(e.g. decimal(19,2) vs decimal(19,3) — their integer-digit counts
|
|
41
|
+
differ). Grabbing the newest file's schema instead gives us the
|
|
42
|
+
current source-of-truth type; older files with narrower types are
|
|
43
|
+
read losslessly under that hint by Auto Loader, and Delta type
|
|
44
|
+
widening handles the existing bronze table.
|
|
45
|
+
"""
|
|
46
|
+
listing = (
|
|
47
|
+
spark.read.format('binaryFile')
|
|
48
|
+
.load(files_path)
|
|
49
|
+
.select('path', 'modificationTime')
|
|
50
|
+
.orderBy('modificationTime', ascending=False)
|
|
51
|
+
.limit(1)
|
|
52
|
+
.collect()
|
|
53
|
+
)
|
|
54
|
+
if not listing:
|
|
55
|
+
return None
|
|
56
|
+
return listing[0]['path']
|
|
57
|
+
|
|
58
|
+
|
|
36
59
|
# Base code pipeline: streaming process
|
|
37
60
|
def base_cdc_replication_process(dlt,
|
|
38
61
|
spark,
|
|
@@ -123,12 +146,16 @@ def create_bronze_table_definition(spark,
|
|
|
123
146
|
table_properties=table_properties,
|
|
124
147
|
)
|
|
125
148
|
def transform_cdc_to_bronze():
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
|
|
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)
|
|
132
159
|
fields = [field for field in df.schema.fields if field.name not in schema_exclude_columns]
|
|
133
160
|
schema_string = ', '.join([f"{field.name} {field.dataType.simpleString()}" for field in fields])
|
|
134
161
|
reader = spark \
|
|
@@ -140,7 +140,7 @@ def test_silver_streaming_process_merges_extra_table_properties(monkeypatch):
|
|
|
140
140
|
assert table_properties["my.custom.prop"] == "42"
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
def
|
|
143
|
+
def test_create_bronze_table_definition_reads_schema_from_latest_file_and_enables_type_widening(monkeypatch):
|
|
144
144
|
class _DT:
|
|
145
145
|
def __init__(self, t):
|
|
146
146
|
self._t = t
|
|
@@ -164,25 +164,28 @@ def test_create_bronze_table_definition_reads_schema_with_merge_and_enables_type
|
|
|
164
164
|
def transform(self, _fn):
|
|
165
165
|
return self
|
|
166
166
|
|
|
167
|
-
calls = {"
|
|
167
|
+
calls = {"load_paths": [], "format": None}
|
|
168
168
|
|
|
169
169
|
class _Reader:
|
|
170
170
|
def format(self, fmt):
|
|
171
171
|
calls["format"] = fmt
|
|
172
172
|
return self
|
|
173
173
|
|
|
174
|
-
def
|
|
175
|
-
calls["
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def load(self, _path):
|
|
179
|
-
# After a mergeSchema=true read the parquet reader would union
|
|
180
|
-
# decimal(19,2) and decimal(19,3) footers into decimal(19,3).
|
|
174
|
+
def load(self, path):
|
|
175
|
+
calls["load_paths"].append(path)
|
|
176
|
+
# The picked "latest" file reports decimal(19,3), the current
|
|
177
|
+
# source-of-truth after upstream drift.
|
|
181
178
|
return _DF([_Field("pnl", "decimal(19,3)"), _Field("id", "bigint")])
|
|
182
179
|
|
|
183
180
|
spark = type("_Spark", (), {})()
|
|
184
181
|
spark.read = _Reader()
|
|
185
182
|
|
|
183
|
+
monkeypatch.setattr(
|
|
184
|
+
main_cdc_utils,
|
|
185
|
+
"_pick_latest_source_file",
|
|
186
|
+
lambda _spark, _files_path: "s3://bucket/dms_bronze/artemis/settled_bet/20260701-090901614.parquet",
|
|
187
|
+
)
|
|
188
|
+
|
|
186
189
|
captured = {}
|
|
187
190
|
|
|
188
191
|
class _Dlt:
|
|
@@ -218,5 +221,73 @@ def test_create_bronze_table_definition_reads_schema_with_merge_and_enables_type
|
|
|
218
221
|
pass
|
|
219
222
|
|
|
220
223
|
assert calls["format"] == "parquet"
|
|
221
|
-
assert calls["
|
|
224
|
+
assert calls["load_paths"] == [
|
|
225
|
+
"s3://bucket/dms_bronze/artemis/settled_bet/20260701-090901614.parquet"
|
|
226
|
+
]
|
|
222
227
|
assert captured["kwargs"]["table_properties"]["delta.enableTypeWidening"] == "true"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_create_bronze_table_definition_falls_back_to_directory_read_when_listing_empty(monkeypatch):
|
|
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
|
+
|
|
251
|
+
load_paths = []
|
|
252
|
+
|
|
253
|
+
class _Reader:
|
|
254
|
+
def format(self, _fmt):
|
|
255
|
+
return self
|
|
256
|
+
|
|
257
|
+
def load(self, path):
|
|
258
|
+
load_paths.append(path)
|
|
259
|
+
return _DF()
|
|
260
|
+
|
|
261
|
+
spark = type("_Spark", (), {})()
|
|
262
|
+
spark.read = _Reader()
|
|
263
|
+
|
|
264
|
+
monkeypatch.setattr(main_cdc_utils, "_pick_latest_source_file", lambda *_: None)
|
|
265
|
+
|
|
266
|
+
class _Dlt:
|
|
267
|
+
def table(self, **_kwargs):
|
|
268
|
+
def _decorator(fn):
|
|
269
|
+
_Dlt.fn = staticmethod(fn)
|
|
270
|
+
return fn
|
|
271
|
+
|
|
272
|
+
return _decorator
|
|
273
|
+
|
|
274
|
+
main_cdc_utils.create_bronze_table_definition(
|
|
275
|
+
spark=spark,
|
|
276
|
+
dlt=_Dlt(),
|
|
277
|
+
source_table_name="settled_bet",
|
|
278
|
+
table_name="apollo.artemis.bronze_settled_bet",
|
|
279
|
+
files_path="s3://bucket/dms_bronze/artemis/settled_bet/",
|
|
280
|
+
file_format="parquet",
|
|
281
|
+
partitions=[],
|
|
282
|
+
schema_exclude_columns=[],
|
|
283
|
+
time_diff_history_cdc_timestamp=30,
|
|
284
|
+
default_value_for_removed_col={},
|
|
285
|
+
use_managed_file_events=False,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
_Dlt.fn()
|
|
290
|
+
except AttributeError:
|
|
291
|
+
pass
|
|
292
|
+
|
|
293
|
+
assert load_paths == ["s3://bucket/dms_bronze/artemis/settled_bet/"]
|
|
File without changes
|
{dlt_utils_lib-1.2.4 → dlt_utils_lib-1.2.5}/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
|