dbt-common 1.13.0__py3-none-any.whl → 1.15.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 CHANGED
@@ -1 +1 @@
1
- version = "1.13.0"
1
+ version = "1.15.0"
@@ -1,4 +1,5 @@
1
1
  import codecs
2
+ import dataclasses
2
3
  import linecache
3
4
  import os
4
5
  import tempfile
@@ -46,6 +47,7 @@ from dbt_common.exceptions import (
46
47
  MaterializationArgError,
47
48
  JinjaRenderingError,
48
49
  UndefinedCompilationError,
50
+ DbtRuntimeError,
49
51
  )
50
52
  from dbt_common.exceptions.macros import MacroReturn, UndefinedMacroError, CaughtMacroError
51
53
 
@@ -89,6 +91,12 @@ def _linecache_inject(source: str, write: bool) -> str:
89
91
  return filename
90
92
 
91
93
 
94
+ @dataclasses.dataclass
95
+ class MacroType:
96
+ name: str
97
+ type_params: List["MacroType"] = dataclasses.field(default_factory=list)
98
+
99
+
92
100
  class MacroFuzzParser(jinja2.parser.Parser):
93
101
  def parse_macro(self) -> jinja2.nodes.Macro:
94
102
  node = jinja2.nodes.Macro(lineno=next(self.stream).lineno)
@@ -102,6 +110,65 @@ class MacroFuzzParser(jinja2.parser.Parser):
102
110
  node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
103
111
  return node
104
112
 
113
+ def parse_signature(self, node: Union[jinja2.nodes.Macro, jinja2.nodes.CallBlock]) -> None:
114
+ """Overrides the default jinja Parser.parse_signature method, modifying
115
+ the original implementation to allow macros to have typed parameters."""
116
+
117
+ # Jinja does not support extending its node types, such as Macro, so
118
+ # at least while typed macros are experimental, we will patch the
119
+ # information onto the existing types.
120
+ setattr(node, "arg_types", [])
121
+ setattr(node, "has_type_annotations", False)
122
+
123
+ args = node.args = [] # type: ignore
124
+ defaults = node.defaults = [] # type: ignore
125
+
126
+ self.stream.expect("lparen")
127
+ while self.stream.current.type != "rparen":
128
+ if args:
129
+ self.stream.expect("comma")
130
+
131
+ arg = self.parse_assign_target(name_only=True)
132
+ arg.set_ctx("param")
133
+
134
+ type_name: Optional[str]
135
+ if self.stream.skip_if("colon"):
136
+ node.has_type_annotations = True # type: ignore
137
+ type_name = self.parse_type_name()
138
+ else:
139
+ type_name = ""
140
+
141
+ node.arg_types.append(type_name) # type: ignore
142
+
143
+ if self.stream.skip_if("assign"):
144
+ defaults.append(self.parse_expression())
145
+ elif defaults:
146
+ self.fail("non-default argument follows default argument")
147
+
148
+ args.append(arg)
149
+ self.stream.expect("rparen")
150
+
151
+ def parse_type_name(self) -> MacroType:
152
+ # NOTE: Types syntax is validated here, but not whether type names
153
+ # are valid or have correct parameters.
154
+
155
+ # A type name should consist of a name (i.e. 'Dict')...
156
+ type_name = self.stream.expect("name").value
157
+ type = MacroType(type_name)
158
+
159
+ # ..and an optional comma-delimited list of type parameters
160
+ # as in the type declaration 'Dict[str, str]'
161
+ if self.stream.skip_if("lbracket"):
162
+ while self.stream.current.type != "rbracket":
163
+ if type.type_params:
164
+ self.stream.expect("comma")
165
+ param_type = self.parse_type_name()
166
+ type.type_params.append(param_type)
167
+
168
+ self.stream.expect("rbracket")
169
+
170
+ return type
171
+
105
172
 
106
173
  class MacroFuzzEnvironment(jinja2.sandbox.SandboxedEnvironment):
