dbt-common 1.0.4__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {dbt_common-1.0.4 → dbt_common-1.1.0}/PKG-INFO +2 -1
  2. dbt_common-1.1.0/dbt_common/__about__.py +1 -0
  3. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/base_types.py +5 -5
  4. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/event_manager.py +10 -4
  5. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/event_manager_client.py +6 -0
  6. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/logger.py +19 -2
  7. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/types.proto +10 -0
  8. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/types.py +11 -0
  9. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/types_pb2.py +56 -51
  10. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/record.py +2 -2
  11. dbt_common-1.1.0/docs/README.md +5 -0
  12. dbt_common-1.1.0/docs/guides/record_replay.md +41 -0
  13. {dbt_common-1.0.4 → dbt_common-1.1.0}/pyproject.toml +1 -0
  14. dbt_common-1.0.4/dbt_common/__about__.py +0 -1
  15. {dbt_common-1.0.4 → dbt_common-1.1.0}/.gitignore +0 -0
  16. {dbt_common-1.0.4 → dbt_common-1.1.0}/CHANGELOG.md +0 -0
  17. {dbt_common-1.0.4 → dbt_common-1.1.0}/LICENSE +0 -0
  18. {dbt_common-1.0.4 → dbt_common-1.1.0}/README.md +0 -0
  19. {dbt_common-1.0.4 → dbt_common-1.1.0}/codecov.yml +0 -0
  20. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/__init__.py +0 -0
  21. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/clients/__init__.py +0 -0
  22. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/clients/_jinja_blocks.py +0 -0
  23. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/clients/agate_helper.py +0 -0
  24. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/clients/jinja.py +0 -0
  25. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/clients/system.py +0 -0
  26. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/constants.py +0 -0
  27. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/context.py +0 -0
  28. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/__init__.py +0 -0
  29. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/config/__init__.py +0 -0
  30. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/config/base.py +0 -0
  31. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/config/materialization.py +0 -0
  32. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/config/metadata.py +0 -0
  33. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/config/properties.py +0 -0
  34. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/constraints.py +0 -0
  35. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/contracts/util.py +0 -0
  36. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/dataclass_schema.py +0 -0
  37. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/README.md +0 -0
  38. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/__init__.py +0 -0
  39. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/contextvars.py +0 -0
  40. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/event_handler.py +0 -0
  41. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/format.py +0 -0
  42. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/functions.py +0 -0
  43. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/helpers.py +0 -0
  44. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/events/interfaces.py +0 -0
  45. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/__init__.py +0 -0
  46. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/base.py +0 -0
  47. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/cache.py +0 -0
  48. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/connection.py +0 -0
  49. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/contracts.py +0 -0
  50. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/events.py +0 -0
  51. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/jinja.py +0 -0
  52. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/macros.py +0 -0
  53. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/exceptions/system.py +0 -0
  54. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/helper_types.py +0 -0
  55. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/invocation.py +0 -0
  56. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/py.typed +0 -0
  57. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/semver.py +0 -0
  58. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/tests.py +0 -0
  59. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/ui.py +0 -0
  60. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/__init__.py +0 -0
  61. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/casting.py +0 -0
  62. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/connection.py +0 -0
  63. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/dict.py +0 -0
  64. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/encoding.py +0 -0
  65. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/executor.py +0 -0
  66. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/formatting.py +0 -0
  67. {dbt_common-1.0.4 → dbt_common-1.1.0}/dbt_common/utils/jinja.py +0 -0
  68. {dbt_common-1.0.4 → dbt_common-1.1.0}/docs/arch/adr-0001-build-tooling.md +0 -0
  69. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/agate/__init__.pyi +0 -0
  70. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/agate/data_types.pyi +0 -0
  71. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/colorama/__init__.pyi +0 -0
  72. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/isodate/__init__.pyi +0 -0
  73. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/__init__.pyi +0 -0
  74. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/config.pyi +0 -0
  75. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/__init__.pyi +0 -0
  76. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/const.pyi +0 -0
  77. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/helpers.pyi +0 -0
  78. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/__init__.pyi +0 -0
  79. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/code/__init__.pyi +0 -0
  80. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/code/builder.pyi +0 -0
  81. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/code/lines.pyi +0 -0
  82. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/helpers.pyi +0 -0
  83. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/mixin.pyi +0 -0
  84. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/types/__init__.pyi +0 -0
  85. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/types/common.pyi +0 -0
  86. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/types/pack.pyi +0 -0
  87. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/core/meta/types/unpack.pyi +0 -0
  88. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/dialect.pyi +0 -0
  89. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/exceptions.pyi +0 -0
  90. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/helper.pyi +0 -0
  91. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/jsonschema/__init__.pyi +0 -0
  92. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/jsonschema/annotations.pyi +0 -0
  93. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/jsonschema/builder.pyi +0 -0
  94. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/jsonschema/dialects.pyi +0 -0
  95. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/jsonschema/models.pyi +0 -0
  96. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/jsonschema/schema.pyi +0 -0
  97. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/__init__.pyi +0 -0
  98. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/dict.pyi +0 -0
  99. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/json.pyi +0 -0
  100. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/msgpack.pyi +0 -0
  101. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/orjson.pyi +0 -0
  102. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/toml.pyi +0 -0
  103. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/mixins/yaml.pyi +0 -0
  104. {dbt_common-1.0.4 → dbt_common-1.1.0}/third-party-stubs/mashumaro/types.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dbt-common
