dbt-common 1.16.0__py3-none-any.whl → 1.18__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.16.0"
1
+ version = "1.18"
@@ -1,19 +1,66 @@
1
1
  import os
2
2
  import traceback
3
- from typing import List, Optional, Protocol, Tuple
3
+ from typing import Any, List, Optional, Protocol, Tuple
4
4
 
5
5
  from dbt_common.events.base_types import BaseEvent, EventLevel, msg_from_base_event, TCallback
6
6
  from dbt_common.events.logger import LoggerConfig, _Logger, _TextLogger, _JsonLogger, LineFormat
7
+ from dbt_common.exceptions.events import EventCompilationError
8
+ from dbt_common.helper_types import WarnErrorOptions
7
9
 
8
10
 
9
11
  class EventManager:
10
12
  def __init__(self) -> None:
11
13
  self.loggers: List[_Logger] = []
12
14
  self.callbacks: List[TCallback] = []
13
-
14
- def fire_event(self, e: BaseEvent, level: Optional[EventLevel] = None) -> None:
15
+ self._warn_error: Optional[bool] = None
16
+ self._warn_error_options: Optional[WarnErrorOptions] = None
17
+ self.require_warn_or_error_handling: bool = False
18
+
19
+ @property
20
+ def warn_error(self) -> bool:
21
+ if self._warn_error is None:
22
+ from dbt_common.events.functions import WARN_ERROR
23
+
24
+ return WARN_ERROR
25
+ return self._warn_error
26
+
27
+ @warn_error.setter
28
+ def warn_error(self, warn_error: bool) -> None:
29
+ self._warn_error = warn_error
30
+
31
+ @property
32
+ def warn_error_options(self) -> WarnErrorOptions:
33
+ if self._warn_error_options is None:
34
+ from dbt_common.events.functions import WARN_ERROR_OPTIONS
35
+
36
+ return WARN_ERROR_OPTIONS
37
+ return self._warn_error_options
38
+
39
+ @warn_error_options.setter
40
+ def warn_error_options(self, warn_error_options: WarnErrorOptions) -> None:
41
+ self._warn_error_options = warn_error_options
42
+
43
+ def fire_event(
44
+ self,
45
+ e: BaseEvent,
46
+ level: Optional[EventLevel] = None,
47
+ node: Any = None,
48
+ force_warn_or_error_handling: bool = False,
49
+ ) -> None:
15
50
  msg = msg_from_base_event(e, level=level)
16
51
 
52
+ if force_warn_or_error_handling or (
53
+ self.require_warn_or_error_handling and msg.info.level == "warn"
54
+ ):
55
+ event_name = type(e).__name__
56
+ if self.warn_error or self.warn_error_options.includes(event_name):
57
+ # This has the potential to create an infinite loop if the handling of the raised
58
+ # EventCompilationError fires an event as a warning instead of an error.
59
+ raise EventCompilationError(e.message(), node)
60
+ elif self.warn_error_options.silenced(event_name):
61
+ # Return early if the event is silenced
62
+ return
63
+
17
64
  if os.environ.get("DBT_TEST_BINARY_SERIALIZATION"):
18
65
  print(f"--- {msg.info.name}")
19
66
  try:
@@ -48,8 +95,17 @@ class EventManager:
48
95
  class IEventManager(Protocol):
49
96
  callbacks: List[TCallback]
50
97
  loggers: List[_Logger]
51
-
52
- def fire_event(self, e: BaseEvent, level: Optional[EventLevel] = None) -> None:
98
+ warn_error: bool
99
+ warn_error_options: WarnErrorOptions
100
+ require_warn_or_error_handling: bool
101
+
102
+ def fire_event(
103
+ self,
104
+ e: BaseEvent,
105
+ level: Optional[EventLevel] = None,
106
+ node: Any = None,
107
+ force_warn_or_error_handling: bool = False,
108
+ ) -> None:
53
109
  ...
54
110
 
55
111
  def add_logger(self, config: LoggerConfig) -> None:
