dbt-common 1.8.0__py3-none-any.whl → 1.9.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/behavior_flags.py +33 -19
- dbt_common/clients/jinja.py +75 -49
- dbt_common/contracts/config/base.py +83 -7
- dbt_common/events/types.proto +5 -6
- dbt_common/events/types.py +6 -25
- dbt_common/events/types_pb2.py +45 -45
- dbt_common/exceptions/jinja.py +9 -4
- dbt_common/record.py +8 -2
- dbt_common/semver.py +39 -27
- {dbt_common-1.8.0.dist-info → dbt_common-1.9.0.dist-info}/METADATA +1 -1
- {dbt_common-1.8.0.dist-info → dbt_common-1.9.0.dist-info}/RECORD +14 -14
- {dbt_common-1.8.0.dist-info → dbt_common-1.9.0.dist-info}/WHEEL +0 -0
- {dbt_common-1.8.0.dist-info → dbt_common-1.9.0.dist-info}/licenses/LICENSE +0 -0
dbt_common/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = "1.
|
1
|
+
version = "1.9.0"
|
dbt_common/behavior_flags.py
CHANGED
@@ -9,8 +9,8 @@ except ImportError:
|
|
9
9
|
from typing import Optional as NotRequired
|
10
10
|
|
11
11
|
from dbt_common.events.functions import fire_event
|
12
|
-
from dbt_common.events.types import
|
13
|
-
from dbt_common.exceptions import CompilationError
|
12
|
+
from dbt_common.events.types import BehaviorChangeEvent
|
13
|
+
from dbt_common.exceptions import CompilationError, DbtInternalError
|
14
14
|
|
15
15
|
|
16
16
|
class BehaviorFlag(TypedDict):
|
@@ -20,16 +20,20 @@ class BehaviorFlag(TypedDict):
|
|
20
20
|
Args:
|
21
21
|
name: the name of the behavior flag
|
22
22
|
default: default setting, starts as False, becomes True after a bake-in period
|
23
|
-
|
24
|
-
deprecation_message: an additional message to send when the flag evaluates to False
|
23
|
+
description: an additional message to send when the flag evaluates to False
|
25
24
|
docs_url: the url to the relevant docs on docs.getdbt.com
|
25
|
+
|
26
|
+
*Note*:
|
27
|
+
While `description` and `docs_url` are both listed as `NotRequired`, at least one of them is required.
|
28
|
+
This is validated when the flag is rendered in `BehaviorFlagRendered` below.
|
29
|
+
The goal of this restriction is to provide the end user with context so they can make an informed decision
|
30
|
+
about if, and when, to enable the behavior flag.
|
26
31
|
"""
|
27
32
|
|
28
33
|
name: str
|
29
34
|
default: bool
|
30
35
|
source: NotRequired[str]
|
31
|
-
|
32
|
-
deprecation_message: NotRequired[str]
|
36
|
+
description: NotRequired[str]
|
33
37
|
docs_url: NotRequired[str]
|
34
38
|
|
35
39
|
|
@@ -43,14 +47,33 @@ class BehaviorFlagRendered:
|
|
43
47
|
"""
|
44
48
|
|
45
49
|
def __init__(self, flag: BehaviorFlag, user_overrides: Dict[str, Any]) -> None:
|
50
|
+
self._validate(flag)
|
51
|
+
|
46
52
|
self.name = flag["name"]
|
47
53
|
self.setting = user_overrides.get(flag["name"], flag["default"])
|
48
|
-
|
54
|
+
|
55
|
+
default_description = (
|
56
|
+
f"""The behavior controlled by `{flag["name"]}` is currently turned off.\n"""
|
57
|
+
)
|
58
|
+
default_docs_url = "https://docs.getdbt.com/reference/global-configs/behavior-changes"
|
59
|
+
self._behavior_change_event = BehaviorChangeEvent(
|
60
|
+
flag_name=flag["name"],
|
61
|
+
flag_source=flag.get("source", self._default_source()),
|
62
|
+
description=flag.get("description", default_description),
|
63
|
+
docs_url=flag.get("docs_url", default_docs_url),
|
64
|
+
)
|
65
|
+
|
66
|
+
@staticmethod
|
67
|
+
def _validate(flag: BehaviorFlag) -> None:
|
68
|
+
if flag.get("description") is None and flag.get("docs_url") is None:
|
69
|
+
raise DbtInternalError(
|
70
|
+
"Behavior change flags require at least one of `description` and `docs_url`."
|
71
|
+
)
|
49
72
|
|
50
73
|
@property
|
51
74
|
def setting(self) -> bool:
|
52
75
|
if self._setting is False:
|
53
|
-
fire_event(self.
|
76
|
+
fire_event(self._behavior_change_event)
|
54
77
|
return self._setting
|
55
78
|
|
56
79
|
@setting.setter
|
@@ -61,15 +84,6 @@ class BehaviorFlagRendered:
|
|
61
84
|
def no_warn(self) -> bool:
|
62
85
|
return self._setting
|
63
86
|
|
64
|
-
def _deprecation_event(self, flag: BehaviorFlag) -> BehaviorDeprecationEvent:
|
65
|
-
return BehaviorDeprecationEvent(
|
66
|
-
flag_name=flag["name"],
|
67
|
-
flag_source=flag.get("source", self._default_source()),
|
68
|
-
deprecation_version=flag.get("deprecation_version"),
|
69
|
-
deprecation_message=flag.get("deprecation_message"),
|
70
|
-
docs_url=flag.get("docs_url"),
|
71
|
-
)
|
72
|
-
|
73
87
|
@staticmethod
|
74
88
|
def _default_source() -> str:
|
75
89
|
"""
|
@@ -95,7 +109,7 @@ class Behavior:
|
|
95
109
|
if adapter.behavior.my_flag:
|
96
110
|
...
|
97
111
|
|
98
|
-
if adapter.behavior.my_flag.no_warn: # this will not fire the
|
112
|
+
if adapter.behavior.my_flag.no_warn: # this will not fire the behavior change event
|
99
113
|
...
|
100
114
|
```
|
101
115
|
```jinja
|
@@ -103,7 +117,7 @@ class Behavior:
|
|
103
117
|
...
|
104
118
|
{% endif %}
|
105
119
|
|
106
|
-
{% if adapter.behavior.my_flag.no_warn %} {# this will not fire the
|
120
|
+
{% if adapter.behavior.my_flag.no_warn %} {# this will not fire the behavior change event #}
|
107
121
|
...
|
108
122
|
{% endif %}
|
109
123
|
```
|
dbt_common/clients/jinja.py
CHANGED
@@ -6,7 +6,21 @@ from ast import literal_eval
|
|
6
6
|
from collections import ChainMap
|
7
7
|
from contextlib import contextmanager
|
8
8
|
from itertools import chain, islice
|
9
|
-
from
|
9
|
+
from types import CodeType
|
10
|
+
from typing import (
|
11
|
+
Any,
|
12
|
+
Callable,
|
13
|
+
Dict,
|
14
|
+
Iterator,
|
15
|
+
List,
|
16
|
+
Mapping,
|
17
|
+
Optional,
|
18
|
+
Union,
|
19
|
+
Set,
|
20
|
+
Type,
|
21
|
+
NoReturn,
|
22
|
+
)
|
23
|
+
|
10
24
|
from typing_extensions import Protocol
|
11
25
|
|
12
26
|
import jinja2
|
@@ -39,10 +53,17 @@ from dbt_common.exceptions.macros import MacroReturn, UndefinedMacroError, Caugh
|
|
39
53
|
SUPPORTED_LANG_ARG = jinja2.nodes.Name("supported_languages", "param")
|
40
54
|
|
41
55
|
# Global which can be set by dependents of dbt-common (e.g. core via flag parsing)
|
42
|
-
MACRO_DEBUGGING = False
|
56
|
+
MACRO_DEBUGGING: Union[str, bool] = False
|
57
|
+
|
58
|
+
_ParseReturn = Union[jinja2.nodes.Node, List[jinja2.nodes.Node]]
|
59
|
+
|
60
|
+
|
61
|
+
# Temporary type capturing the concept the functions in this file expect for a "node"
|
62
|
+
class _NodeProtocol(Protocol):
|
63
|
+
pass
|
43
64
|
|
44
65
|
|
45
|
-
def _linecache_inject(source, write):
|
66
|
+
def _linecache_inject(source: str, write: bool) -> str:
|
46
67
|
if write:
|
47
68
|
# this is the only reliable way to accomplish this. Obviously, it's
|
48
69
|
# really darn noisy and will fill your temporary directory
|
@@ -58,18 +79,18 @@ def _linecache_inject(source, write):
|
|
58
79
|
else:
|
59
80
|
# `codecs.encode` actually takes a `bytes` as the first argument if
|
60
81
|
# the second argument is 'hex' - mypy does not know this.
|
61
|
-
rnd = codecs.encode(os.urandom(12), "hex")
|
82
|
+
rnd = codecs.encode(os.urandom(12), "hex")
|
62
83
|
filename = rnd.decode("ascii")
|
63
84
|
|
64
85
|
# put ourselves in the cache
|
65
86
|
cache_entry = (len(source), None, [line + "\n" for line in source.splitlines()], filename)
|
66
87
|
# linecache does in fact have an attribute `cache`, thanks
|
67
|
-
linecache.cache[filename] = cache_entry
|
88
|
+
linecache.cache[filename] = cache_entry
|
68
89
|
return filename
|
69
90
|
|
70
91
|
|
71
92
|
class MacroFuzzParser(jinja2.parser.Parser):
|
72
|
-
def parse_macro(self):
|
93
|
+
def parse_macro(self) -> jinja2.nodes.Macro:
|
73
94
|
node = jinja2.nodes.Macro(lineno=next(self.stream).lineno)
|
74
95
|
|
75
96
|
# modified to fuzz macros defined in the same file. this way
|
@@ -83,16 +104,13 @@ class MacroFuzzParser(jinja2.parser.Parser):
|
|
83
104
|
|
84
105
|
|
85
106
|
class MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):
|
86
|
-
def _parse(
|
107
|
+
def _parse(
|
108
|
+
self, source: str, name: Optional[str], filename: Optional[str]
|
109
|
+
) -> jinja2.nodes.Template:
|
87
110
|
return MacroFuzzParser(self, source, name, filename).parse()
|
88
111
|
|
89
|
-
def _compile(self, source, filename):
|
112
|
+
def _compile(self, source: str, filename: str) -> CodeType:
|
90
113
|
"""
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
114
|
Override jinja's compilation. Use to stash the rendered source inside
|
97
115
|
the python linecache for debugging when the appropriate environment
|
98
116
|
variable is set.
|
@@ -108,7 +126,7 @@ class MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):
|
|
108
126
|
|
109
127
|
|
110
128
|
class MacroFuzzTemplate(jinja2.nativetypes.NativeTemplate):
|
111
|
-
environment_class = MacroFuzzEnvironment
|
129
|
+
environment_class = MacroFuzzEnvironment # type: ignore
|
112
130
|
|
113
131
|
def new_context(
|
114
132
|
self,
|
@@ -171,11 +189,11 @@ class NumberMarker(NativeMarker):
|
|
171
189
|
pass
|
172
190
|
|
173
191
|
|
174
|
-
def _is_number(value) -> bool:
|
192
|
+
def _is_number(value: Any) -> bool:
|
175
193
|
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
176
194
|
|
177
195
|
|
178
|
-
def quoted_native_concat(nodes):
|
196
|
+
def quoted_native_concat(nodes: Iterator[str]) -> Any:
|
179
197
|
"""Handle special case for native_concat from the NativeTemplate.
|
180
198
|
|
181
199
|
This is almost native_concat from the NativeTemplate, except in the
|
@@ -213,7 +231,7 @@ def quoted_native_concat(nodes):
|
|
213
231
|
class NativeSandboxTemplate(jinja2.nativetypes.NativeTemplate): # mypy: ignore
|
214
232
|
environment_class = NativeSandboxEnvironment # type: ignore
|
215
233
|
|
216
|
-
def render(self, *args, **kwargs):
|
234
|
+
def render(self, *args: Any, **kwargs: Any) -> Any:
|
217
235
|
"""Render the template to produce a native Python type.
|
218
236
|
|
219
237
|
If the result is a single node, its value is returned. Otherwise,
|
@@ -229,6 +247,11 @@ class NativeSandboxTemplate(jinja2.nativetypes.NativeTemplate): # mypy: ignore
|
|
229
247
|
return self.environment.handle_exception()
|
230
248
|
|
231
249
|
|
250
|
+
class MacroProtocol(Protocol):
|
251
|
+
name: str
|
252
|
+
macro_sql: str
|
253
|
+
|
254
|
+
|
232
255
|
NativeSandboxEnvironment.template_class = NativeSandboxTemplate # type: ignore
|
233
256
|
|
234
257
|
|
@@ -236,7 +259,7 @@ class TemplateCache:
|
|
236
259
|
def __init__(self) -> None:
|
237
260
|
self.file_cache: Dict[str, jinja2.Template] = {}
|
238
261
|
|
239
|
-
def get_node_template(self, node) -> jinja2.Template:
|
262
|
+
def get_node_template(self, node: MacroProtocol) -> jinja2.Template:
|
240
263
|
key = node.macro_sql
|
241
264
|
|
242
265
|
if key in self.file_cache:
|
@@ -251,7 +274,7 @@ class TemplateCache:
|
|
251
274
|
self.file_cache[key] = template
|
252
275
|
return template
|
253
276
|
|
254
|
-
def clear(self):
|
277
|
+
def clear(self) -> None:
|
255
278
|
self.file_cache.clear()
|
256
279
|
|
257
280
|
|
@@ -262,13 +285,13 @@ class BaseMacroGenerator:
|
|
262
285
|
def __init__(self, context: Optional[Dict[str, Any]] = None) -> None:
|
263
286
|
self.context: Optional[Dict[str, Any]] = context
|
264
287
|
|
265
|
-
def get_template(self):
|
288
|
+
def get_template(self) -> jinja2.Template:
|
266
289
|
raise NotImplementedError("get_template not implemented!")
|
267
290
|
|
268
291
|
def get_name(self) -> str:
|
269
292
|
raise NotImplementedError("get_name not implemented!")
|
270
293
|
|
271
|
-
def get_macro(self):
|
294
|
+
def get_macro(self) -> Callable:
|
272
295
|
name = self.get_name()
|
273
296
|
template = self.get_template()
|
274
297
|
# make the module. previously we set both vars and local, but that's
|
@@ -286,7 +309,7 @@ class BaseMacroGenerator:
|
|
286
309
|
except (TypeError, jinja2.exceptions.TemplateRuntimeError) as e:
|
287
310
|
raise CaughtMacroError(e)
|
288
311
|
|
289
|
-
def call_macro(self, *args, **kwargs):
|
312
|
+
def call_macro(self, *args: Any, **kwargs: Any) -> Any:
|
290
313
|
# called from __call__ methods
|
291
314
|
if self.context is None:
|
292
315
|
raise DbtInternalError("Context is still None in call_macro!")
|
@@ -301,11 +324,6 @@ class BaseMacroGenerator:
|
|
301
324
|
return e.value
|
302
325
|
|
303
326
|
|
304
|
-
class MacroProtocol(Protocol):
|
305
|
-
name: str
|
306
|
-
macro_sql: str
|
307
|
-
|
308
|
-
|
309
327
|
class CallableMacroGenerator(BaseMacroGenerator):
|
310
328
|
def __init__(
|
311
329
|
self,
|
@@ -315,7 +333,7 @@ class CallableMacroGenerator(BaseMacroGenerator):
|
|
315
333
|
super().__init__(context)
|
316
334
|
self.macro = macro
|
317
335
|
|
318
|
-
def get_template(self):
|
336
|
+
def get_template(self) -> jinja2.Template:
|
319
337
|
return template_cache.get_node_template(self.macro)
|
320
338
|
|
321
339
|
def get_name(self) -> str:
|
@@ -332,14 +350,14 @@ class CallableMacroGenerator(BaseMacroGenerator):
|
|
332
350
|
raise e
|
333
351
|
|
334
352
|
# this makes MacroGenerator objects callable like functions
|
335
|
-
def __call__(self, *args, **kwargs):
|
353
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
336
354
|
return self.call_macro(*args, **kwargs)
|
337
355
|
|
338
356
|
|
339
357
|
class MaterializationExtension(jinja2.ext.Extension):
|
340
358
|
tags = ["materialization"]
|
341
359
|
|
342
|
-
def parse(self, parser):
|
360
|
+
def parse(self, parser: jinja2.parser.Parser) -> _ParseReturn:
|
343
361
|
node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)
|
344
362
|
materialization_name = parser.parse_assign_target(name_only=True).name
|
345
363
|
|
@@ -382,7 +400,7 @@ class MaterializationExtension(jinja2.ext.Extension):
|
|
382
400
|
class DocumentationExtension(jinja2.ext.Extension):
|
383
401
|
tags = ["docs"]
|
384
402
|
|
385
|
-
def parse(self, parser):
|
403
|
+
def parse(self, parser: jinja2.parser.Parser) -> _ParseReturn:
|
386
404
|
node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)
|
387
405
|
docs_name = parser.parse_assign_target(name_only=True).name
|
388
406
|
|
@@ -396,7 +414,7 @@ class DocumentationExtension(jinja2.ext.Extension):
|
|
396
414
|
class TestExtension(jinja2.ext.Extension):
|
397
415
|
tags = ["test"]
|
398
416
|
|
399
|
-
def parse(self, parser):
|
417
|
+
def parse(self, parser: jinja2.parser.Parser) -> _ParseReturn:
|
400
418
|
node = jinja2.nodes.Macro(lineno=next(parser.stream).lineno)
|
401
419
|
test_name = parser.parse_assign_target(name_only=True).name
|
402
420
|
|
@@ -406,13 +424,19 @@ class TestExtension(jinja2.ext.Extension):
|
|
406
424
|
return node
|
407
425
|
|
408
426
|
|
409
|
-
def _is_dunder_name(name):
|
427
|
+
def _is_dunder_name(name: str) -> bool:
|
410
428
|
return name.startswith("__") and name.endswith("__")
|
411
429
|
|
412
430
|
|
413
|
-
def create_undefined(node=None):
|
431
|
+
def create_undefined(node: Optional[_NodeProtocol] = None) -> Type[jinja2.Undefined]:
|
414
432
|
class Undefined(jinja2.Undefined):
|
415
|
-
def __init__(
|
433
|
+
def __init__(
|
434
|
+
self,
|
435
|
+
hint: Optional[str] = None,
|
436
|
+
obj: Any = None,
|
437
|
+
name: Optional[str] = None,
|
438
|
+
exc: Any = None,
|
439
|
+
) -> None:
|
416
440
|
super().__init__(hint=hint, name=name)
|
417
441
|
self.node = node
|
418
442
|
self.name = name
|
@@ -422,12 +446,12 @@ def create_undefined(node=None):
|
|
422
446
|
self.unsafe_callable = False
|
423
447
|
self.alters_data = False
|
424
448
|
|
425
|
-
def __getitem__(self, name):
|
449
|
+
def __getitem__(self, name: Any) -> "Undefined":
|
426
450
|
# Propagate the undefined value if a caller accesses this as if it
|
427
451
|
# were a dictionary
|
428
452
|
return self
|
429
453
|
|
430
|
-
def __getattr__(self, name):
|
454
|
+
def __getattr__(self, name: str) -> "Undefined":
|
431
455
|
if name == "name" or _is_dunder_name(name):
|
432
456
|
raise AttributeError(
|
433
457
|
"'{}' object has no attribute '{}'".format(type(self).__name__, name)
|
@@ -437,11 +461,11 @@ def create_undefined(node=None):
|
|
437
461
|
|
438
462
|
return self.__class__(hint=self.hint, name=self.name)
|
439
463
|
|
440
|
-
def __call__(self, *args, **kwargs):
|
464
|
+
def __call__(self, *args: Any, **kwargs: Any) -> "Undefined":
|
441
465
|
return self
|
442
466
|
|
443
|
-
def __reduce__(self):
|
444
|
-
raise UndefinedCompilationError(name=self.name, node=node)
|
467
|
+
def __reduce__(self) -> NoReturn:
|
468
|
+
raise UndefinedCompilationError(name=self.name or "unknown", node=node)
|
445
469
|
|
446
470
|
return Undefined
|
447
471
|
|
@@ -463,7 +487,7 @@ TEXT_FILTERS: Dict[str, Callable[[Any], Any]] = {
|
|
463
487
|
|
464
488
|
|
465
489
|
def get_environment(
|
466
|
-
node=None,
|
490
|
+
node: Optional[_NodeProtocol] = None,
|
467
491
|
capture_macros: bool = False,
|
468
492
|
native: bool = False,
|
469
493
|
) -> jinja2.Environment:
|
@@ -472,7 +496,7 @@ def get_environment(
|
|
472
496
|
}
|
473
497
|
|
474
498
|
if capture_macros:
|
475
|
-
args["undefined"] = create_undefined(node)
|
499
|
+
args["undefined"] = create_undefined(node) # type: ignore
|
476
500
|
|
477
501
|
args["extensions"].append(MaterializationExtension)
|
478
502
|
args["extensions"].append(DocumentationExtension)
|
@@ -493,7 +517,7 @@ def get_environment(
|
|
493
517
|
|
494
518
|
|
495
519
|
@contextmanager
|
496
|
-
def catch_jinja(node=None) -> Iterator[None]:
|
520
|
+
def catch_jinja(node: Optional[_NodeProtocol] = None) -> Iterator[None]:
|
497
521
|
try:
|
498
522
|
yield
|
499
523
|
except jinja2.exceptions.TemplateSyntaxError as e:
|
@@ -506,16 +530,16 @@ def catch_jinja(node=None) -> Iterator[None]:
|
|
506
530
|
raise
|
507
531
|
|
508
532
|
|
509
|
-
_TESTING_PARSE_CACHE: Dict[str, jinja2.Template] = {}
|
533
|
+
_TESTING_PARSE_CACHE: Dict[str, jinja2.nodes.Template] = {}
|
510
534
|
|
511
535
|
|
512
|
-
def parse(string):
|
536
|
+
def parse(string: Any) -> jinja2.nodes.Template:
|
513
537
|
str_string = str(string)
|
514
538
|
if test_caching_enabled() and str_string in _TESTING_PARSE_CACHE:
|
515
539
|
return _TESTING_PARSE_CACHE[str_string]
|
516
540
|
|
517
541
|
with catch_jinja():
|
518
|
-
parsed = get_environment().parse(str(string))
|
542
|
+
parsed: jinja2.nodes.Template = get_environment().parse(str(string))
|
519
543
|
if test_caching_enabled():
|
520
544
|
_TESTING_PARSE_CACHE[str_string] = parsed
|
521
545
|
return parsed
|
@@ -524,10 +548,10 @@ def parse(string):
|
|
524
548
|
def get_template(
|
525
549
|
string: str,
|
526
550
|
ctx: Dict[str, Any],
|
527
|
-
node=None,
|
551
|
+
node: Optional[_NodeProtocol] = None,
|
528
552
|
capture_macros: bool = False,
|
529
553
|
native: bool = False,
|
530
|
-
):
|
554
|
+
) -> jinja2.Template:
|
531
555
|
with catch_jinja(node):
|
532
556
|
env = get_environment(node, capture_macros, native=native)
|
533
557
|
|
@@ -535,7 +559,9 @@ def get_template(
|
|
535
559
|
return env.from_string(template_source, globals=ctx)
|
536
560
|
|
537
561
|
|
538
|
-
def render_template(
|
562
|
+
def render_template(
|
563
|
+
template: jinja2.Template, ctx: Dict[str, Any], node: Optional[_NodeProtocol] = None
|
564
|
+
) -> str:
|
539
565
|
with catch_jinja(node):
|
540
566
|
return template.render(ctx)
|
541
567
|
|
@@ -96,10 +96,14 @@ class BaseConfig(AdditionalPropertiesAllowed, Replaceable):
|
|
96
96
|
return False
|
97
97
|
return True
|
98
98
|
|
99
|
-
# This is used in '
|
100
|
-
# '
|
99
|
+
# This is used in 'merge_config_dicts' to create the combined orig_dict.
|
100
|
+
# Note: "clobber" fields aren't defined, because that's the default.
|
101
|
+
# "access" is currently the only Clobber field.
|
102
|
+
# This shouldn't really be defined here. It would be better to have it
|
103
|
+
# associated with the config definitions, but at the point we use it, we
|
104
|
+
# don't know which config we're dealing with.
|
101
105
|
mergebehavior = {
|
102
|
-
"append": ["pre-hook", "pre_hook", "post-hook", "post_hook", "tags"],
|
106
|
+
"append": ["pre-hook", "pre_hook", "post-hook", "post_hook", "tags", "packages"],
|
103
107
|
"update": [
|
104
108
|
"quoting",
|
105
109
|
"column_types",
|
@@ -108,6 +112,7 @@ class BaseConfig(AdditionalPropertiesAllowed, Replaceable):
|
|
108
112
|
"contract",
|
109
113
|
],
|
110
114
|
"dict_key_append": ["grants"],
|
115
|
+
"object": ["snapshot_meta_column_names"],
|
111
116
|
}
|
112
117
|
|
113
118
|
@classmethod
|
@@ -180,6 +185,7 @@ class MergeBehavior(Metadata):
|
|
180
185
|
Update = 2
|
181
186
|
Clobber = 3
|
182
187
|
DictKeyAppend = 4
|
188
|
+
Object = 5
|
183
189
|
|
184
190
|
@classmethod
|
185
191
|
def default_field(cls) -> "MergeBehavior":
|
@@ -215,8 +221,10 @@ def _listify(value: Any) -> List[Any]:
|
|
215
221
|
|
216
222
|
|
217
223
|
# There are two versions of this code. The one here is for config
|
218
|
-
# objects
|
219
|
-
#
|
224
|
+
# objects which can get the "MergeBehavior" from the field in the class,
|
225
|
+
# the one below in 'merge_config_dicts' (formerly in
|
226
|
+
# _add_config_call in core context_config.py) is for config_call dictionaries
|
227
|
+
# where we need to get the MergeBehavior from someplace else.
|
220
228
|
def _merge_field_value(
|
221
229
|
merge_behavior: MergeBehavior,
|
222
230
|
self_value: Any,
|
@@ -225,7 +233,8 @@ def _merge_field_value(
|
|
225
233
|
if merge_behavior == MergeBehavior.Clobber:
|
226
234
|
return other_value
|
227
235
|
elif merge_behavior == MergeBehavior.Append:
|
228
|
-
|
236
|
+
new_value = _listify(self_value) + _listify(other_value)
|
237
|
+
return new_value
|
229
238
|
elif merge_behavior == MergeBehavior.Update:
|
230
239
|
if not isinstance(self_value, dict):
|
231
240
|
raise DbtInternalError(f"expected dict, got {self_value}")
|
@@ -258,6 +267,73 @@ def _merge_field_value(
|
|
258
267
|
# clobber the list
|
259
268
|
new_dict[new_key] = _listify(other_value[key])
|
260
269
|
return new_dict
|
261
|
-
|
270
|
+
elif merge_behavior == MergeBehavior.Object:
|
271
|
+
# All fields in classes with MergeBehavior.Object should have a default of None
|
272
|
+
if not type(self_value).__name__ == type(other_value).__name__:
|
273
|
+
raise DbtInternalError(
|
274
|
+
f"got conflicting types: {type(self_value).__name__} and {type(other_value).__name__}"
|
275
|
+
)
|
276
|
+
new_value = self_value.copy()
|
277
|
+
new_value.update(other_value)
|
278
|
+
return new_value
|
262
279
|
else:
|
263
280
|
raise DbtInternalError(f"Got an invalid merge_behavior: {merge_behavior}")
|
281
|
+
|
282
|
+
|
283
|
+
# This is used in ContextConfig._add_config_call. It updates the orig_dict in place.
|
284
|
+
def merge_config_dicts(orig_dict: Dict[str, Any], new_dict: Dict[str, Any]) -> None:
|
285
|
+
# orig_dict is already encountered configs, new_dict is new
|
286
|
+
# This mirrors code in _merge_field_value in model_config.py which is similar but
|
287
|
+
# operates on config objects.
|
288
|
+
if orig_dict == {}:
|
289
|
+
orig_dict.update(new_dict)
|
290
|
+
return
|
291
|
+
for k, v in new_dict.items():
|
292
|
+
# MergeBehavior for post-hook and pre-hook is to collect all
|
293
|
+
# values, instead of overwriting
|
294
|
+
if k in BaseConfig.mergebehavior["append"]:
|
295
|
+
if k in orig_dict: # should always be a list here
|
296
|
+
orig_dict[k] = _listify(orig_dict[k]) + _listify(v)
|
297
|
+
else:
|
298
|
+
orig_dict[k] = _listify(v)
|
299
|
+
elif k in BaseConfig.mergebehavior["update"]:
|
300
|
+
if not isinstance(v, dict):
|
301
|
+
raise DbtInternalError(f"expected dict, got {v}")
|
302
|
+
if k in orig_dict and isinstance(orig_dict[k], dict):
|
303
|
+
orig_dict[k].update(v)
|
304
|
+
else:
|
305
|
+
orig_dict[k] = v
|
306
|
+
elif k in BaseConfig.mergebehavior["dict_key_append"]:
|
307
|
+
if not isinstance(v, dict):
|
308
|
+
raise DbtInternalError(f"expected dict, got {v}")
|
309
|
+
if k in orig_dict: # should always be a dict
|
310
|
+
for key in orig_dict[k].keys():
|
311
|
+
orig_dict[k][key] = _listify(orig_dict[k][key])
|
312
|
+
for key, value in v.items():
|
313
|
+
extend = False
|
314
|
+
# This might start with a +, to indicate we should extend the list
|
315
|
+
# instead of just clobbering it. We don't want to remove the + here
|
316
|
+
# (like in the other method) because we want it preserved
|
317
|
+
if key.startswith("+"):
|
318
|
+
extend = True
|
319
|
+
if key in orig_dict[k] and extend:
|
320
|
+
# extend the list
|
321
|
+
orig_dict[k][key].extend(_listify(value))
|
322
|
+
else:
|
323
|
+
# clobber the list
|
324
|
+
orig_dict[k][key] = _listify(value)
|
325
|
+
else:
|
326
|
+
# This is always a dictionary
|
327
|
+
orig_dict[k] = v
|
328
|
+
# listify everything
|
329
|
+
for key, value in orig_dict[k].items():
|
330
|
+
orig_dict[k][key] = _listify(value)
|
331
|
+
elif k in BaseConfig.mergebehavior["object"]:
|
332
|
+
if not isinstance(v, dict):
|
333
|
+
raise DbtInternalError(f"expected dict, got {v}")
|
334
|
+
if k not in orig_dict:
|
335
|
+
orig_dict[k] = {}
|
336
|
+
for obj_k, obj_v in v.items():
|
337
|
+
orig_dict[k][obj_k] = obj_v
|
338
|
+
else: # Clobber
|
339
|
+
orig_dict[k] = v
|
dbt_common/events/types.proto
CHANGED
@@ -26,17 +26,16 @@ message GenericMessage {
|
|
26
26
|
// D - Deprecations
|
27
27
|
|
28
28
|
// D018
|
29
|
-
message
|
29
|
+
message BehaviorChangeEvent {
|
30
30
|
string flag_name = 1;
|
31
31
|
string flag_source = 2;
|
32
|
-
string
|
33
|
-
string
|
34
|
-
string docs_url = 5;
|
32
|
+
string description = 3;
|
33
|
+
string docs_url = 4;
|
35
34
|
}
|
36
35
|
|
37
|
-
message
|
36
|
+
message BehaviorChangeEventMsg {
|
38
37
|
EventInfo info = 1;
|
39
|
-
|
38
|
+
BehaviorChangeEvent data = 2;
|
40
39
|
}
|
41
40
|
|
42
41
|
// M - Deps generation
|
dbt_common/events/types.py
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
1
|
from dbt_common.events.base_types import (
|
4
2
|
DebugLevel,
|
5
3
|
InfoLevel,
|
@@ -38,33 +36,16 @@ from dbt_common.ui import warning_tag
|
|
38
36
|
# =======================================================
|
39
37
|
|
40
38
|
|
41
|
-
class
|
42
|
-
flag_name: str
|
43
|
-
flag_source: str
|
44
|
-
deprecation_version: Optional[str]
|
45
|
-
deprecation_message: Optional[str]
|
46
|
-
docs_url: Optional[str]
|
47
|
-
|
39
|
+
class BehaviorChangeEvent(WarnLevel):
|
48
40
|
def code(self) -> str:
|
49
41
|
return "D018"
|
50
42
|
|
51
43
|
def message(self) -> str:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
)
|
58
|
-
|
59
|
-
msg += f"The new behavior can be turned on by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n"
|
60
|
-
|
61
|
-
if self.deprecation_message:
|
62
|
-
msg += f"{self.deprecation_message}.\n"
|
63
|
-
|
64
|
-
docs_url = self.docs_url or f"https://docs.getdbt.com/search?q={self.flag_name}"
|
65
|
-
msg += f"Visit {docs_url} for more information."
|
66
|
-
|
67
|
-
return warning_tag(msg)
|
44
|
+
return warning_tag(
|
45
|
+
f"{self.description}\n"
|
46
|
+
f"You may opt into the new behavior sooner by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n"
|
47
|
+
f"Visit {self.docs_url} for more information."
|
48
|
+
)
|
68
49
|
|
69
50
|
|
70
51
|
# =======================================================
|
dbt_common/events/types_pb2.py
CHANGED
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
|
|
15
15
|
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
|
16
16
|
|
17
17
|
|
18
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0btypes.proto\x12\x0bproto_types\x1a\x1fgoogle/protobuf/timestamp.proto\"\x91\x02\n\tEventInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0b\n\x03msg\x18\x03 \x01(\t\x12\r\n\x05level\x18\x04 \x01(\t\x12\x15\n\rinvocation_id\x18\x05 \x01(\t\x12\x0b\n\x03pid\x18\x06 \x01(\x05\x12\x0e\n\x06thread\x18\x07 \x01(\t\x12&\n\x02ts\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x05\x65xtra\x18\t \x03(\x0b\x32!.proto_types.EventInfo.ExtraEntry\x12\x10\n\x08\x63\x61tegory\x18\n \x01(\t\x1a,\n\nExtraEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"6\n\x0eGenericMessage\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\"\
|
18
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0btypes.proto\x12\x0bproto_types\x1a\x1fgoogle/protobuf/timestamp.proto\"\x91\x02\n\tEventInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0b\n\x03msg\x18\x03 \x01(\t\x12\r\n\x05level\x18\x04 \x01(\t\x12\x15\n\rinvocation_id\x18\x05 \x01(\t\x12\x0b\n\x03pid\x18\x06 \x01(\x05\x12\x0e\n\x06thread\x18\x07 \x01(\t\x12&\n\x02ts\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x05\x65xtra\x18\t \x03(\x0b\x32!.proto_types.EventInfo.ExtraEntry\x12\x10\n\x08\x63\x61tegory\x18\n \x01(\t\x1a,\n\nExtraEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"6\n\x0eGenericMessage\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\"d\n\x13\x42\x65haviorChangeEvent\x12\x11\n\tflag_name\x18\x01 \x01(\t\x12\x13\n\x0b\x66lag_source\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x10\n\x08\x64ocs_url\x18\x04 \x01(\t\"n\n\x16\x42\x65haviorChangeEventMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12.\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32 .proto_types.BehaviorChangeEvent\"1\n\x11RetryExternalCall\x12\x0f\n\x07\x61ttempt\x18\x01 \x01(\x05\x12\x0b\n\x03max\x18\x02 \x01(\x05\"j\n\x14RetryExternalCallMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12,\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x1e.proto_types.RetryExternalCall\"#\n\x14RecordRetryException\x12\x0b\n\x03\x65xc\x18\x01 \x01(\t\"p\n\x17RecordRetryExceptionMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12/\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32!.proto_types.RecordRetryException\"@\n\x13SystemCouldNotWrite\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x0b\n\x03\x65xc\x18\x03 \x01(\t\"n\n\x16SystemCouldNotWriteMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12.\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32 .proto_types.SystemCouldNotWrite\"!\n\x12SystemExecutingCmd\x12\x0b\n\x03\x63md\x18\x01 \x03(\t\"l\n\x15SystemExecutingCmdMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12-\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x1f.proto_types.SystemExecutingCmd\"\x1c\n\x0cSystemStdOut\x12\x0c\n\x04\x62msg\x18\x01 \x01(\t\"`\n\x0fSystemStdOutMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12\'\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x19.proto_types.SystemStdOut\"\x1c\n\x0cSystemStdErr\x12\x0c\n\x04\x62msg\x18\x01 \x01(\t\"`\n\x0fSystemStdErrMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12\'\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x19.proto_types.SystemStdErr\",\n\x16SystemReportReturnCode\x12\x12\n\nreturncode\x18\x01 \x01(\x05\"t\n\x19SystemReportReturnCodeMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12\x31\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32#.proto_types.SystemReportReturnCode\"\x19\n\nFormatting\x12\x0b\n\x03msg\x18\x01 \x01(\t\"\\\n\rFormattingMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.proto_types.Formatting\"\x13\n\x04Note\x12\x0b\n\x03msg\x18\x01 \x01(\t\"P\n\x07NoteMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12\x1f\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x11.proto_types.Note\"\x19\n\nPrintEvent\x12\x0b\n\x03msg\x18\x01 \x01(\t\"\\\n\rPrintEventMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12%\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x17.proto_types.PrintEventb\x06proto3')
|
19
19
|
|
20
20
|
_globals = globals()
|
21
21
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
@@ -30,48 +30,48 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
30
30
|
_globals['_EVENTINFO_EXTRAENTRY']._serialized_end=335
|
31
31
|
_globals['_GENERICMESSAGE']._serialized_start=337
|
32
32
|
_globals['_GENERICMESSAGE']._serialized_end=391
|
33
|
-
_globals['
|
34
|
-
_globals['
|
35
|
-
_globals['
|
36
|
-
_globals['
|
37
|
-
_globals['_RETRYEXTERNALCALL']._serialized_start=
|
38
|
-
_globals['_RETRYEXTERNALCALL']._serialized_end=
|
39
|
-
_globals['_RETRYEXTERNALCALLMSG']._serialized_start=
|
40
|
-
_globals['_RETRYEXTERNALCALLMSG']._serialized_end=
|
41
|
-
_globals['_RECORDRETRYEXCEPTION']._serialized_start=
|
42
|
-
_globals['_RECORDRETRYEXCEPTION']._serialized_end=
|
43
|
-
_globals['_RECORDRETRYEXCEPTIONMSG']._serialized_start=
|
44
|
-
_globals['_RECORDRETRYEXCEPTIONMSG']._serialized_end=
|
45
|
-
_globals['_SYSTEMCOULDNOTWRITE']._serialized_start=
|
46
|
-
_globals['_SYSTEMCOULDNOTWRITE']._serialized_end=
|
47
|
-
_globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_start=
|
48
|
-
_globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_end=
|
49
|
-
_globals['_SYSTEMEXECUTINGCMD']._serialized_start=
|
50
|
-
_globals['_SYSTEMEXECUTINGCMD']._serialized_end=
|
51
|
-
_globals['_SYSTEMEXECUTINGCMDMSG']._serialized_start=
|
52
|
-
_globals['_SYSTEMEXECUTINGCMDMSG']._serialized_end=
|
53
|
-
_globals['_SYSTEMSTDOUT']._serialized_start=
|
54
|
-
_globals['_SYSTEMSTDOUT']._serialized_end=
|
55
|
-
_globals['_SYSTEMSTDOUTMSG']._serialized_start=
|
56
|
-
_globals['_SYSTEMSTDOUTMSG']._serialized_end=
|
57
|
-
_globals['_SYSTEMSTDERR']._serialized_start=
|
58
|
-
_globals['_SYSTEMSTDERR']._serialized_end=
|
59
|
-
_globals['_SYSTEMSTDERRMSG']._serialized_start=
|
60
|
-
_globals['_SYSTEMSTDERRMSG']._serialized_end=
|
61
|
-
_globals['_SYSTEMREPORTRETURNCODE']._serialized_start=
|
62
|
-
_globals['_SYSTEMREPORTRETURNCODE']._serialized_end=
|
63
|
-
_globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_start=
|
64
|
-
_globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_end=
|
65
|
-
_globals['_FORMATTING']._serialized_start=
|
66
|
-
_globals['_FORMATTING']._serialized_end=
|
67
|
-
_globals['_FORMATTINGMSG']._serialized_start=
|
68
|
-
_globals['_FORMATTINGMSG']._serialized_end=
|
69
|
-
_globals['_NOTE']._serialized_start=
|
70
|
-
_globals['_NOTE']._serialized_end=
|
71
|
-
_globals['_NOTEMSG']._serialized_start=
|
72
|
-
_globals['_NOTEMSG']._serialized_end=
|
73
|
-
_globals['_PRINTEVENT']._serialized_start=
|
74
|
-
_globals['_PRINTEVENT']._serialized_end=
|
75
|
-
_globals['_PRINTEVENTMSG']._serialized_start=
|
76
|
-
_globals['_PRINTEVENTMSG']._serialized_end=
|
33
|
+
_globals['_BEHAVIORCHANGEEVENT']._serialized_start=393
|
34
|
+
_globals['_BEHAVIORCHANGEEVENT']._serialized_end=493
|
35
|
+
_globals['_BEHAVIORCHANGEEVENTMSG']._serialized_start=495
|
36
|
+
_globals['_BEHAVIORCHANGEEVENTMSG']._serialized_end=605
|
37
|
+
_globals['_RETRYEXTERNALCALL']._serialized_start=607
|
38
|
+
_globals['_RETRYEXTERNALCALL']._serialized_end=656
|
39
|
+
_globals['_RETRYEXTERNALCALLMSG']._serialized_start=658
|
40
|
+
_globals['_RETRYEXTERNALCALLMSG']._serialized_end=764
|
41
|
+
_globals['_RECORDRETRYEXCEPTION']._serialized_start=766
|
42
|
+
_globals['_RECORDRETRYEXCEPTION']._serialized_end=801
|
43
|
+
_globals['_RECORDRETRYEXCEPTIONMSG']._serialized_start=803
|
44
|
+
_globals['_RECORDRETRYEXCEPTIONMSG']._serialized_end=915
|
45
|
+
_globals['_SYSTEMCOULDNOTWRITE']._serialized_start=917
|
46
|
+
_globals['_SYSTEMCOULDNOTWRITE']._serialized_end=981
|
47
|
+
_globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_start=983
|
48
|
+
_globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_end=1093
|
49
|
+
_globals['_SYSTEMEXECUTINGCMD']._serialized_start=1095
|
50
|
+
_globals['_SYSTEMEXECUTINGCMD']._serialized_end=1128
|
51
|
+
_globals['_SYSTEMEXECUTINGCMDMSG']._serialized_start=1130
|
52
|
+
_globals['_SYSTEMEXECUTINGCMDMSG']._serialized_end=1238
|
53
|
+
_globals['_SYSTEMSTDOUT']._serialized_start=1240
|
54
|
+
_globals['_SYSTEMSTDOUT']._serialized_end=1268
|
55
|
+
_globals['_SYSTEMSTDOUTMSG']._serialized_start=1270
|
56
|
+
_globals['_SYSTEMSTDOUTMSG']._serialized_end=1366
|
57
|
+
_globals['_SYSTEMSTDERR']._serialized_start=1368
|
58
|
+
_globals['_SYSTEMSTDERR']._serialized_end=1396
|
59
|
+
_globals['_SYSTEMSTDERRMSG']._serialized_start=1398
|
60
|
+
_globals['_SYSTEMSTDERRMSG']._serialized_end=1494
|
61
|
+
_globals['_SYSTEMREPORTRETURNCODE']._serialized_start=1496
|
62
|
+
_globals['_SYSTEMREPORTRETURNCODE']._serialized_end=1540
|
63
|
+
_globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_start=1542
|
64
|
+
_globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_end=1658
|
65
|
+
_globals['_FORMATTING']._serialized_start=1660
|
66
|
+
_globals['_FORMATTING']._serialized_end=1685
|
67
|
+
_globals['_FORMATTINGMSG']._serialized_start=1687
|
68
|
+
_globals['_FORMATTINGMSG']._serialized_end=1779
|
69
|
+
_globals['_NOTE']._serialized_start=1781
|
70
|
+
_globals['_NOTE']._serialized_end=1800
|
71
|
+
_globals['_NOTEMSG']._serialized_start=1802
|
72
|
+
_globals['_NOTEMSG']._serialized_end=1882
|
73
|
+
_globals['_PRINTEVENT']._serialized_start=1884
|
74
|
+
_globals['_PRINTEVENT']._serialized_end=1909
|
75
|
+
_globals['_PRINTEVENTMSG']._serialized_start=1911
|
76
|
+
_globals['_PRINTEVENTMSG']._serialized_end=2003
|
77
77
|
# @@protoc_insertion_point(module_scope)
|
dbt_common/exceptions/jinja.py
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
if TYPE_CHECKING:
|
4
|
+
from dbt_common.clients._jinja_blocks import Tag, TagIterator
|
5
|
+
|
1
6
|
from dbt_common.exceptions import CompilationError
|
2
7
|
|
3
8
|
|
4
9
|
class BlockDefinitionNotAtTopError(CompilationError):
|
5
|
-
def __init__(self, tag_parser, tag_start) -> None:
|
10
|
+
def __init__(self, tag_parser: "TagIterator", tag_start: int) -> None:
|
6
11
|
self.tag_parser = tag_parser
|
7
12
|
self.tag_start = tag_start
|
8
13
|
super().__init__(msg=self.get_message())
|
@@ -31,7 +36,7 @@ class MissingCloseTagError(CompilationError):
|
|
31
36
|
|
32
37
|
|
33
38
|
class MissingControlFlowStartTagError(CompilationError):
|
34
|
-
def __init__(self, tag, expected_tag: str, tag_parser) -> None:
|
39
|
+
def __init__(self, tag: "Tag", expected_tag: str, tag_parser: "TagIterator") -> None:
|
35
40
|
self.tag = tag
|
36
41
|
self.expected_tag = expected_tag
|
37
42
|
self.tag_parser = tag_parser
|
@@ -47,7 +52,7 @@ class MissingControlFlowStartTagError(CompilationError):
|
|
47
52
|
|
48
53
|
|
49
54
|
class NestedTagsError(CompilationError):
|
50
|
-
def __init__(self, outer, inner) -> None:
|
55
|
+
def __init__(self, outer: "Tag", inner: "Tag") -> None:
|
51
56
|
self.outer = outer
|
52
57
|
self.inner = inner
|
53
58
|
super().__init__(msg=self.get_message())
|
@@ -62,7 +67,7 @@ class NestedTagsError(CompilationError):
|
|
62
67
|
|
63
68
|
|
64
69
|
class UnexpectedControlFlowEndTagError(CompilationError):
|
65
|
-
def __init__(self, tag, expected_tag: str, tag_parser) -> None:
|
70
|
+
def __init__(self, tag: "Tag", expected_tag: str, tag_parser: "TagIterator") -> None:
|
66
71
|
self.tag = tag
|
67
72
|
self.expected_tag = expected_tag
|
68
73
|
self.tag_parser = tag_parser
|
dbt_common/record.py
CHANGED
@@ -29,8 +29,14 @@ class Record:
|
|
29
29
|
|
30
30
|
def to_dict(self) -> Dict[str, Any]:
|
31
31
|
return {
|
32
|
-
"params": self.params._to_dict()
|
33
|
-
|
32
|
+
"params": self.params._to_dict()
|
33
|
+
if hasattr(self.params, "_to_dict")
|
34
|
+
else dataclasses.asdict(self.params),
|
35
|
+
"result": self.result._to_dict()
|
36
|
+
if hasattr(self.result, "_to_dict")
|
37
|
+
else dataclasses.asdict(self.result)
|
38
|
+
if self.result is not None
|
39
|
+
else None,
|
34
40
|
}
|
35
41
|
|
36
42
|
@classmethod
|
dbt_common/semver.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
import re
|
3
|
-
from typing import Iterable, List, Union
|
3
|
+
from typing import Any, Iterable, List, Union
|
4
4
|
|
5
5
|
import dbt_common.exceptions.base
|
6
6
|
from dbt_common.exceptions import VersionsNotCompatibleError
|
@@ -67,9 +67,9 @@ $
|
|
67
67
|
_VERSION_REGEX = re.compile(_VERSION_REGEX_PAT_STR, re.VERBOSE)
|
68
68
|
|
69
69
|
|
70
|
-
def _cmp(a, b) -> int:
|
70
|
+
def _cmp(a: Any, b: Any) -> int:
|
71
71
|
"""Return negative if a<b, zero if a==b, positive if a>b."""
|
72
|
-
return (a > b) - (a < b)
|
72
|
+
return int((a > b) - (a < b))
|
73
73
|
|
74
74
|
|
75
75
|
@dataclass
|
@@ -102,7 +102,9 @@ class VersionSpecifier(VersionSpecification):
|
|
102
102
|
|
103
103
|
matched = {k: v for k, v in match.groupdict().items() if v is not None}
|
104
104
|
|
105
|
-
|
105
|
+
spec = cls.from_dict(matched)
|
106
|
+
assert isinstance(spec, VersionSpecifier)
|
107
|
+
return spec
|
106
108
|
|
107
109
|
def __str__(self) -> str:
|
108
110
|
return self.to_version_string()
|
@@ -198,10 +200,11 @@ class VersionSpecifier(VersionSpecification):
|
|
198
200
|
def __gt__(self, other: "VersionSpecifier") -> bool:
|
199
201
|
return self.compare(other) == 1
|
200
202
|
|
201
|
-
def
|
203
|
+
def __eq__(self, other: object) -> bool:
|
204
|
+
assert isinstance(other, VersionSpecifier)
|
202
205
|
return self.compare(other) == 0
|
203
206
|
|
204
|
-
def
|
207
|
+
def __cmp__(self, other: "VersionSpecifier") -> int:
|
205
208
|
return self.compare(other)
|
206
209
|
|
207
210
|
@property
|
@@ -221,8 +224,8 @@ class VersionSpecifier(VersionSpecification):
|
|
221
224
|
return self.matcher == Matchers.EXACT
|
222
225
|
|
223
226
|
@classmethod
|
224
|
-
def _nat_cmp(cls, a, b) -> int:
|
225
|
-
def cmp_prerelease_tag(a, b):
|
227
|
+
def _nat_cmp(cls, a: str, b: str) -> int:
|
228
|
+
def cmp_prerelease_tag(a: Union[str, int], b: Union[str, int]) -> int:
|
226
229
|
if isinstance(a, int) and isinstance(b, int):
|
227
230
|
return _cmp(a, b)
|
228
231
|
elif isinstance(a, int):
|
@@ -234,10 +237,10 @@ class VersionSpecifier(VersionSpecification):
|
|
234
237
|
|
235
238
|
a, b = a or "", b or ""
|
236
239
|
a_parts, b_parts = a.split("."), b.split(".")
|
237
|
-
|
238
|
-
|
239
|
-
for sub_a, sub_b in zip(
|
240
|
-
cmp_result = cmp_prerelease_tag(sub_a, sub_b)
|
240
|
+
a_parts_2 = [int(x) if re.match(r"^\d+$", x) else x for x in a_parts]
|
241
|
+
b_parts_2 = [int(x) if re.match(r"^\d+$", x) else x for x in b_parts]
|
242
|
+
for sub_a, sub_b in zip(a_parts_2, b_parts_2):
|
243
|
+
cmp_result = cmp_prerelease_tag(sub_a, sub_b) # type: ignore
|
241
244
|
if cmp_result != 0:
|
242
245
|
return cmp_result
|
243
246
|
else:
|
@@ -249,13 +252,15 @@ class VersionRange:
|
|
249
252
|
start: VersionSpecifier
|
250
253
|
end: VersionSpecifier
|
251
254
|
|
252
|
-
def _try_combine_exact(self, a, b):
|
255
|
+
def _try_combine_exact(self, a: VersionSpecifier, b: VersionSpecifier) -> VersionSpecifier:
|
253
256
|
if a.compare(b) == 0:
|
254
257
|
return a
|
255
258
|
else:
|
256
259
|
raise VersionsNotCompatibleError()
|
257
260
|
|
258
|
-
def _try_combine_lower_bound_with_exact(
|
261
|
+
def _try_combine_lower_bound_with_exact(
|
262
|
+
self, lower: VersionSpecifier, exact: VersionSpecifier
|
263
|
+
) -> VersionSpecifier:
|
259
264
|
comparison = lower.compare(exact)
|
260
265
|
|
261
266
|
if comparison < 0 or (comparison == 0 and lower.matcher == Matchers.GREATER_THAN_OR_EQUAL):
|
@@ -263,7 +268,9 @@ class VersionRange:
|
|
263
268
|
|
264
269
|
raise VersionsNotCompatibleError()
|
265
270
|
|
266
|
-
def _try_combine_lower_bound(
|
271
|
+
def _try_combine_lower_bound(
|
272
|
+
self, a: VersionSpecifier, b: VersionSpecifier
|
273
|
+
) -> VersionSpecifier:
|
267
274
|
if b.is_unbounded:
|
268
275
|
return a
|
269
276
|
elif a.is_unbounded:
|
@@ -280,10 +287,12 @@ class VersionRange:
|
|
280
287
|
elif a.is_exact:
|
281
288
|
return self._try_combine_lower_bound_with_exact(b, a)
|
282
289
|
|
283
|
-
|
290
|
+
else:
|
284
291
|
return self._try_combine_lower_bound_with_exact(a, b)
|
285
292
|
|
286
|
-
def _try_combine_upper_bound_with_exact(
|
293
|
+
def _try_combine_upper_bound_with_exact(
|
294
|
+
self, upper: VersionSpecifier, exact: VersionSpecifier
|
295
|
+
) -> VersionSpecifier:
|
287
296
|
comparison = upper.compare(exact)
|
288
297
|
|
289
298
|
if comparison > 0 or (comparison == 0 and upper.matcher == Matchers.LESS_THAN_OR_EQUAL):
|
@@ -291,7 +300,9 @@ class VersionRange:
|
|
291
300
|
|
292
301
|
raise VersionsNotCompatibleError()
|
293
302
|
|
294
|
-
def _try_combine_upper_bound(
|
303
|
+
def _try_combine_upper_bound(
|
304
|
+
self, a: VersionSpecifier, b: VersionSpecifier
|
305
|
+
) -> VersionSpecifier:
|
295
306
|
if b.is_unbounded:
|
296
307
|
return a
|
297
308
|
elif a.is_unbounded:
|
@@ -308,15 +319,14 @@ class VersionRange:
|
|
308
319
|
elif a.is_exact:
|
309
320
|
return self._try_combine_upper_bound_with_exact(b, a)
|
310
321
|
|
311
|
-
|
322
|
+
else:
|
312
323
|
return self._try_combine_upper_bound_with_exact(a, b)
|
313
324
|
|
314
|
-
def reduce(self, other):
|
325
|
+
def reduce(self, other: "VersionRange") -> "VersionRange":
|
315
326
|
start = None
|
316
327
|
|
317
328
|
if self.start.is_exact and other.start.is_exact:
|
318
329
|
start = end = self._try_combine_exact(self.start, other.start)
|
319
|
-
|
320
330
|
else:
|
321
331
|
start = self._try_combine_lower_bound(self.start, other.start)
|
322
332
|
end = self._try_combine_upper_bound(self.end, other.end)
|
@@ -326,7 +336,7 @@ class VersionRange:
|
|
326
336
|
|
327
337
|
return VersionRange(start=start, end=end)
|
328
338
|
|
329
|
-
def __str__(self):
|
339
|
+
def __str__(self) -> str:
|
330
340
|
result = []
|
331
341
|
|
332
342
|
if self.start.is_unbounded and self.end.is_unbounded:
|
@@ -340,7 +350,7 @@ class VersionRange:
|
|
340
350
|
|
341
351
|
return ", ".join(result)
|
342
352
|
|
343
|
-
def to_version_string_pair(self):
|
353
|
+
def to_version_string_pair(self) -> List[str]:
|
344
354
|
to_return = []
|
345
355
|
|
346
356
|
if not self.start.is_unbounded:
|
@@ -353,7 +363,7 @@ class VersionRange:
|
|
353
363
|
|
354
364
|
|
355
365
|
class UnboundedVersionSpecifier(VersionSpecifier):
|
356
|
-
def __init__(self, *args, **kwargs) -> None:
|
366
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
357
367
|
super().__init__(
|
358
368
|
matcher=Matchers.EXACT, major=None, minor=None, patch=None, prerelease=None, build=None
|
359
369
|
)
|
@@ -418,7 +428,7 @@ def reduce_versions(*args: Union[VersionSpecifier, VersionRange, str]) -> Versio
|
|
418
428
|
return to_return
|
419
429
|
|
420
430
|
|
421
|
-
def versions_compatible(*args) -> bool:
|
431
|
+
def versions_compatible(*args: Union[VersionSpecifier, VersionRange, str]) -> bool:
|
422
432
|
if len(args) == 1:
|
423
433
|
return True
|
424
434
|
|
@@ -429,7 +439,9 @@ def versions_compatible(*args) -> bool:
|
|
429
439
|
return False
|
430
440
|
|
431
441
|
|
432
|
-
def find_possible_versions(
|
442
|
+
def find_possible_versions(
|
443
|
+
requested_range: VersionRange, available_versions: Iterable[str]
|
444
|
+
) -> List[str]:
|
433
445
|
possible_versions = []
|
434
446
|
|
435
447
|
for version_string in available_versions:
|
@@ -443,7 +455,7 @@ def find_possible_versions(requested_range, available_versions: Iterable[str]):
|
|
443
455
|
|
444
456
|
|
445
457
|
def resolve_to_specific_version(
|
446
|
-
requested_range, available_versions: Iterable[str]
|
458
|
+
requested_range: VersionRange, available_versions: Iterable[str]
|
447
459
|
) -> Optional[str]:
|
448
460
|
max_version = None
|
449
461
|
max_version_string = None
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: dbt-common
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.9.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
|
@@ -1,27 +1,27 @@
|
|
1
|
-
dbt_common/__about__.py,sha256=
|
1
|
+
dbt_common/__about__.py,sha256=cXjavBUobbggOyp9SOIeDA3iCRdu2Hzw1qBttGe-RYs,18
|
2
2
|
dbt_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
dbt_common/behavior_flags.py,sha256=
|
3
|
+
dbt_common/behavior_flags.py,sha256=GkrXvJ-Q7P73O0XcsIxyYeyiUAOe5E5DdNKIh6x3unE,4809
|
4
4
|
dbt_common/constants.py,sha256=-Y5DIL1SDPQWtlCNizXRYxFgbx1D7LaLs1ysamvGMRk,278
|
5
5
|
dbt_common/context.py,sha256=rk4EYBU4SpDXRhqbSvDTJwojilxPSoaiEdOxkXow_BU,2549
|
6
6
|
dbt_common/dataclass_schema.py,sha256=u2S0dxwxIghv8RMqC91HlWZJVxmsC_844yZQaGyOwdY,5563
|
7
7
|
dbt_common/helper_types.py,sha256=FWJGPmp7Qp2iToHyI4uvhkBbu_d1tl2_oF-obi98_N4,3917
|
8
8
|
dbt_common/invocation.py,sha256=2ZchIr4Wq7NwtAjjB-mxHP9ITD6-r45jEq4Zooso0gc,210
|
9
9
|
dbt_common/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
dbt_common/record.py,sha256=
|
11
|
-
dbt_common/semver.py,sha256=
|
10
|
+
dbt_common/record.py,sha256=vItvHEF2yl_NbvuaIXYHo-q6v0LUpCxR2twcTMVCeQ0,12698
|
11
|
+
dbt_common/semver.py,sha256=Znewz6tc_NBpXr4mZf20bK_RayPL4ODrnxDbkUZrrRo,15034
|
12
12
|
dbt_common/tests.py,sha256=6lC_JuRtoYO6cbAF8-R5aTM4HtQiM_EH8X5m_97duGY,315
|
13
13
|
dbt_common/ui.py,sha256=rc2TEM29raBFc_LXcg901pMDD07C2ohwp9qzkE-7pBY,2567
|
14
14
|
dbt_common/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
dbt_common/clients/_jinja_blocks.py,sha256=xoJK9Y0F93U2PKfT_3SJbBopCGYCtl7LiwKuylXnrEE,12947
|
16
16
|
dbt_common/clients/agate_helper.py,sha256=anKKgKV5PSnFRuFeBwRMdHK3JCaQoUqB2ZXHD0su0Wo,9123
|
17
|
-
dbt_common/clients/jinja.py,sha256
|
17
|
+
dbt_common/clients/jinja.py,sha256=-eDrUnhH3nxOD0xu81jlweJ_iJqIWdJpYSHZBB27xXo,19682
|
18
18
|
dbt_common/clients/system.py,sha256=aoUBtOuXVmkOyj6IhhJ3Y4a7JFzPO2F_zKyOtz3xy44,23932
|
19
19
|
dbt_common/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
20
|
dbt_common/contracts/constraints.py,sha256=_f1q3Rkcg2UwA7zI5XBbUMXnPUg6pw17UC7l1HyhyQM,1352
|
21
21
|
dbt_common/contracts/metadata.py,sha256=K_M06Rue0wmrQhFP_mq3uvQszq10CIt93oGiAVgbRfE,1293
|
22
22
|
dbt_common/contracts/util.py,sha256=_-vtcI6ZGmYVgrlxOw4M3YuH34Ncx-oKdKg7TV0pV78,916
|
23
23
|
dbt_common/contracts/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
-
dbt_common/contracts/config/base.py,sha256=
|
24
|
+
dbt_common/contracts/config/base.py,sha256=vLkmnwWUGazIpUuUYqdnTrpBmFLJQ9hantsIv7T0GoU,12315
|
25
25
|
dbt_common/contracts/config/materialization.py,sha256=rahC72qZ0-jB8oPwxyPooZXc5NJ-smg74g2HnGOMj34,256
|
26
26
|
dbt_common/contracts/config/metadata.py,sha256=X47-tEA8q2ZfSMcYv0godUwTSVjt8NI77tD4NqdgM0c,1877
|
27
27
|
dbt_common/contracts/config/properties.py,sha256=gWt6xsP4rVOqRKhmagiUhWnDynzD9mykfMYMTviwpEU,2281
|
@@ -37,16 +37,16 @@ dbt_common/events/functions.py,sha256=K-R-FBTeO03U51gtMBu1EUcNsIAsgw_e5spWxLJ44V
|
|
37
37
|
dbt_common/events/helpers.py,sha256=CfsWwNDjsLJkPIgOtAfuLEnZ3rGUKeYsH8aDtCW12OA,410
|
38
38
|
dbt_common/events/interfaces.py,sha256=hEDeDoB0FW2RYHVZBG7gebEt_mUVBzkn1yPubpaxs-s,147
|
39
39
|
dbt_common/events/logger.py,sha256=mAUNLZlIIOl3T2u7KOe8FF_deTNNe1CRJqmkPw4YH1U,6728
|
40
|
-
dbt_common/events/types.proto,sha256=
|
41
|
-
dbt_common/events/types.py,sha256=
|
42
|
-
dbt_common/events/types_pb2.py,sha256=
|
40
|
+
dbt_common/events/types.proto,sha256=Ujl0O-X-pat8vlo2C0TMH1LZqa8EP_9f8k2TjbFuCV8,2276
|
41
|
+
dbt_common/events/types.py,sha256=MXCmG7qaj7hLbDZjjazjWftPTfoLjhNPATPMirO0DvU,4475
|
42
|
+
dbt_common/events/types_pb2.py,sha256=oFjR6pFqz3_Nk8wWIrVB9y6inNTxnj-CSGr2g8dnFz4,7167
|
43
43
|
dbt_common/exceptions/__init__.py,sha256=X_Uw7BxOzXev_9JMYfs5Cm-_i_Qf2PJim8_-dDJI7Y8,361
|
44
44
|
dbt_common/exceptions/base.py,sha256=d6lsA8sLqR6_BERowm91Mrs1ZwrEOeT8WQKWg4nPSPA,7750
|
45
45
|
dbt_common/exceptions/cache.py,sha256=0z4fBcdNZMAR41YbPRo2GN0__xAMaYs8Uc-t3hjmVio,2532
|
46
46
|
dbt_common/exceptions/connection.py,sha256=rXLJXUdLhyXP3CUUyiqWN2DDO5-0Pn1ChX3OIR7pxKk,176
|
47
47
|
dbt_common/exceptions/contracts.py,sha256=i2PKRqda1Pq_Sro9FA22W7FTGklhBAGl6__nimR5_qI,527
|
48
48
|
dbt_common/exceptions/events.py,sha256=j83szhbqmK3ITZR_xwA2dYTNaPcGF_lN_kQar6dQgjQ,317
|
49
|
-
dbt_common/exceptions/jinja.py,sha256=
|
49
|
+
dbt_common/exceptions/jinja.py,sha256=EfqWKLePqPIfzFjn1-Sn_lEp3fttzhzwogo0I-McAdc,3321
|
50
50
|
dbt_common/exceptions/macros.py,sha256=2nujJrtpWHnhBwcyhcOmeVaEzILh3W9gtyP6vVpkA0o,3301
|
51
51
|
dbt_common/exceptions/system.py,sha256=scoKnSx2frTFCrfk2cH0g-z3MuzE-SEBXFC9P-jzB2s,1603
|
52
52
|
dbt_common/utils/__init__.py,sha256=8PNb_A9zm2YfYMa0GsM-pAyJy3Iu0FUtWKvCs8kFhD0,549
|
@@ -57,7 +57,7 @@ dbt_common/utils/encoding.py,sha256=6_kSY2FvGNYMg7oX7PrbvVioieydih3Kl7Ii802LaHI,
|
|
57
57
|
dbt_common/utils/executor.py,sha256=pNY0UbPlwQmTE69Vt_Rj91YGCIOEaqeYU3CjAds0T70,2454
|
58
58
|
dbt_common/utils/formatting.py,sha256=JUn5rzJ-uajs9wPCN0-f2iRFY1pOJF5YjTD9dERuLoc,165
|
59
59
|
dbt_common/utils/jinja.py,sha256=JXgNmJArGGy0h7qkbNLA3zaEQmoF1CxsNBYTlIwFXDw,1101
|
60
|
-
dbt_common-1.
|
61
|
-
dbt_common-1.
|
62
|
-
dbt_common-1.
|
63
|
-
dbt_common-1.
|
60
|
+
dbt_common-1.9.0.dist-info/METADATA,sha256=gJ4sX5mtKWkJP66WbImh-5UrFsb5Mo3KQkoGkzbbsAQ,5298
|
61
|
+
dbt_common-1.9.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
62
|
+
dbt_common-1.9.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
63
|
+
dbt_common-1.9.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|