107
174
  def _parse(
@@ -534,6 +601,12 @@ def catch_jinja(node: Optional[_NodeProtocol] = None) -> Iterator[None]:
534
601
  except CompilationError as exc:
535
602
  exc.add_node(node)
536
603
  raise
604
+ except DbtRuntimeError:
605
+ # Propagate dbt exception raised during jinja compilation
606
+ raise
607
+ except Exception as e:
608
+ # Raise any non-dbt exceptions as CompilationError
609
+ raise CompilationError(str(e), node) from e
537
610
 
538
611
 
539
612
  _TESTING_PARSE_CACHE: Dict[str, jinja2.nodes.Template] = {}
dbt_common/context.py CHANGED
@@ -39,6 +39,7 @@ class InvocationContext:
39
39
  else:
40
40
  self._env = env_public
41
41
 
42
+ self.name = "unset"
42
43
  self._env_secrets: Optional[List[str]] = None
43
44
  self._env_private = env_private
44
45
  self.recorder: Optional[Recorder] = None
dbt_common/invocation.py CHANGED
@@ -1,12 +1,19 @@
1
1
  import uuid
2
+ from datetime import datetime
2
3
 
3
4
  _INVOCATION_ID = str(uuid.uuid4())
5
+ _INVOCATION_STARTED_AT = datetime.utcnow()
4
6
 
5
7
 
6
8
  def get_invocation_id() -> str:
7
9
  return _INVOCATION_ID
8
10
 
9
11
 
12
+ def get_invocation_started_at() -> datetime:
13
+ return _INVOCATION_STARTED_AT
14
+
15
+
10
16
  def reset_invocation_id() -> None:
11
- global _INVOCATION_ID
17
+ global _INVOCATION_ID, _INVOCATION_STARTED_AT
12
18
  _INVOCATION_ID = str(uuid.uuid4())
19
+ _INVOCATION_STARTED_AT = datetime.utcnow()
dbt_common/record.py CHANGED
@@ -1,17 +1,25 @@
1
- """The record module provides a mechanism for recording dbt's interaction with
2
- external systems during a command invocation, so that the command can be re-run
3
- later with the recording 'replayed' to dbt.
1
+ """The record module provides a record/replay mechanism for recording dbt's
2
+ interactions with external systems during a command invocation, so that the
3
+ command can be re-run later with the recording 'replayed' to dbt.
4
4
 
5
5
  The rationale for and architecture of this module are described in detail in the
6
6
  docs/guides/record_replay.md document in this repository.
7
7
  """
8
8
  import functools
9
9
  import dataclasses
10
+ import inspect
10
11
  import json
11
12
  import os
12
13
 
13
14
  from enum import Enum
14
- from typing import Any, Callable, Dict, List, Mapping, Optional, Type
15
+ from typing import Any, Callable, Dict, List, Mapping, Optional, TextIO, Tuple, Type
16
+ import contextvars
17
+
18
+ from mashumaro import field_options
19
+ from mashumaro.mixins.json import DataClassJSONMixin
20
+ from mashumaro.types import SerializationStrategy
21
+
22
+ RECORDED_BY_HIGHER_FUNCTION = contextvars.ContextVar("RECORDED_BY_HIGHER_FUNCTION", default=False)
15
23
 
16
24
 
17
25
  class Record:
@@ -129,6 +137,7 @@ class RecorderMode(Enum):
129
137
  class Recorder:
130
138
  _record_cls_by_name: Dict[str, Type] = {}
131
139
  _record_name_by_params_name: Dict[str, str] = {}
140
+ _auto_serialization_strategies: Dict[Type, SerializationStrategy] = {}
132
141
 
133
142
  def __init__(
134
143
  self,
@@ -189,9 +198,13 @@ class Recorder:
189
198
 
190
199
  return match
191
200
 
201
+ def write_json(self, out_stream: TextIO):
202
+ d = self._to_dict()
203
+ json.dump(d, out_stream)
204
+
192
205
  def write(self) -> None:
193
206
  with open(self.current_recording_path, "w") as file:
194
- json.dump(self._to_dict(), file)
207
+ self.write_json(file)
195
208
 
196
209
  def _to_dict(self) -> Dict:
197
210
  dct: Dict[str, Any] = {}
@@ -205,7 +218,11 @@ class Recorder:
205
218
  @classmethod
206
219
  def load(cls, file_name: str) -> Dict[str, List[Dict[str, Any]]]:
207
220
  with open(file_name) as file:
208
- return json.load(file)
221
+ return cls.load_json(file)
222
+
223
+ @classmethod
224
+ def load_json(cls, in_stream: TextIO) -> Dict[str, List[Dict[str, Any]]]:
225
+ return json.load(in_stream)
209
226
 
210
227
  def _ensure_records_processed(self, record_type_name: str) -> None:
211
228
  if record_type_name in self._records_by_type:
@@ -239,6 +256,12 @@ class Recorder:
239
256
  assert self.diff is not None
240
257
  print(repr(self.diff.calculate_diff()))
241
258
 
259
+ @classmethod
260
+ def register_serialization_strategy(
261
+ cls, t: Type, serialization_strategy: SerializationStrategy
262
+ ) -> None:
263
+ cls._auto_serialization_strategies[t] = serialization_strategy
264
+
242
265
 
243
266
  def get_record_mode_from_env() -> Optional[RecorderMode]:
244
267
  """
@@ -271,7 +294,7 @@ def get_record_types_from_env() -> Optional[List]:
271
294
 
272
295
  If no types are provided, there will be no filtering.
273
296
  Invalid types will be ignored.
274
- Expected format: 'DBT_RECORDER_TYPES=QueryRecord,FileLoadRecord,OtherRecord'
297
+ Expected format: 'DBT_RECORDER_TYPES=Database,FileLoadRecord'
275
298
  """
276
299
  record_types_str = os.environ.get("DBT_RECORDER_TYPES")
277
300
 
@@ -283,74 +306,255 @@ def get_record_types_from_env() -> Optional[List]:
283
306
 
284
307
 
285
308
  def get_record_types_from_dict(fp: str) -> List:
286
- """
287
- Get the record subset from the dict.
288
- """
309
+ """Get the record subset from the dict."""
289
310
  with open(fp) as file:
290
311
  loaded_dct = json.load(file)
291
312
  return list(loaded_dct.keys())
292
313
 
293
314
 
315
+ def auto_record_function(
316
+ record_name: str,
317
+ method: bool = True,
318
+ group: Optional[str] = None,
319
+ index_on_thread_name: bool = True,
320
+ ) -> Callable:
321
+ """This is the @auto_record_function decorator. It works in a similar way to
322
+ the @record_function decorator, except automatically generates boilerplate
323
+ classes for the Record, Params, and Result classes which would otherwise be
324
+ needed. That makes it suitable for quickly adding record support to simple
325
+ functions with simple parameters."""
326
+ return functools.partial(
327
+ _record_function_inner,
328
+ record_name,
329
+ method,
330
+ False,
331
+ None,
332
+ group,
333
+ index_on_thread_name,
334
+ False,
335
+ )
336
+
337
+
294
338
  def record_function(
295
339
  record_type,
296
340
  method: bool = False,
297
341
  tuple_result: bool = False,
298
342
  id_field_name: Optional[str] = None,
299
343
  ) -> Callable:
300
- def record_function_inner(func_to_record):
301
- # To avoid runtime overhead and other unpleasantness, we only apply the
302
- # record/replay decorator if a relevant env var is set.
303
- if get_record_mode_from_env() is None:
304
- return func_to_record
305
-
306
- @functools.wraps(func_to_record)
307
- def record_replay_wrapper(*args, **kwargs) -> Any:
308
- recorder: Optional[Recorder] = None
309
- try:
310
- from dbt_common.context import get_invocation_context
344
+ """This is the @record_function decorator, which marks functions which will
345
+ have their function calls recorded during record mode, and mocked out with
346
+ previously recorded replay data during replay."""
347
+ return functools.partial(
348
+ _record_function_inner,
349
+ record_type,
350
+ method,
351
+ tuple_result,
352
+ id_field_name,
353
+ None,
354
+ False,
355
+ False,
356
+ )
357
+
358
+
359
+ def _get_arg_fields(
360
+ spec: inspect.FullArgSpec,
361
+ skip_first: bool = False,
362
+ ) -> List[Tuple[str, Optional[Type], dataclasses.Field]]:
363
+ arg_fields = []
364
+ defaults = len(spec.defaults) if spec.defaults else 0
365
+ for i, arg_name in enumerate(spec.args):
366
+ if skip_first and i == 0:
367
+ continue
368
+ annotation = spec.annotations.get(arg_name)
369
+ if annotation is None:
370
+ raise Exception("Recorded functions must have type annotations.")
371
+ field = _get_field(arg_name, annotation)
372
+ if i >= len(spec.args) - defaults:
373
+ field[2].default = (
374
+ spec.defaults[i - len(spec.args) + defaults] if spec.defaults else None
375
+ )
376
+ arg_fields.append(field)
377
+ return arg_fields
378
+
379
+
380
+ def _get_field(field_name: str, t: Type) -> Tuple[str, Optional[Type], dataclasses.Field]:
381
+ dc_field: dataclasses.Field = dataclasses.field()
382
+ strat = Recorder._auto_serialization_strategies.get(t)
383
+ if strat is not None:
384
+ dc_field.metadata = field_options(serialization_strategy=Recorder._auto_serialization_strategies[t]) # type: ignore
385
+
386
+ return field_name, t, dc_field
387
+
388
+
389
+ @dataclasses.dataclass
390
+ class AutoValues(DataClassJSONMixin):
391
+ def _to_dict(self):
392
+ return self.to_dict()
393
+
394
+ def _from_dict(self, data):
395
+ return self.from_dict(data)
396
+
397
+
398
+ def _record_function_inner(
399
+ record_type,
400
+ method,
401
+ tuple_result,
402
+ id_field_name,
403
+ group,
404
+ index_on_thread_id,
405
+ is_classmethod,
406
+ func_to_record,
407
+ ):
408
+ if isinstance(record_type, str):
409
+ return_type = inspect.signature(func_to_record).return_annotation
410
+ fields = _get_arg_fields(inspect.getfullargspec(func_to_record), method)
411
+ if index_on_thread_id:
412
+ id_field_name = "thread_id"
413
+ fields.insert(0, _get_field("thread_id", str))
414
+ params_cls = dataclasses.make_dataclass(
415
+ f"{record_type}Params", fields, bases=(AutoValues,)
416
+ )
417
+ result_cls = (
418
+ None
419
+ if return_type is None or return_type == inspect._empty
420
+ else dataclasses.make_dataclass(
421
+ f"{record_type}Result",
422
+ [_get_field("return_val", return_type)],
423
+ bases=(AutoValues,),
424
+ )
425
+ )
426
+
427
+ record_type = type(
428
+ f"{record_type}Record",
429
+ (Record,),
430
+ {"params_cls": params_cls, "result_cls": result_cls, "group": group},
431
+ )
432
+
433
+ Recorder.register_record_type(record_type)
311
434
 
312
- recorder = get_invocation_context().recorder
313
- except LookupError:
314
- pass
435
+ @functools.wraps(func_to_record)
436
+ def record_replay_wrapper(*args, **kwargs) -> Any:
437
+ recorder: Optional[Recorder] = None
438
+ try:
439
+ from dbt_common.context import get_invocation_context
315
440
 
316
- if recorder is None:
317
- return func_to_record(*args, **kwargs)
441
+ recorder = get_invocation_context().recorder
442
+ except LookupError:
443
+ pass
318
444
 
319
- if recorder.recorded_types is not None and not (
320
- record_type.__name__ in recorder.recorded_types
321
- or record_type.group in recorder.recorded_types
322
- ):
323
- return func_to_record(*args, **kwargs)
445
+ call_args = args[1:] if is_classmethod else args
446
+
447
+ if recorder is None:
448
+ return func_to_record(*call_args, **kwargs)
449
+
450
+ if recorder.recorded_types is not None and not (
451
+ record_type.__name__ in recorder.recorded_types
452
+ or record_type.group in recorder.recorded_types
453
+ ):
454
+ return func_to_record(*call_args, **kwargs)
324
455
 
325
- # For methods, peel off the 'self' argument before calling the
326
- # params constructor.
327
- param_args = args[1:] if method else args
328
- if method and id_field_name is not None:
456
+ # For methods, peel off the 'self' argument before calling the
457
+ # params constructor.
458
+ param_args = args[1:] if method else args
459
+ if method and id_field_name is not None:
460
+ if index_on_thread_id:
461
+ from dbt_common.context import get_invocation_context
462
+
463
+ param_args = (get_invocation_context().name,) + param_args
464
+ else:
329
465
  param_args = (getattr(args[0], id_field_name),) + param_args
330
466
 
331
- params = record_type.params_cls(*param_args, **kwargs)
467
+ params = record_type.params_cls(*param_args, **kwargs)
332
468
 
333
- include = True
334
- if hasattr(params, "_include"):
335
- include = params._include()
469
+ include = True
470
+ if hasattr(params, "_include"):
471
+ include = params._include()
336
472
 
337
- if not include:
338
- return func_to_record(*args, **kwargs)
473
+ if not include:
474
+ return func_to_record(*call_args, **kwargs)
339
475
 
340
- if recorder.mode == RecorderMode.REPLAY:
341
- return recorder.expect_record(params)
476
+ if recorder.mode == RecorderMode.REPLAY:
477
+ return recorder.expect_record(params)
478
+ if RECORDED_BY_HIGHER_FUNCTION.get():
479
+ return func_to_record(*call_args, **kwargs)
342
480
 
343
- r = func_to_record(*args, **kwargs)
344
- result = (
345
- None
346
- if record_type.result_cls is None
347
- else record_type.result_cls(*r)
348
- if tuple_result
349
- else record_type.result_cls(r)
350
- )
351
- recorder.add_record(record_type(params=params, result=result))
352
- return r
481
+ RECORDED_BY_HIGHER_FUNCTION.set(True)
482
+ r = func_to_record(*call_args, **kwargs)
483
+ result = (
484
+ None
485
+ if record_type.result_cls is None
486
+ else record_type.result_cls(*r)
487
+ if tuple_result
488
+ else record_type.result_cls(r)
489
+ )
490
+ RECORDED_BY_HIGHER_FUNCTION.set(False)
491
+ recorder.add_record(record_type(params=params, result=result))
492
+ return r
493
+
494
+ setattr(
495
+ record_replay_wrapper,
496
+ "_record_metadata",
497
+ {
498
+ "record_type": record_type,
499
+ "method": method,
500
+ "tuple_result": tuple_result,
501
+ "id_field_name": id_field_name,
502
+ "group": group,
503
+ "index_on_thread_id": index_on_thread_id,
504
+ },
505
+ )
506
+
507
+ return record_replay_wrapper
508
+
509
+
510
+ def _is_classmethod(method):
511
+ b = inspect.ismethod(method) and isinstance(method.__self__, type)
512
+ return b
513
+
514
+
515
+ def supports_replay(cls):
516
+ """Class decorator which adds record/replay support for a class. In particular,
517
+ this decorator ensures that calls to overriden functions are still recorded."""
518
+
519
+ # When record/replay is inactive, do nothing.
520
+ if get_record_mode_from_env() is None:
521
+ return cls
522
+
523
+ # Replace the __init_subclass__ method of this class so that when it
524
+ # is subclassed, methods on the new subclass which override recorded
525
+ # functions are modified to be recorded as well.
526
+ original_init_subclass = cls.__init_subclass__
527
+
528
+ @classmethod
529
+ def wrapping_init_subclass(sub_cls):
530
+ for method_name in dir(cls):
531
+ method = getattr(cls, method_name)
532
+ metadata = getattr(method, "_record_metadata", None)
533
+ if method and getattr(method, "_record_metadata", None):
534
+ sub_method = getattr(sub_cls, method_name, None)
535
+ recorded_sub_method = _record_function_inner(
536
+ metadata["record_type"],
537
+ metadata["method"],
538
+ metadata["tuple_result"],
539
+ metadata["id_field_name"],
540
+ metadata["group"],
541
+ metadata["index_on_thread_id"],
542
+ _is_classmethod(method),
543
+ sub_method,
544
+ )
545
+
546
+ if _is_classmethod(method):
547
+ recorded_sub_method = classmethod(recorded_sub_method)
548
+
549
+ if sub_method is not None:
550
+ setattr(
551
+ sub_cls,
552
+ method_name,
553
+ recorded_sub_method,
554
+ )
555
+
556
+ original_init_subclass()
353
557
 
354
- return record_replay_wrapper
558
+ cls.__init_subclass__ = wrapping_init_subclass
355
559
 
356
- return record_function_inner
560
+ return cls
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: dbt-common
3
- Version: 1.13.0
3
+ Version: 1.15.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
@@ -8,7 +8,8 @@ Project-URL: Issues, https://github.com/dbt-labs/dbt-common/issues
8
8
  Project-URL: Changelog, https://github.com/dbt-labs/dbt-common/blob/main/CHANGELOG.md
9
9
  Author-email: dbt Labs <info@dbtlabs.com>
10
10
  Maintainer-email: dbt Labs <info@dbtlabs.com>
11
- License: Apache-2.0
11
+ License-Expression: Apache-2.0
12
+ License-File: LICENSE
12
13
  Classifier: Development Status :: 2 - Pre-Alpha
13
14
  Classifier: License :: OSI Approved :: Apache Software License
14
15
  Classifier: Operating System :: MacOS :: MacOS X
@@ -65,9 +66,7 @@ The shared common utilities for dbt-core and adapter implementations use
65
66
 
66
67
  ### Releasing dbt-common
67
68
  To release a new version of dbt-common to pypi, you'll need to:
68
- 1. Bump the `version` in [dbt_common_/__about__.py](https://github.com/dbt-labs/dbt-common/blob/main/dbt_common/__about__.py)
69
- 2. Run the [release workflow](https://github.com/dbt-labs/dbt-common/actions/workflows/release.yml) to test pypi and confirm a successful test release in: https://test.pypi.org/project/dbt-common/
70
- 3. Run the [release workflow](https://github.com/dbt-labs/dbt-common/actions/workflows/release.yml) to prod pypi and confirm a successful release in: https://pypi.org/project/dbt-common/
69
+ 1. Run the [release workflow](https://github.com/dbt-labs/dbt-common/actions/workflows/release.yml) to bump the version, generate changelogs and release to pypi
71
70
  4. Bump the version of `dbt-common` in `dbt-core` and `dbt-adapters` if you're releasing a new major version or a pre-release:
72
71
  * `dbt-core`: [setup.py](https://github.com/dbt-labs/dbt-core/blob/main/core/setup.py)
73
72
  * `dbt-adapters`: [pyproject.toml](https://github.com/dbt-labs/dbt-adapters/blob/main/pyproject.toml)
@@ -1,20 +1,20 @@
1
- dbt_common/__about__.py,sha256=M85oP8JJdZ4yZHcp9qfGYLKUYvnN3kTyQosVcYPCPow,19
1
+ dbt_common/__about__.py,sha256=ifS0ZWsb82NyM4xyQWKPKGKE5bG3EXlYHT1H7h1HxOY,19
2
2
  dbt_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  dbt_common/behavior_flags.py,sha256=hQzxCqQSweJbRp_xoQqNnlUF77PBuOdCdLOSdcBlkxk,4885
4
4
  dbt_common/constants.py,sha256=-Y5DIL1SDPQWtlCNizXRYxFgbx1D7LaLs1ysamvGMRk,278
5
- dbt_common/context.py,sha256=rk4EYBU4SpDXRhqbSvDTJwojilxPSoaiEdOxkXow_BU,2549
5
+ dbt_common/context.py,sha256=tVeXtsptvuw7d8CvdlYSBFcKLyLZ852iQNwcxNmUzYY,2577
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
- dbt_common/invocation.py,sha256=2ZchIr4Wq7NwtAjjB-mxHP9ITD6-r45jEq4Zooso0gc,210
8
+ dbt_common/invocation.py,sha256=xw0NBIE-6LHd135cx4non-MkGGsia0mYN0lMkmNEucE,435
9
9
  dbt_common/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- dbt_common/record.py,sha256=vItvHEF2yl_NbvuaIXYHo-q6v0LUpCxR2twcTMVCeQ0,12698
10
+ dbt_common/record.py,sha256=ExF8ccUpDv2kButAMnTR8zSyD9agU7nhXTKFDx7Iw7Y,19352
11
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=5I_VEWkkW_54uK09ErP_8ey7wj-OOXvt1OHqr73HLOk,14879
16
16
  dbt_common/clients/agate_helper.py,sha256=anKKgKV5PSnFRuFeBwRMdHK3JCaQoUqB2ZXHD0su0Wo,9123
17
- dbt_common/clients/jinja.py,sha256=GzpW1BN3W-__YFQ2amyx85Z_qwOOZXEwjmoTIr70HlA,19787
17
+ dbt_common/clients/jinja.py,sha256=VpCYe_0seh6K4667-4TmQ2PP-T48UIcvDRT3miRyn4o,22488
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
@@ -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.13.0.dist-info/METADATA,sha256=LMXT2OegSelr0vg0b1mMBO79jYzoAb5DpWx7DI16xfg,5211
61
- dbt_common-1.13.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
62
- dbt_common-1.13.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
63
- dbt_common-1.13.0.dist-info/RECORD,,
60
+ dbt_common-1.15.0.dist-info/METADATA,sha256=gcyH_sfjldI5SpDsEGDig-tUAVMVANrqNEmN0jun2c0,4895
61
+ dbt_common-1.15.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
+ dbt_common-1.15.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
63
+ dbt_common-1.15.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any