@@ -65,8 +121,17 @@ class TestEventManager(IEventManager):
65
121
  def __init__(self) -> None:
66
122
  self.event_history: List[Tuple[BaseEvent, Optional[EventLevel]]] = []
67
123
  self.loggers = []
68
-
69
- def fire_event(self, e: BaseEvent, level: Optional[EventLevel] = None) -> None:
124
+ self.warn_error = False
125
+ self.warn_error_options = WarnErrorOptions(include=[], exclude=[])
126
+ self.require_warn_or_error_handling = False
127
+
128
+ def fire_event(
129
+ self,
130
+ e: BaseEvent,
131
+ level: Optional[EventLevel] = None,
132
+ node: Any = None,
133
+ force_warn_or_error_handling: bool = False,
134
+ ) -> None:
70
135
  self.event_history.append((e, level))
71
136
 
72
137
  def add_logger(self, config: LoggerConfig) -> None:
@@ -8,17 +8,14 @@ _EVENT_MANAGER: IEventManager = EventManager()
8
8
 
9
9
 
10
10
  def get_event_manager() -> IEventManager:
11
- global _EVENT_MANAGER
12
11
  return _EVENT_MANAGER
13
12
 
14
13
 
15
14
  def add_logger_to_manager(logger) -> None:
16
- global _EVENT_MANAGER
17
15
  _EVENT_MANAGER.add_logger(logger)
18
16
 
19
17
 
20
18
  def add_callback_to_manager(callback: TCallback) -> None:
21
- global _EVENT_MANAGER
22
19
  _EVENT_MANAGER.add_callback(callback)
23
20
 
24
21
 
@@ -1,9 +1,8 @@
1
1
  from pathlib import Path
2
2
 
3
3
  from dbt_common.events.event_manager_client import get_event_manager
4
- from dbt_common.exceptions import EventCompilationError
5
- from dbt_common.invocation import get_invocation_id
6
4
  from dbt_common.helper_types import WarnErrorOptions
5
+ from dbt_common.invocation import get_invocation_id
7
6
  from dbt_common.utils.encoding import ForgivingJSONEncoder
8
7
  from dbt_common.events.base_types import BaseEvent, EventLevel, EventMsg
9
8
  from dbt_common.events.logger import LoggerConfig, LineFormat
@@ -13,7 +12,7 @@ from functools import partial
13
12
  import json
14
13
  import os
15
14
  import sys
16
- from typing import Callable, Dict, Optional, TextIO, Union
15
+ from typing import Any, Callable, Dict, Optional, TextIO, Union
17
16
  from google.protobuf.json_format import MessageToDict
18
17
 
19
18
  LOG_VERSION = 3
@@ -114,12 +113,9 @@ def msg_to_dict(msg: EventMsg) -> dict:
114
113
  return msg_dict
115
114
 
116
115
 
116
+ # This function continues to exist to provide backwards compatibility
117
117
  def warn_or_error(event, node=None) -> None:
118
- event_name = type(event).__name__
119
- if WARN_ERROR or WARN_ERROR_OPTIONS.includes(event_name):
120
- raise EventCompilationError(event.message(), node)
121
- elif not WARN_ERROR_OPTIONS.silenced(event_name):
122
- fire_event(event)
118
+ fire_event(e=event, node=node, force_warn_or_error_handling=True)
123
119
 
124
120
 
125
121
  # an alternative to fire_event which only creates and logs the event value