3
- Version: 1.0.4
3
+ Version: 1.1.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
@@ -55,6 +55,7 @@ Requires-Dist: types-requests; extra == 'lint'
55
55
  Provides-Extra: test
56
56
  Requires-Dist: hypothesis<7.0,>=6.87; extra == 'test'
57
57
  Requires-Dist: pytest-cov<5.0,>=4.1; extra == 'test'
58
+ Requires-Dist: pytest-mock; extra == 'test'
58
59
  Requires-Dist: pytest-xdist<4.0,>=3.2; extra == 'test'
59
60
  Requires-Dist: pytest<8.0,>=7.3; extra == 'test'
60
61
  Description-Content-Type: text/markdown
@@ -0,0 +1 @@
1
+ version = "1.1.0"
@@ -6,14 +6,11 @@ import sys
6
6
  from google.protobuf.json_format import ParseDict, MessageToDict, MessageToJson
7
7
  from google.protobuf.message import Message
8
8
  from dbt_common.events.helpers import get_json_string_utcnow
9
- from typing import Optional
9
+ from typing import Callable, Optional
10
10
 
11
11
  from dbt_common.invocation import get_invocation_id
12
12
 
13
- if sys.version_info >= (3, 8):
14
- from typing import Protocol
15
- else:
16
- from typing_extensions import Protocol
13
+ from typing import Protocol
17
14
 
18
15
 
19
16
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@@ -128,6 +125,9 @@ class EventMsg(Protocol):
128
125
  data: Message
129
126
 
130
127
 
128
+ TCallback = Callable[[EventMsg], None]
129
+
130
+
131
131
  def msg_from_base_event(event: BaseEvent, level: Optional[EventLevel] = None):
132
132
  msg_class_name = f"{type(event).__name__}Msg"
133
133
  msg_cls = getattr(event.PROTO_TYPES_MODULE, msg_class_name)
@@ -1,15 +1,15 @@
1
1
  import os
2
2
  import traceback
3
- from typing import Callable, List, Optional, Protocol, Tuple
3
+ from typing import List, Optional, Protocol, Tuple
4
4
 
5
- from dbt_common.events.base_types import BaseEvent, EventLevel, msg_from_base_event, EventMsg
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
7
 
8
8
 
9
9
  class EventManager:
10
10
  def __init__(self) -> None:
11
11
  self.loggers: List[_Logger] = []
12
- self.callbacks: List[Callable[[EventMsg], None]] = []
12
+ self.callbacks: List[TCallback] = []
13
13
 
14
14
  def fire_event(self, e: BaseEvent, level: Optional[EventLevel] = None) -> None:
15
15
  msg = msg_from_base_event(e, level=level)
@@ -37,13 +37,16 @@ class EventManager:
37
37
  )
38
38
  self.loggers.append(logger)
39
39
 
40
+ def add_callback(self, callback: TCallback) -> None:
41
+ self.callbacks.append(callback)
42
+
40
43
  def flush(self) -> None:
41
44
  for logger in self.loggers:
42
45
  logger.flush()
43
46
 
44
47
 
45
48
  class IEventManager(Protocol):
46
- callbacks: List[Callable[[EventMsg], None]]
49
+ callbacks: List[TCallback]
47
50
  loggers: List[_Logger]
48
51
 
49
52
  def fire_event(self, e: BaseEvent, level: Optional[EventLevel] = None) -> None:
