dbt-common 1.2.0__py3-none-any.whl → 1.4.0__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.
- dbt_common/__about__.py +1 -1
- dbt_common/contracts/metadata.py +34 -1
- dbt_common/record.py +94 -11
- {dbt_common-1.2.0.dist-info → dbt_common-1.4.0.dist-info}/METADATA +2 -1
- {dbt_common-1.2.0.dist-info → dbt_common-1.4.0.dist-info}/RECORD +7 -7
- {dbt_common-1.2.0.dist-info → dbt_common-1.4.0.dist-info}/WHEEL +0 -0
- {dbt_common-1.2.0.dist-info → dbt_common-1.4.0.dist-info}/licenses/LICENSE +0 -0
dbt_common/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = "1.
|
1
|
+
version = "1.4.0"
|
dbt_common/contracts/metadata.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
-
from typing import Dict, Optional, Union
|
2
|
+
from typing import Dict, Optional, Union, NamedTuple
|
3
3
|
|
4
4
|
from dbt_common.dataclass_schema import dbtClassMixin
|
5
|
+
from dbt_common.utils.formatting import lowercase
|
5
6
|
|
6
7
|
|
7
8
|
@dataclass
|
@@ -24,3 +25,35 @@ class TableMetadata(dbtClassMixin):
|
|
24
25
|
database: Optional[str] = None
|
25
26
|
comment: Optional[str] = None
|
26
27
|
owner: Optional[str] = None
|
28
|
+
|
29
|
+
|
30
|
+
CatalogKey = NamedTuple(
|
31
|
+
"CatalogKey", [("database", Optional[str]), ("schema", str), ("name", str)]
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
@dataclass
|
36
|
+
class ColumnMetadata(dbtClassMixin):
|
37
|
+
type: str
|
38
|
+
index: int
|
39
|
+
name: str
|
40
|
+
comment: Optional[str] = None
|
41
|
+
|
42
|
+
|
43
|
+
ColumnMap = Dict[str, ColumnMetadata]
|
44
|
+
|
45
|
+
|
46
|
+
@dataclass
|
47
|
+
class CatalogTable(dbtClassMixin):
|
48
|
+
metadata: TableMetadata
|
49
|
+
columns: ColumnMap
|
50
|
+
stats: StatsDict
|
51
|
+
# the same table with two unique IDs will just be listed two times
|
52
|
+
unique_id: Optional[str] = None
|
53
|
+
|
54
|
+
def key(self) -> CatalogKey:
|
55
|
+
return CatalogKey(
|
56
|
+
lowercase(self.metadata.database),
|
57
|
+
self.metadata.schema.lower(),
|
58
|
+
self.metadata.name.lower(),
|
59
|
+
)
|
dbt_common/record.py
CHANGED
@@ -9,6 +9,8 @@ import functools
|
|
9
9
|
import dataclasses
|
10
10
|
import json
|
11
11
|
import os
|
12
|
+
|
13
|
+
from deepdiff import DeepDiff # type: ignore
|
12
14
|
from enum import Enum
|
13
15
|
from typing import Any, Dict, List, Mapping, Optional, Type
|
14
16
|
|
@@ -51,15 +53,68 @@ class Record:
|
|
51
53
|
|
52
54
|
|
53
55
|
class Diff:
|
54
|
-
|
56
|
+
def __init__(self, current_recording_path: str, previous_recording_path: str) -> None:
|
57
|
+
self.current_recording_path = current_recording_path
|
58
|
+
self.previous_recording_path = previous_recording_path
|
59
|
+
|
60
|
+
def diff_query_records(self, current: List, previous: List) -> Dict[str, Any]:
|
61
|
+
# some of the table results are returned as a stringified list of dicts that don't
|
62
|
+
# diff because order isn't consistent. convert it into a list of dicts so it can
|
63
|
+
# be diffed ignoring order
|
64
|
+
|
65
|
+
for i in range(len(current)):
|
66
|
+
if current[i].get("result").get("table") is not None:
|
67
|
+
current[i]["result"]["table"] = json.loads(current[i]["result"]["table"])
|
68
|
+
for i in range(len(previous)):
|
69
|
+
if previous[i].get("result").get("table") is not None:
|
70
|
+
previous[i]["result"]["table"] = json.loads(previous[i]["result"]["table"])
|
71
|
+
|
72
|
+
return DeepDiff(previous, current, ignore_order=True, verbose_level=2)
|
73
|
+
|
74
|
+
def diff_env_records(self, current: List, previous: List) -> Dict[str, Any]:
|
75
|
+
# The mode and filepath may change. Ignore them.
|
76
|
+
|
77
|
+
exclude_paths = [
|
78
|
+
"root[0]['result']['env']['DBT_RECORDER_FILE_PATH']",
|
79
|
+
"root[0]['result']['env']['DBT_RECORDER_MODE']",
|
80
|
+
]
|
81
|
+
|
82
|
+
return DeepDiff(
|
83
|
+
previous, current, ignore_order=True, verbose_level=2, exclude_paths=exclude_paths
|
84
|
+
)
|
85
|
+
|
86
|
+
def diff_default(self, current: List, previous: List) -> Dict[str, Any]:
|
87
|
+
return DeepDiff(previous, current, ignore_order=True, verbose_level=2)
|
88
|
+
|
89
|
+
def calculate_diff(self) -> Dict[str, Any]:
|
90
|
+
with open(self.current_recording_path) as current_recording:
|
91
|
+
current_dct = json.load(current_recording)
|
92
|
+
|
93
|
+
with open(self.previous_recording_path) as previous_recording:
|
94
|
+
previous_dct = json.load(previous_recording)
|
95
|
+
|
96
|
+
diff = {}
|
97
|
+
for record_type in current_dct:
|
98
|
+
if record_type == "QueryRecord":
|
99
|
+
diff[record_type] = self.diff_query_records(
|
100
|
+
current_dct[record_type], previous_dct[record_type]
|
101
|
+
)
|
102
|
+
elif record_type == "GetEnvRecord":
|
103
|
+
diff[record_type] = self.diff_env_records(
|
104
|
+
current_dct[record_type], previous_dct[record_type]
|
105
|
+
)
|
106
|
+
else:
|
107
|
+
diff[record_type] = self.diff_default(
|
108
|
+
current_dct[record_type], previous_dct[record_type]
|
109
|
+
)
|
55
110
|
|
56
|
-
|
111
|
+
return diff
|
57
112
|
|
58
113
|
|
59
114
|
class RecorderMode(Enum):
|
60
115
|
RECORD = 1
|
61
116
|
REPLAY = 2
|
62
|
-
|
117
|
+
DIFF = 3 # records and does diffing
|
63
118
|
|
64
119
|
|
65
120
|
class Recorder:
|
@@ -67,15 +122,31 @@ class Recorder:
|
|
67
122
|
_record_name_by_params_name: Dict[str, str] = {}
|
68
123
|
|
69
124
|
def __init__(
|
70
|
-
self,
|
125
|
+
self,
|
126
|
+
mode: RecorderMode,
|
127
|
+
types: Optional[List],
|
128
|
+
current_recording_path: str = "recording.json",
|
129
|
+
previous_recording_path: Optional[str] = None,
|
71
130
|
) -> None:
|
72
131
|
self.mode = mode
|
73
132
|
self.types = types
|
74
133
|
self._records_by_type: Dict[str, List[Record]] = {}
|
75
134
|
self._replay_diffs: List["Diff"] = []
|
135
|
+
self.diff: Diff
|
136
|
+
self.previous_recording_path = previous_recording_path
|
137
|
+
self.current_recording_path = current_recording_path
|
138
|
+
|
139
|
+
if self.previous_recording_path is not None and self.mode in (
|
140
|
+
RecorderMode.REPLAY,
|
141
|
+
RecorderMode.DIFF,
|
142
|
+
):
|
143
|
+
self.diff = Diff(
|
144
|
+
current_recording_path=self.current_recording_path,
|
145
|
+
previous_recording_path=self.previous_recording_path,
|
146
|
+
)
|
76
147
|
|
77
|
-
|
78
|
-
|
148
|
+
if self.mode == RecorderMode.REPLAY:
|
149
|
+
self._records_by_type = self.load(self.previous_recording_path)
|
79
150
|
|
80
151
|
@classmethod
|
81
152
|
def register_record_type(cls, rec_type) -> Any:
|
@@ -101,8 +172,8 @@ class Recorder:
|
|
101
172
|
|
102
173
|
return match
|
103
174
|
|
104
|
-
def write(self
|
105
|
-
with open(
|
175
|
+
def write(self) -> None:
|
176
|
+
with open(self.current_recording_path, "w") as file:
|
106
177
|
json.dump(self._to_dict(), file)
|
107
178
|
|
108
179
|
def _to_dict(self) -> Dict:
|
@@ -143,19 +214,19 @@ class Recorder:
|
|
143
214
|
|
144
215
|
def write_diffs(self, diff_file_name) -> None:
|
145
216
|
json.dump(
|
146
|
-
self.
|
217
|
+
self.diff.calculate_diff(),
|
147
218
|
open(diff_file_name, "w"),
|
148
219
|
)
|
149
220
|
|
150
221
|
def print_diffs(self) -> None:
|
151
|
-
print(repr(self.
|
222
|
+
print(repr(self.diff.calculate_diff()))
|
152
223
|
|
153
224
|
|
154
225
|
def get_record_mode_from_env() -> Optional[RecorderMode]:
|
155
226
|
"""
|
156
227
|
Get the record mode from the environment variables.
|
157
228
|
|
158
|
-
If the mode is not set to 'RECORD' or 'REPLAY', return None.
|
229
|
+
If the mode is not set to 'RECORD', 'DIFF' or 'REPLAY', return None.
|
159
230
|
Expected format: 'DBT_RECORDER_MODE=RECORD'
|
160
231
|
"""
|
161
232
|
record_mode = os.environ.get("DBT_RECORDER_MODE")
|
@@ -165,6 +236,9 @@ def get_record_mode_from_env() -> Optional[RecorderMode]:
|
|
165
236
|
|
166
237
|
if record_mode.lower() == "record":
|
167
238
|
return RecorderMode.RECORD
|
239
|
+
# diffing requires a file path, otherwise treat as noop
|
240
|
+
elif record_mode.lower() == "diff" and os.environ.get("DBT_RECORDER_FILE_PATH") is not None:
|
241
|
+
return RecorderMode.DIFF
|
168
242
|
# replaying requires a file path, otherwise treat as noop
|
169
243
|
elif record_mode.lower() == "replay" and os.environ.get("DBT_RECORDER_FILE_PATH") is not None:
|
170
244
|
return RecorderMode.REPLAY
|
@@ -190,6 +264,15 @@ def get_record_types_from_env() -> Optional[List]:
|
|
190
264
|
return record_types_str.split(",")
|
191
265
|
|
192
266
|
|
267
|
+
def get_record_types_from_dict(fp: str) -> List:
|
268
|
+
"""
|
269
|
+
Get the record subset from the dict.
|
270
|
+
"""
|
271
|
+
with open(fp) as file:
|
272
|
+
loaded_dct = json.load(file)
|
273
|
+
return list(loaded_dct.keys())
|
274
|
+
|
275
|
+
|
193
276
|
def record_function(record_type, method=False, tuple_result=False):
|
194
277
|
def record_function_inner(func_to_record):
|
195
278
|
# To avoid runtime overhead and other unpleasantness, we only apply the
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: dbt-common
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.4.0
|
4
4
|
Summary: The shared common utilities that dbt-core and adapter implementations use
|
5
5
|
Project-URL: Homepage, https://github.com/dbt-labs/dbt-common
|
6
6
|
Project-URL: Repository, https://github.com/dbt-labs/dbt-common.git
|
@@ -26,6 +26,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
26
26
|
Requires-Python: >=3.8
|
27
27
|
Requires-Dist: agate<1.10,>=1.7.0
|
28
28
|
Requires-Dist: colorama<0.5,>=0.3.9
|
29
|
+
Requires-Dist: deepdiff<8.0,>=7.0
|
29
30
|
Requires-Dist: isodate<0.7,>=0.6
|
30
31
|
Requires-Dist: jinja2<4,>=3.1.3
|
31
32
|
Requires-Dist: jsonschema<5.0,>=4.0
|
@@ -1,4 +1,4 @@
|
|
1
|
-
dbt_common/__about__.py,sha256
|
1
|
+
dbt_common/__about__.py,sha256=0kccNYBMuNA3PIhlESWmh8xP1TWpNtIEzS0d-x80SC0,18
|
2
2
|
dbt_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
dbt_common/constants.py,sha256=-Y5DIL1SDPQWtlCNizXRYxFgbx1D7LaLs1ysamvGMRk,278
|
4
4
|
dbt_common/context.py,sha256=BhgT7IgyvpZHEtIdFVVuBBBX5LuU7obXT7NvIPeuD2g,1760
|
@@ -6,7 +6,7 @@ dbt_common/dataclass_schema.py,sha256=t3HGD0oXTSjitctuCVHv3iyq5BT3jxoSxv_VGkrJlE
|
|
6
6
|
dbt_common/helper_types.py,sha256=NoxqGFAq9bOjh7rqtz_eepXAxk20n3mmW_gUVpnMyYU,3901
|
7
7
|
dbt_common/invocation.py,sha256=Zw8jRPn75oi2VrUD6qGvaCDtSyIfqm5pJlPpRjs3s1E,202
|
8
8
|
dbt_common/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
dbt_common/record.py,sha256=
|
9
|
+
dbt_common/record.py,sha256=_P-OZWKQwGmq6nTC4r2oQDyh89gi-VGC2gdok6TywhI,11765
|
10
10
|
dbt_common/semver.py,sha256=2zoZYCQ7PfswqslT2NHuMGgPGMuMuX-yRThVoqfDWQU,13954
|
11
11
|
dbt_common/tests.py,sha256=6lC_JuRtoYO6cbAF8-R5aTM4HtQiM_EH8X5m_97duGY,315
|
12
12
|
dbt_common/ui.py,sha256=rc2TEM29raBFc_LXcg901pMDD07C2ohwp9qzkE-7pBY,2567
|
@@ -17,7 +17,7 @@ dbt_common/clients/jinja.py,sha256=i6VQ94FU4F6ZCQLHTxNSeGHmvyYSIe34nDhNkH6wO08,1
|
|
17
17
|
dbt_common/clients/system.py,sha256=OOhRDWR5t0Ns3OhkqjPTNTtyl_RMRWPDHWCzDoFtgkA,23014
|
18
18
|
dbt_common/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
dbt_common/contracts/constraints.py,sha256=hyqTW2oPB1dfXWW388LWnL-EFdqTpQciKISH3CeLkro,1267
|
20
|
-
dbt_common/contracts/metadata.py,sha256=
|
20
|
+
dbt_common/contracts/metadata.py,sha256=K_M06Rue0wmrQhFP_mq3uvQszq10CIt93oGiAVgbRfE,1293
|
21
21
|
dbt_common/contracts/util.py,sha256=RZpeEExSKdyFwTq7MM3rd1ZkAf11C7I-bgppUJ6SXOg,741
|
22
22
|
dbt_common/contracts/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
23
|
dbt_common/contracts/config/base.py,sha256=jWLf6SBUy7wngYs0Z5Zmx1O1v86XRueYaABlZ0W2Bxc,8356
|
@@ -56,7 +56,7 @@ dbt_common/utils/encoding.py,sha256=6_kSY2FvGNYMg7oX7PrbvVioieydih3Kl7Ii802LaHI,
|
|
56
56
|
dbt_common/utils/executor.py,sha256=Zyzd1wML3aN-iYn9ZG2Gc_jj5vknmvQNyH-c0RaPIpo,2446
|
57
57
|
dbt_common/utils/formatting.py,sha256=JUn5rzJ-uajs9wPCN0-f2iRFY1pOJF5YjTD9dERuLoc,165
|
58
58
|
dbt_common/utils/jinja.py,sha256=XNfZHuZhLM_R_yPmzYojPm6bF7QOoxIjSWrkJRw6wks,965
|
59
|
-
dbt_common-1.
|
60
|
-
dbt_common-1.
|
61
|
-
dbt_common-1.
|
62
|
-
dbt_common-1.
|
59
|
+
dbt_common-1.4.0.dist-info/METADATA,sha256=QH1OkqWWMGMTDUDSrqHmEnEvHs0akdlpDU-5asSCUD8,5298
|
60
|
+
dbt_common-1.4.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
61
|
+
dbt_common-1.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
62
|
+
dbt_common-1.4.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|