@@ -135,8 +131,15 @@ def fire_event_if(
135
131
  # this is where all the side effects happen branched by event type
136
132
  # (i.e. - mutating the event history, printing to stdout, logging
137
133
  # to files, etc.)
138
- def fire_event(e: BaseEvent, level: Optional[EventLevel] = None) -> None:
139
- get_event_manager().fire_event(e, level=level)
134
+ def fire_event(
135
+ e: BaseEvent,
136
+ level: Optional[EventLevel] = None,
137
+ node: Any = None,
138
+ force_warn_or_error_handling: bool = False,
139
+ ) -> None:
140
+ get_event_manager().fire_event(
141
+ e, level=level, node=node, force_warn_or_error_handling=force_warn_or_error_handling
142
+ )
140
143
 
141
144
 
142
145
  def get_metadata_vars() -> Dict[str, str]:
@@ -1,4 +1,4 @@
1
- from datetime import datetime
1
+ from datetime import datetime, timezone
2
2
 
3
3
 
4
4
  # This converts a datetime to a json format datetime string which
@@ -9,6 +9,6 @@ def datetime_to_json_string(dt: datetime) -> str:
9
9
 
10
10
  # preformatted time stamp
11
11
  def get_json_string_utcnow() -> str:
12
- ts = datetime.utcnow()
12
+ ts = datetime.now(timezone.utc).replace(tzinfo=None)
13
13
  ts_rfc3339 = datetime_to_json_string(ts)
14
14
  return ts_rfc3339
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
  import threading
4
4
  from dataclasses import dataclass
5
- from datetime import datetime
5
+ from datetime import datetime, timezone
6
6
  from enum import Enum
7
7
  from logging.handlers import RotatingFileHandler
8
8
  from typing import Optional, TextIO, Any, Callable
@@ -156,7 +156,7 @@ class _TextLogger(_Logger):
156
156
  if _is_print_event(msg):
157
157
  # PrintEvent is a special case, we don't want to add a timestamp
158
158
  return scrubbed_msg
159
- ts: str = datetime.utcnow().strftime("%H:%M:%S")
159
+ ts: str = datetime.now(timezone.utc).replace(tzinfo=None).strftime("%H:%M:%S")
160
160
  return f"{self._get_color_tag()}{ts} {scrubbed_msg}"
161
161
 
162
162
  def create_debug_line(self, msg: EventMsg) -> str:
@@ -144,3 +144,13 @@ message PrintEventMsg {
144
144
  EventInfo info = 1;
145
145
  PrintEvent data = 2;
146
146
  }
147
+
148
+ // Z053
149
+ message RecordReplayIssue {
150
+ string msg = 1;
151
+ }
152
+
153
+ message RecordReplayIssueMsg {
154
+ EventInfo info = 1;
155
+ RecordReplayIssue data = 2;
156
+ }
@@ -118,7 +118,7 @@ class SystemReportReturnCode(DebugLevel):
118
118
 
119
119
 
120
120
  # We use events to create console output, but also think of them as a sequence of important and
121
- # meaningful occurrences to be used for debugging and monitoring. The Formatting event helps eases
121
+ # meaningful occurrences to be used for debugging and monitoring. The Formatting event eases
122
122
  # the tension between these two goals by allowing empty lines, heading separators, and other
123
123
  # formatting to be written to the console, while they can be ignored for other purposes. For
124
124
  # general information that isn't simple formatting, the Note event should be used instead.
@@ -156,3 +156,13 @@ class PrintEvent(InfoLevel):
156
156
 
157
157
  def message(self) -> str:
158
158
  return self.msg
159
+
160
+
161
+ class RecordReplayIssue(InfoLevel):
162
+ """General event for reporting record/replay issues at runtime."""
163
+
164
+ def code(self) -> str:
165
+ return "Z053"
166
+
167
+ def message(self) -> str:
168
+ return self.msg
@@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default()
25
25
  from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
26
26
 
27
27
 
28
- 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')
28
+ 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.PrintEvent\" \n\x11RecordReplayIssue\x12\x0b\n\x03msg\x18\x01 \x01(\t\"j\n\x14RecordReplayIssueMsg\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.RecordReplayIssueb\x06proto3')
29
29
 
30
30
  _globals = globals()
31
31
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -84,4 +84,8 @@ if not _descriptor._USE_C_DESCRIPTORS:
84
84
  _globals['_PRINTEVENT']._serialized_end=1909
85
85
  _globals['_PRINTEVENTMSG']._serialized_start=1911
86
86
  _globals['_PRINTEVENTMSG']._serialized_end=2003
87
+ _globals['_RECORDREPLAYISSUE']._serialized_start=2005
88
+ _globals['_RECORDREPLAYISSUE']._serialized_end=2037
89
+ _globals['_RECORDREPLAYISSUEMSG']._serialized_start=2039
90
+ _globals['_RECORDREPLAYISSUEMSG']._serialized_end=2145
87
91
  # @@protoc_insertion_point(module_scope)
@@ -12,6 +12,7 @@ from dbt_common.dataclass_schema import (
12
12
  ValidationError,
13
13
  StrEnum,
14
14
  )
15
+ from dbt_common.events.base_types import BaseEvent
15
16
 
16
17
  Port = NewType("Port", int)
17
18
 
@@ -64,6 +65,22 @@ class IncludeExclude(dbtClassMixin):
64
65
 
65
66
 
66
67
  class WarnErrorOptions(IncludeExclude):
68
+ """
69
+ This class is used to configure the behavior of the warn_error feature (now part of fire_event).
70
+
71
+ include: "all", "*", or a list of event names.
72
+ exclude: a list of event names.
73
+ silence: a list of event names.
74
+ valid_error_names: a set of event names that can be named in include, exclude, and silence.
75
+
76
+ In a hierarchy of configuration, the following rules apply:
77
+ 1. named > Deprecations > "all"/"*"
78
+ 2. silence > exclude > include
79
+ 3. (1) > (2)
80
+ """
81
+
82
+ DEPRECATIONS = "Deprecations"
83
+
67
84
  def __init__(
68
85
  self,
69
86
  include: Union[str, List[str]],
@@ -73,18 +90,137 @@ class WarnErrorOptions(IncludeExclude):
73
90
  ):
74
91
  self.silence = silence or []
75
92
  self._valid_error_names: Set[str] = valid_error_names or set()
93
+ self._valid_error_names.add(self.DEPRECATIONS)
76
94
  super().__init__(include=include, exclude=(exclude or []))
77
95
 
78
96
  def __post_init__(self):
79
- super().__post_init__()
80
- self._validate_items(self.silence)
97
+ if isinstance(self.include, str) and self.include not in self.INCLUDE_ALL:
98
+ raise ValidationError(
99
+ f"include must be one of {self.INCLUDE_ALL} or a list of strings"
100
+ )
81
101
 
82
- def includes(self, item_name: str) -> bool:
83
- return super().includes(item_name) and not self.silenced(item_name)
102
+ # To specify exclude, either `include` must be "all" or "deprecations" must be
103
+ # in `include` or `silence`.
104
+ if self.exclude and not (
105
+ self.include in self.INCLUDE_ALL
106
+ or self.DEPRECATIONS in self.include
107
+ or self.DEPRECATIONS in self.silence
108
+ ):
109
+ raise ValidationError(
110
+ f"exclude can only be specified if include is one of {self.INCLUDE_ALL} or "
111
+ f"{self.DEPRECATIONS} is in include or silence."
112
+ )
113
+
114
+ if isinstance(self.include, list):
115
+ self._validate_items(self.include)
84
116
 
85
- def silenced(self, item_name: str) -> bool:
117
+ if isinstance(self.exclude, list):
118
+ self._validate_items(self.exclude)
119
+
120
+ if isinstance(self.silence, list):
121
+ self._validate_items(self.silence)
122
+
123
+ def _includes_all(self) -> bool:
124
+ """Is `*` or `all` set as include?"""
125
+ return self.include in self.INCLUDE_ALL
126
+
127
+ def _named_inclusion(self, item_name: str) -> bool:
128
+ """Is the item_name named in the include list?"""
129
+ return item_name in self.include
130
+
131
+ def _named_exclusion(self, item_name: str) -> bool:
132
+ """Is the item_name named in the exclude list?"""
133
+ return item_name in self.exclude
134
+
135
+ def _named_silence(self, item_name: str) -> bool:
136
+ """Is the item_name named in the silence list?"""
86
137
  return item_name in self.silence
87
138
 
139
+ def _include_as_deprecation(self, event: Optional[BaseEvent]) -> bool:
140
+ """Is event included as a deprecation?"""
141
+ return (
142
+ event is not None
143
+ and event.code().startswith("D")
144
+ and self.DEPRECATIONS in self.include
145
+ )
146
+
147
+ def _exclude_as_deprecation(self, event: Optional[BaseEvent]) -> bool:
148
+ """Is event excluded as a deprecation?"""
149
+ return (
150
+ event is not None
151
+ and event.code().startswith("D")
152
+ and self.DEPRECATIONS in self.exclude
153
+ )
154
+
155
+ def _silence_as_deprecation(self, event: Optional[BaseEvent]) -> bool:
156
+ """Is event silenced as a deprecation?"""
157
+ return (
158
+ event is not None
159
+ and event.code().startswith("D")
160
+ and self.DEPRECATIONS in self.silence
161
+ )
162
+
163
+ def includes(self, item_name: Union[str, BaseEvent]) -> bool:
164
+ """Is the event included?
165
+
166
+ An event included if any of the following are true:
167
+ - The event is named in `include` and not named in `exclude` or `silence`
168
+ - "*" or "all" is specified for `include`, and the event is not named in `exclude` or `silence`
169
+ - The event is a deprecation, "deprecations" is in `include`, and the event is not named in `exclude` or `silence`
170
+ nor is "deprecations" in `exclude` or `silence`
171
+ """
172
+ # Setup based on item_name type
173
+ if isinstance(item_name, str):
174
+ event_name = item_name
175
+ event = None
176
+ else:
177
+ event_name = type(item_name).__name__
178
+ event = item_name
179
+
180
+ # Pre-compute checks that will be used multiple times
181
+ named_elsewhere = self._named_exclusion(event_name) or self._named_silence(event_name)
182
+ deprecation_elsewhere = self._exclude_as_deprecation(
183
+ event
184
+ ) or self._silence_as_deprecation(event)
185
+
186
+ # Calculate result
187
+ if self._named_inclusion(event_name) and not named_elsewhere:
188
+ return True
189
+ elif self._include_as_deprecation(event) and not (
190
+ named_elsewhere or deprecation_elsewhere
191
+ ):
192
+ return True
193
+ elif self._includes_all() and not (named_elsewhere or deprecation_elsewhere):
194
+ return True
195
+ else:
196
+ return False
197
+
198
+ def silenced(self, item_name: Union[str, BaseEvent]) -> bool:
199
+ """Is the event silenced?
200
+
201
+ An event silenced if any of the following are true:
202
+ - The event is named in `silence`
203
+ - "Deprecations" is in `silence` and the event is not named in `include` or `exclude`
204
+ """
205
+ # Setup based on item_name type
206
+ if isinstance(item_name, str):
207
+ event_name = item_name
208
+ event = None
209
+ else:
210
+ event_name = type(item_name).__name__
211
+ event = item_name
212
+
213
+ # Pre-compute checks that will be used multiple times
214
+ named_elsewhere = self._named_inclusion(event_name) or self._named_exclusion(event_name)
215
+
216
+ # Calculate result
217
+ if self._named_silence(event_name):
218
+ return True
219
+ elif self._silence_as_deprecation(event) and not named_elsewhere:
220
+ return True
221
+ else:
222
+ return False
223
+
88
224
  def _validate_items(self, items: List[str]):
89
225
  for item in items:
90
226
  if item not in self._valid_error_names:
dbt_common/record.py CHANGED
@@ -151,6 +151,7 @@ class Recorder:
151
151
  types: Optional[List],
152
152
  current_recording_path: str = "recording.json",
153
153
  previous_recording_path: Optional[str] = None,
154
+ in_memory: bool = False,
154
155
  ) -> None:
155
156
  self.mode = mode
156
157
  self.recorded_types = types
@@ -176,6 +177,15 @@ class Recorder:
176
177
  self._counter = 0
177
178
  self._counter_lock = Lock()
178
179
 
180
+ self._record_added = False
181
+ self._recording_file: Optional[TextIO] = None
182
+ if mode == RecorderMode.RECORD and not in_memory:
183
+ self._recording_file = open(current_recording_path, "w")
184
+ self._recording_file.write("[")
185
+
186
+ def __del__(self):
187
+ self.clean_up_stream()
188
+
179
189
  @classmethod
180
190
  def register_record_type(cls, rec_type) -> Any:
181
191
  cls._record_cls_by_name[rec_type.__name__] = rec_type
@@ -184,14 +194,26 @@ class Recorder:
184
194
 
185
195
  def add_record(self, record: Record) -> None:
186
196
  rec_cls_name = record.__class__.__name__ # type: ignore
187
- if rec_cls_name not in self._records_by_type:
188
- self._records_by_type[rec_cls_name] = []
189
197
 
190
198
  with self._counter_lock:
191
199
  record.seq = self._counter
192
200
  self._counter += 1
193
201
 
194
- self._records_by_type[rec_cls_name].append(record)
202
+ if self._recording_file is not None:
203
+ if self._record_added:
204
+ self._recording_file.write(",")
205
+ try:
206
+ dct = Recorder._get_tagged_dict(record, rec_cls_name)
207
+ json.dump(dct, self._recording_file)
208
+ self._record_added = True
209
+ except Exception:
210
+ json.dump(
211
+ {"type": "RecordingError", "record_type": rec_cls_name}, self._recording_file
212
+ )
213
+ else:
214
+ if rec_cls_name not in self._records_by_type:
215
+ self._records_by_type[rec_cls_name] = []
216
+ self._records_by_type[rec_cls_name].append(record)
195
217
 