@@ -52,6 +55,9 @@ class IEventManager(Protocol):
52
55
  def add_logger(self, config: LoggerConfig) -> None:
53
56
  ...
54
57
 
58
+ def add_callback(self, callback: TCallback) -> None:
59
+ ...
60
+
55
61
 
56
62
  class TestEventManager(IEventManager):
57
63
  __test__ = False
@@ -1,6 +1,7 @@
1
1
  # Since dbt-rpc does not do its own log setup, and since some events can
2
2
  # currently fire before logs can be configured by setup_event_logger(), we
3
3
  # create a default configuration with default settings and no file output.
4
+ from dbt_common.events.base_types import TCallback
4
5
  from dbt_common.events.event_manager import IEventManager, EventManager
5
6
 
6
7
  _EVENT_MANAGER: IEventManager = EventManager()
@@ -16,6 +17,11 @@ def add_logger_to_manager(logger) -> None:
16
17
  _EVENT_MANAGER.add_logger(logger)
17
18
 
18
19
 
20
+ def add_callback_to_manager(callback: TCallback) -> None:
21
+ global _EVENT_MANAGER
22
+ _EVENT_MANAGER.add_callback(callback)
23
+
24
+
19
25
  def ctx_set_event_manager(event_manager: IEventManager) -> None:
20
26
  global _EVENT_MANAGER
21
27
  _EVENT_MANAGER = event_manager
@@ -13,6 +13,13 @@ from dbt_common.events.base_types import EventLevel, EventMsg
13
13
  from dbt_common.events.format import timestamp_to_datetime_string
14
14
  from dbt_common.utils.encoding import ForgivingJSONEncoder
15
15
 
16
+ PRINT_EVENT_NAME = "PrintEvent"
17
+
18
+
19
+ def _is_print_event(msg: EventMsg) -> bool:
20
+ return msg.info.name == PRINT_EVENT_NAME
21
+
22
+
16
23
  # A Filter is a function which takes a BaseEvent and returns True if the event
17
24
  # should be logged, False otherwise.
18
25
  Filter = Callable[[EventMsg], bool]
@@ -120,7 +127,14 @@ class _Logger:
120
127
  def write_line(self, msg: EventMsg):
121
128
  line = self.create_line(msg)
122
129
  if self._python_logger is not None:
123
- send_to_logger(self._python_logger, msg.info.level, line)
130
+ # We send PrintEvent to logger as error so it goes to stdout
131
+ # when --quiet flag is set.
132
+ # --quiet flag will filter out all events lower than ERROR.
133
+ if _is_print_event(msg):
134
+ level = "error"
135
+ else:
136
+ level = msg.info.level
137
+ send_to_logger(self._python_logger, level, line)
124
138
 
125
139
  def flush(self):
126
140
  if self._python_logger is not None:
@@ -138,8 +152,11 @@ class _TextLogger(_Logger):
138
152
  return self.create_debug_line(msg) if self.use_debug_format else self.create_info_line(msg)
139
153
 
140
154
  def create_info_line(self, msg: EventMsg) -> str:
141
- ts: str = datetime.utcnow().strftime("%H:%M:%S")
142
155
  scrubbed_msg: str = self.scrubber(msg.info.msg) # type: ignore
156
+ if _is_print_event(msg):
157
+ # PrintEvent is a special case, we don't want to add a timestamp
158
+ return scrubbed_msg
159
+ ts: str = datetime.utcnow().strftime("%H:%M:%S")
143
160
  return f"{self._get_color_tag()}{ts} {scrubbed_msg}"
144
161
 
145
162
  def create_debug_line(self, msg: EventMsg) -> str:
@@ -119,3 +119,13 @@ message NoteMsg {
119
119
  EventInfo info = 1;
120
120
  Note data = 2;
121
121
  }
122
+
123
+ // Z052
124
+ message PrintEvent {
125
+ string msg = 1;
126
+ }
127
+
128
+ message PrintEventMsg {
129
+ EventInfo info = 1;
130
+ PrintEvent data = 2;
131
+ }
@@ -125,3 +125,14 @@ class Note(InfoLevel):
125
125
 
126
126
  def message(self) -> str:
127
127
  return self.msg
128
+
129
+
130
+ class PrintEvent(InfoLevel):
131
+ # Use this event to skip any formatting and just print a message
132
+ # This event will get to stdout even if the logger is set to ERROR
133
+ # This is to support commands that want --quiet option but also log something to stdout
134
+ def code(self) -> str:
135
+ return "Z052"
136
+
137
+ def message(self) -> str:
138
+ return self.msg
@@ -1,11 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
3
  # source: types.proto
4
+ # Protobuf Python Version: 5.26.1
4
5
  """Generated protocol buffer code."""
5
- from google.protobuf.internal import builder as _builder
6
6
  from google.protobuf import descriptor as _descriptor
7
7
  from google.protobuf import descriptor_pool as _descriptor_pool
8
8
  from google.protobuf import symbol_database as _symbol_database
9
+ from google.protobuf.internal import builder as _builder
9
10
  # @@protoc_insertion_point(imports)
10
11
 
11
12
  _sym_db = _symbol_database.Default()
@@ -14,55 +15,59 @@ _sym_db = _symbol_database.Default()
14
15
  from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
15
16
 
16
17
 
17
- 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\"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.Noteb\x06proto3')
18
-
19
- _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
20
- _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'types_pb2', globals())
21
- if _descriptor._USE_C_DESCRIPTORS == False:
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\"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')
22
19
 
23
- DESCRIPTOR._options = None
24
- _EVENTINFO_EXTRAENTRY._options = None
25
- _EVENTINFO_EXTRAENTRY._serialized_options = b'8\001'
26
- _EVENTINFO._serialized_start=62
27
- _EVENTINFO._serialized_end=335
28
- _EVENTINFO_EXTRAENTRY._serialized_start=291
29
- _EVENTINFO_EXTRAENTRY._serialized_end=335
30
- _GENERICMESSAGE._serialized_start=337
31
- _GENERICMESSAGE._serialized_end=391
32
- _RETRYEXTERNALCALL._serialized_start=393
33
- _RETRYEXTERNALCALL._serialized_end=442
34
- _RETRYEXTERNALCALLMSG._serialized_start=444
35
- _RETRYEXTERNALCALLMSG._serialized_end=550
36
- _RECORDRETRYEXCEPTION._serialized_start=552
37
- _RECORDRETRYEXCEPTION._serialized_end=587
38
- _RECORDRETRYEXCEPTIONMSG._serialized_start=589
39
- _RECORDRETRYEXCEPTIONMSG._serialized_end=701
40
- _SYSTEMCOULDNOTWRITE._serialized_start=703
41
- _SYSTEMCOULDNOTWRITE._serialized_end=767
42
- _SYSTEMCOULDNOTWRITEMSG._serialized_start=769
43
- _SYSTEMCOULDNOTWRITEMSG._serialized_end=879
44
- _SYSTEMEXECUTINGCMD._serialized_start=881
45
- _SYSTEMEXECUTINGCMD._serialized_end=914
46
- _SYSTEMEXECUTINGCMDMSG._serialized_start=916
47
- _SYSTEMEXECUTINGCMDMSG._serialized_end=1024
48
- _SYSTEMSTDOUT._serialized_start=1026
49
- _SYSTEMSTDOUT._serialized_end=1054
50
- _SYSTEMSTDOUTMSG._serialized_start=1056
51
- _SYSTEMSTDOUTMSG._serialized_end=1152
52
- _SYSTEMSTDERR._serialized_start=1154
53
- _SYSTEMSTDERR._serialized_end=1182
54
- _SYSTEMSTDERRMSG._serialized_start=1184
55
- _SYSTEMSTDERRMSG._serialized_end=1280
56
- _SYSTEMREPORTRETURNCODE._serialized_start=1282
57
- _SYSTEMREPORTRETURNCODE._serialized_end=1326
58
- _SYSTEMREPORTRETURNCODEMSG._serialized_start=1328
59
- _SYSTEMREPORTRETURNCODEMSG._serialized_end=1444
60
- _FORMATTING._serialized_start=1446
61
- _FORMATTING._serialized_end=1471
62
- _FORMATTINGMSG._serialized_start=1473
63
- _FORMATTINGMSG._serialized_end=1565
64
- _NOTE._serialized_start=1567
65
- _NOTE._serialized_end=1586
66
- _NOTEMSG._serialized_start=1588
67
- _NOTEMSG._serialized_end=1668
20
+ _globals = globals()
21
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
22
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'types_pb2', _globals)
23
+ if not _descriptor._USE_C_DESCRIPTORS:
24
+ DESCRIPTOR._loaded_options = None
25
+ _globals['_EVENTINFO_EXTRAENTRY']._loaded_options = None
26
+ _globals['_EVENTINFO_EXTRAENTRY']._serialized_options = b'8\001'
27
+ _globals['_EVENTINFO']._serialized_start=62
28
+ _globals['_EVENTINFO']._serialized_end=335
29
+ _globals['_EVENTINFO_EXTRAENTRY']._serialized_start=291
30
+ _globals['_EVENTINFO_EXTRAENTRY']._serialized_end=335
31
+ _globals['_GENERICMESSAGE']._serialized_start=337
32
+ _globals['_GENERICMESSAGE']._serialized_end=391
33
+ _globals['_RETRYEXTERNALCALL']._serialized_start=393
34
+ _globals['_RETRYEXTERNALCALL']._serialized_end=442
35
+ _globals['_RETRYEXTERNALCALLMSG']._serialized_start=444
36
+ _globals['_RETRYEXTERNALCALLMSG']._serialized_end=550
37
+ _globals['_RECORDRETRYEXCEPTION']._serialized_start=552
38
+ _globals['_RECORDRETRYEXCEPTION']._serialized_end=587
39
+ _globals['_RECORDRETRYEXCEPTIONMSG']._serialized_start=589
40
+ _globals['_RECORDRETRYEXCEPTIONMSG']._serialized_end=701
41
+ _globals['_SYSTEMCOULDNOTWRITE']._serialized_start=703
42
+ _globals['_SYSTEMCOULDNOTWRITE']._serialized_end=767
43
+ _globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_start=769
44
+ _globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_end=879
45
+ _globals['_SYSTEMEXECUTINGCMD']._serialized_start=881
46
+ _globals['_SYSTEMEXECUTINGCMD']._serialized_end=914
47
+ _globals['_SYSTEMEXECUTINGCMDMSG']._serialized_start=916
48
+ _globals['_SYSTEMEXECUTINGCMDMSG']._serialized_end=1024
49
+ _globals['_SYSTEMSTDOUT']._serialized_start=1026
50
+ _globals['_SYSTEMSTDOUT']._serialized_end=1054
51
+ _globals['_SYSTEMSTDOUTMSG']._serialized_start=1056
52
+ _globals['_SYSTEMSTDOUTMSG']._serialized_end=1152
53
+ _globals['_SYSTEMSTDERR']._serialized_start=1154
54
+ _globals['_SYSTEMSTDERR']._serialized_end=1182
55
+ _globals['_SYSTEMSTDERRMSG']._serialized_start=1184
56
+ _globals['_SYSTEMSTDERRMSG']._serialized_end=1280
57
+ _globals['_SYSTEMREPORTRETURNCODE']._serialized_start=1282
58
+ _globals['_SYSTEMREPORTRETURNCODE']._serialized_end=1326
59
+ _globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_start=1328
60
+ _globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_end=1444
61
+ _globals['_FORMATTING']._serialized_start=1446
62
+ _globals['_FORMATTING']._serialized_end=1471
63
+ _globals['_FORMATTINGMSG']._serialized_start=1473
64
+ _globals['_FORMATTINGMSG']._serialized_end=1565
65
+ _globals['_NOTE']._serialized_start=1567
66
+ _globals['_NOTE']._serialized_end=1586
67
+ _globals['_NOTEMSG']._serialized_start=1588
68
+ _globals['_NOTEMSG']._serialized_end=1668
69
+ _globals['_PRINTEVENT']._serialized_start=1670
70
+ _globals['_PRINTEVENT']._serialized_end=1695
71
+ _globals['_PRINTEVENTMSG']._serialized_start=1697
72
+ _globals['_PRINTEVENTMSG']._serialized_end=1789
68
73
  # @@protoc_insertion_point(module_scope)