196
218
  def pop_matching_record(self, params: Any) -> Optional[Record]:
197
219
  rec_type_name = self._record_name_by_params_name.get(type(params).__name__)
@@ -217,19 +239,30 @@ class Recorder:
217
239
  json.dump(d, out_stream)
218
240
 
219
241
  def write(self) -> None:
220
- with open(self.current_recording_path, "w") as file:
221
- self.write_json(file)
242
+ if self._recording_file is not None:
243
+ self.clean_up_stream()
244
+ else:
245
+ with open(self.current_recording_path, "w") as file:
246
+ self.write_json(file)
247
+
248
+ def clean_up_stream(self) -> None:
249
+ if self._recording_file is not None:
250
+ self._recording_file.write("]")
251
+ self._recording_file.close()
252
+ self._recording_file = None
253
+
254
+ @staticmethod
255
+ def _get_tagged_dict(record: Record, record_type: str) -> Dict:
256
+ d = record.to_dict()
257
+ d["type"] = record_type
258
+ return d
222
259
 
223
260
  def _to_list(self) -> List[Dict]:
224
- def get_tagged_dict(record: Record, record_type: str) -> Dict:
225
- d = record.to_dict()
226
- d["type"] = record_type
227
- return d
228
-
229
261
  record_list: List[Dict] = []
230
262
  for record_type in self._records_by_type:
231
263
  record_list.extend(
232
- get_tagged_dict(r, record_type) for r in self._records_by_type[record_type]
264
+ Recorder._get_tagged_dict(r, record_type)
265
+ for r in self._records_by_type[record_type]
233
266
  )
234
267
 
235
268
  record_list.sort(key=lambda r: r["seq"])
@@ -352,7 +385,6 @@ def auto_record_function(
352
385
  None,
353
386
  group,
354
387
  index_on_thread_name,
355
- False,
356
388
  )
357
389
 
358
390
 
@@ -374,7 +406,6 @@ def record_function(
374
406
  id_field_name,
375
407
  None,
376
408
  index_on_thread_id,
377
- False,
378
409
  )
379
410
 
380
411
 
@@ -424,9 +455,15 @@ def _record_function_inner(
424
455
  id_field_name,
425
456
  group,
426
457
  index_on_thread_id,
427
- is_classmethod,
428
458
  func_to_record,
429
459
  ):
460
+ recorded_types = get_record_types_from_env()
461
+ if recorded_types is not None and not (
462
+ getattr(record_type, "__name__", record_type) in recorded_types
463
+ or getattr(record_type, "group", group) in recorded_types
464
+ ):
465
+ return func_to_record
466
+
430
467
  if isinstance(record_type, str):
431
468
  return_type = inspect.signature(func_to_record).return_annotation
432
469
  fields = _get_arg_fields(inspect.getfullargspec(func_to_record), method)
@@ -464,7 +501,7 @@ def _record_function_inner(
464
501
  except LookupError:
465
502
  pass
466
503
 
467
- call_args = args[1:] if is_classmethod else args
504
+ call_args = args
468
505
 
469
506
  if recorder is None:
470
507
  return func_to_record(*call_args, **kwargs)
@@ -561,21 +598,25 @@ def supports_replay(cls):
561
598
  metadata = getattr(method, "_record_metadata", None)
562
599
  if method and getattr(method, "_record_metadata", None):
563
600
  sub_method = getattr(sub_cls, method_name, None)
564
- recorded_sub_method = _record_function_inner(
565
- metadata["record_type"],
566
- metadata["method"],
567
- metadata["tuple_result"],
568
- metadata["id_field_name"],
569
- metadata["group"],
570
- metadata["index_on_thread_id"],
571
- _is_classmethod(method),
572
- sub_method,
573
- )
601
+ sub_method_metadata = getattr(sub_method, "_record_metadata", None)
602
+
603
+ if not sub_method_metadata:
604
+ recorded_sub_method = _record_function_inner(
605
+ metadata["record_type"],
606
+ metadata["method"],
607
+ metadata["tuple_result"],
608
+ metadata["id_field_name"],
609
+ metadata["group"],
610
+ metadata["index_on_thread_id"],
611
+ sub_method.__func__
612
+ if _is_classmethod(method)
613
+ else sub_method, # unwrap if classmethod
614
+ )
574
615
 
575
- if _is_classmethod(method):
576
- recorded_sub_method = classmethod(recorded_sub_method)
616
+ if _is_classmethod(method):
617
+ # rewrap if submethod
618
+ recorded_sub_method = classmethod(recorded_sub_method)
577
619
 
578
- if sub_method is not None:
579
620
  setattr(
580
621
  sub_cls,
581
622
  method_name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbt-common
3
- Version: 1.16.0
3
+ Version: 1.18
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
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.9
20
20
  Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
23
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
24
25
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
26
  Requires-Python: >=3.9
@@ -1,13 +1,13 @@
1
- dbt_common/__about__.py,sha256=6EsnLejJWxuMqRt_s2lK0ter3lZGaIm5OK1MPeLZC5M,19
1
+ dbt_common/__about__.py,sha256=ERrGU780ALGuoRiYc9GL9A5N222lJo4nym34aldvEjY,17
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
5
  dbt_common/context.py,sha256=-ErtKG4xfOh1-Y569fwu6u2O381nRan18HhATrYDoZE,2950
6
6
  dbt_common/dataclass_schema.py,sha256=u2S0dxwxIghv8RMqC91HlWZJVxmsC_844yZQaGyOwdY,5563
7
- dbt_common/helper_types.py,sha256=FWJGPmp7Qp2iToHyI4uvhkBbu_d1tl2_oF-obi98_N4,3917
7
+ dbt_common/helper_types.py,sha256=v6ak-05SodNDxJM7BGXyCT4yPM-OUhcvznjemXRYT-g,9117
8
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=B5Ybpf-yzPRPyhxp4JNZyDAOGoSd2dr6VjLykH7VKN4,20247
10
+ dbt_common/record.py,sha256=QLrKppBHYuV7ZTckOMi4Gnz7jLQJcOa5kp4PwK0JmwY,21889
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
@@ -30,16 +30,16 @@ dbt_common/events/__init__.py,sha256=av08vfpxo0ek7PqZNtMxY8FODJ3xwph4ehRxgInx4LA
30
30
  dbt_common/events/base_types.py,sha256=bdDMbawAV0FkxmvuxgsTev82vxTuyu6rJiSkvEQPsO8,5525
31
31
  dbt_common/events/contextvars.py,sha256=EIs1P6NrJzx_IAV17x5cVqOAS4Lqbu6oc0etHtWCJOo,3097
32
32
  dbt_common/events/event_handler.py,sha256=jfi0PyqIOGnXCG9HEa0VIVULqNvXs1RYmAg0b50ChQs,1385
33
- dbt_common/events/event_manager.py,sha256=IIUwSyt_RcBbUI_iE5mnpmZt2uW7lG49RXOWz2VlUv0,2300
34
- dbt_common/events/event_manager_client.py,sha256=VKlIYJPcexmDKnidkyrs8BIuNZ1_CwDFGz-gBM2SAvo,1193
33
+ dbt_common/events/event_manager.py,sha256=0CDHNh_qTjdj4blpJiJ7gq6YkP1NzjE0iIct4v_MdoA,4588
34
+ dbt_common/events/event_manager_client.py,sha256=5IVdIuRVi5xCNQGiZqXdywlED2kLj1W_Pj0lh2HiDiI,1115
35
35
  dbt_common/events/format.py,sha256=x1RWDZ8G7ZMHmxdld6Q4VXca4kvnhiQOIaQXkC6Uo0Q,1609
36
- dbt_common/events/functions.py,sha256=_7CLApCKb9KhurOgfVRpW-yGKGE_yjYUguiAaLxVwnw,4752
37
- dbt_common/events/helpers.py,sha256=CfsWwNDjsLJkPIgOtAfuLEnZ3rGUKeYsH8aDtCW12OA,410
36
+ dbt_common/events/functions.py,sha256=R3DuyNy2TqOwNyMACVOYtiG1NjZ9FQa6FWKkzMeMCBY,4767
37
+ dbt_common/events/helpers.py,sha256=29FF0ZR24Zdvk23-bzIZKu8Pb4wFn-B1fFx28lLIpNw,450
38
38
  dbt_common/events/interfaces.py,sha256=hEDeDoB0FW2RYHVZBG7gebEt_mUVBzkn1yPubpaxs-s,147
39
- dbt_common/events/logger.py,sha256=iBxMhFhAo8wL4NA4Z31pf644I0tsCOWIrt-k4d7EzaY,6760
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=oQauUKUU_cBz3SskNvAYeWcp_OcA8dZPOgEYNeifThQ,7408
39
+ dbt_common/events/logger.py,sha256=siCM47WnjiA5wWoYzHDqe9mIr-b-XYNctP8r3o1zvSc,6800
40
+ dbt_common/events/types.proto,sha256=ohV5f2KfhaM3NRWHFJBNJQtqP3Hie65smA0imlWNkHg,2425
41
+ dbt_common/events/types.py,sha256=Xg2QBLTWSM_p2BP_Key_JcygmR1LyNYzQhKVYvZ9Zmc,4683
42
+ dbt_common/events/types_pb2.py,sha256=fgIoTp7-0hy9FMM0tiJmzENupZH_Vl28Xz2IRvg2KGg,7862
43
43
  dbt_common/exceptions/__init__.py,sha256=X_Uw7BxOzXev_9JMYfs5Cm-_i_Qf2PJim8_-dDJI7Y8,361
44
44
  dbt_common/exceptions/base.py,sha256=23ijq-AtQgUSvZ9JbrCIZ87Pbyn8iEYezX95IXAZ4FY,7783
45
45
  dbt_common/exceptions/cache.py,sha256=0z4fBcdNZMAR41YbPRo2GN0__xAMaYs8Uc-t3hjmVio,2532
@@ -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.16.0.dist-info/METADATA,sha256=k7yyMmWSYXvX-1bT5gzMUphCMcqntoMMloji1mnszvg,4895
61
- dbt_common-1.16.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
- dbt_common-1.16.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
63
- dbt_common-1.16.0.dist-info/RECORD,,
60
+ dbt_common-1.18.dist-info/METADATA,sha256=gd7IHI2wW8J1_OUnmv8GtW2rybVmr9_FiOKIrAZ8QHM,4944
61
+ dbt_common-1.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
+ dbt_common-1.18.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
63
+ dbt_common-1.18.dist-info/RECORD,,