@@ -2,8 +2,8 @@
2
2
  external systems during a command invocation, so that the command can be re-run
3
3
  later with the recording 'replayed' to dbt.
4
4
 
5
- If dbt behaves sufficiently deterministically, we will be able to use the
6
- record/replay mechanism in several interesting test and debugging scenarios.
5
+ The rationale for and architecture of this module is described in detail in the
6
+ docs/guides/record_replay.md document in this repository.
7
7
  """
8
8
  import functools
9
9
  import dataclasses
@@ -0,0 +1,5 @@
1
+ ## Documentation
2
+
3
+ The documentation is divided into the following sub-folders:
4
+ * arch: Architecture Decision Records (ADRs) which explain and justify major architectural decisions
5
+ * guides: Informal documents which describe the code or our development practices at a high level
@@ -0,0 +1,41 @@
1
+ # The Record/Replay Subsystem
2
+ The `dbt_common.record` module provides a semi-experimental mechanism for recording dbt's interaction with external systems during a command invocation. The recording can be used as a record of how dbt behaved during an invocation, or it "replayed" later by a different version of dbt to compare its behavior to the original, without actually interacting with a data warehouse, the filesystem, or any other external process.
3
+
4
+ For now, this mechanism should be regarded as an unstable preview. Details of how it works, which functions are annotated, and how annotations are made, are subject to arbitrary changes.
5
+
6
+ If dbt's internal behavior is sufficiently deterministic, we will be able to use the record/replay mechanism in several interesting test and debugging scenarios, but our plan is to start by developing a robust record capability, since it would immediately support the testing scenarios we are most interested in.
7
+
8
+ This mechanism is a work in progress. Not all of dbt's interactions are recorded as of this writing. The rest of this document explains how the mechanism works and how to improve its coverage, so that more people can help with the effort.
9
+
10
+
11
+ ## How it Works
12
+
13
+ We assume that every interaction between dbt and an external system is performed via a function call, and that all of those function calls are marked with the `@record_function` decorator. When recording, the parameters passed to and results returned from these annotated functions are recorded, so that they can be persisted to file for later comparison or for use with replay.
14
+
15
+ Now, suppose you recorded the data warehouse interactions while running v1 of `dbt-core` and you want to make sure that refactorizations you have done for v2 did not change the way dbt interacts with the warehouse. You can simply record a run with the same command using v2 and compare the two recordings to see if any of the SQL sent to the warehouse has changed. This may mean ignoring certain changes to whitespace or formatting which is also handled by the record/replay mechanism.
16
+
17
+ One problem which might arise in the scenario just described is that the results of introspective queries returned from the warehouse differ between runs, subtly changing dbt's behavior. This is where the replay mechanism will help us. When replay is enabled, an existing recording is used to mock out the function calls annotated with @record_function. The parameters to the function will be used to locate the corresponding call in the recording, and the recorded return value for that call is returned. In principle, all interaction with external systems can be mocked out this way, allowing dbt to be isolated and any deviation from its behavior in the recording can be noted.
18
+
19
+ ## How to Use It
20
+
21
+ An example of how the mechanism is applied can be found in ./clients/system.py with the load_file_contents() function. Notice the decorator applied to this function:
22
+ ```python
23
+ @record_function(LoadFileRecord)
24
+ ```
25
+ When record and replay are disabled, this decorator is a no-op, but when one of them is enabled it implements the behaviors described above.
26
+
27
+ Note also the `LoadFileRecord` class passed as a parameter to this decorator. This is (and must be) a class with the two properties `params_cls`, and `result_cls` specified. The class itself is registered with the record/replay mechanism by annotating it with `@Recorder.register_record_type`.
28
+
29
+ The final detail needed is to define the classes specified by `params_cls` and `result_cls`, which must be dataclasses with properties whose order and names correspond to the parameters passed to the recorded function. In this case those are the `LoadFileParams` and `LoadFileResult` classes, respectively.
30
+
31
+ With these decorators applied and classes defined, dbt is able to record all file access during a run, and mock out the accesses during replay, isolating dbt from actually loading files. At least it would if dbt only used this function for all file access, which is only mostly true. We hope to continue improving the usefulness of this mechanism by adding more recorded functions and routing more operations through them.
32
+
33
+ ## Final Thoughts
34
+
35
+ We are aware of the potential limitations of this mechanism, since it makes several strong assumptions, not least of which are:
36
+
37
+ 1. Every important interaction with an external system can be modeled as a function call.
38
+ 2. Every important interaction can be recorded without creating an impractically large output file.
39
+ 3. The recorded functions do not have important side effects within dbt itself which would not be duplicated during replay.
40
+
41
+ Nonetheless, we are excited to see how far the experiment takes us and how we can apply it to automatically detect changes in dbt's behavior during testing and upgrades.
@@ -58,6 +58,7 @@ lint = [
58
58
  ]
59
59
  test = [
60
60
  "pytest>=7.3,<8.0",
61
+ "pytest-mock",
61
62
  "pytest-xdist>=3.2,<4.0",
62
63
  "pytest-cov>=4.1,<5.0",
63
64
  "hypothesis>=6.87,<7.0",
@@ -1 +0,0 @@
1
- version = "1.0.4"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes