From 1367405eca8ce8bcd4f463c2ff0b066eb24aeece Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Tue, 17 Sep 2024 17:45:49 -0400 Subject: [PATCH 1/6] update behavior deprecation flags to be behavior change flags --- dbt_common/behavior_flags.py | 41 +++++++------- dbt_common/events/types.proto | 11 ++-- dbt_common/events/types.py | 27 +++++----- dbt_common/events/types_pb2.py | 90 +++++++++++++++---------------- tests/unit/test_behavior_flags.py | 64 +++++++++++++++------- tests/unit/test_events.py | 2 +- 6 files changed, 130 insertions(+), 105 deletions(-) diff --git a/dbt_common/behavior_flags.py b/dbt_common/behavior_flags.py index a647ed7..91694f5 100644 --- a/dbt_common/behavior_flags.py +++ b/dbt_common/behavior_flags.py @@ -9,8 +9,8 @@ from typing import Optional as NotRequired from dbt_common.events.functions import fire_event -from dbt_common.events.types import BehaviorDeprecationEvent -from dbt_common.exceptions import CompilationError +from dbt_common.events.types import BehaviorChangeEvent +from dbt_common.exceptions import CompilationError, DbtInternalError class BehaviorFlag(TypedDict): @@ -20,16 +20,14 @@ class BehaviorFlag(TypedDict): Args: name: the name of the behavior flag default: default setting, starts as False, becomes True after a bake-in period - deprecation_version: the version when the default will change to True - deprecation_message: an additional message to send when the flag evaluates to False + description: an additional message to send when the flag evaluates to False docs_url: the url to the relevant docs on docs.getdbt.com """ name: str default: bool source: NotRequired[str] - deprecation_version: NotRequired[str] - deprecation_message: NotRequired[str] + description: NotRequired[str] docs_url: NotRequired[str] @@ -43,14 +41,28 @@ class BehaviorFlagRendered: """ def __init__(self, flag: BehaviorFlag, user_overrides: Dict[str, Any]) -> None: + self._validate(flag) + self.name = flag["name"] self.setting = user_overrides.get(flag["name"], flag["default"]) - self.deprecation_event = self._deprecation_event(flag) + self._behavior_change_event = BehaviorChangeEvent( + flag_name=flag["name"], + flag_source=flag.get("source", self._default_source()), + description=flag.get("description"), + docs_url=flag.get("docs_url"), + ) + + @staticmethod + def _validate(flag: BehaviorFlag) -> None: + if flag.get("description") is None and flag.get("docs_url") is None: + raise DbtInternalError( + "Behavior change flags require at least one of `description` and `docs_url`." + ) @property def setting(self) -> bool: if self._setting is False: - fire_event(self.deprecation_event) + fire_event(self._behavior_change_event) return self._setting @setting.setter @@ -61,15 +73,6 @@ def setting(self, value: bool) -> None: def no_warn(self) -> bool: return self._setting - def _deprecation_event(self, flag: BehaviorFlag) -> BehaviorDeprecationEvent: - return BehaviorDeprecationEvent( - flag_name=flag["name"], - flag_source=flag.get("source", self._default_source()), - deprecation_version=flag.get("deprecation_version"), - deprecation_message=flag.get("deprecation_message"), - docs_url=flag.get("docs_url"), - ) - @staticmethod def _default_source() -> str: """ @@ -95,7 +98,7 @@ class Behavior: if adapter.behavior.my_flag: ... - if adapter.behavior.my_flag.no_warn: # this will not fire the deprecation event + if adapter.behavior.my_flag.no_warn: # this will not fire the behavior change event ... ``` ```jinja @@ -103,7 +106,7 @@ class Behavior: ... {% endif %} - {% if adapter.behavior.my_flag.no_warn %} {# this will not fire the deprecation event #} + {% if adapter.behavior.my_flag.no_warn %} {# this will not fire the behavior change event #} ... {% endif %} ``` diff --git a/dbt_common/events/types.proto b/dbt_common/events/types.proto index d72d6b2..3826a0f 100644 --- a/dbt_common/events/types.proto +++ b/dbt_common/events/types.proto @@ -26,17 +26,16 @@ message GenericMessage { // D - Deprecations // D018 -message BehaviorDeprecationEvent { +message BehaviorChangeEvent { string flag_name = 1; string flag_source = 2; - string deprecation_version = 3; - string deprecation_message = 4; - string docs_url = 5; + string description = 3; + string docs_url = 4; } -message BehaviorDeprecationEventMsg { +message BehaviorChangeEventMsg { EventInfo info = 1; - BehaviorDeprecationEvent data = 2; + BehaviorChangeEvent data = 2; } // M - Deps generation diff --git a/dbt_common/events/types.py b/dbt_common/events/types.py index 02fc3ee..2fc01a1 100644 --- a/dbt_common/events/types.py +++ b/dbt_common/events/types.py @@ -38,30 +38,27 @@ # ======================================================= -class BehaviorDeprecationEvent(WarnLevel): +class BehaviorChangeEvent(WarnLevel): flag_name: str flag_source: str - deprecation_version: Optional[str] - deprecation_message: Optional[str] - docs_url: Optional[str] + description: Optional[str] = None + docs_url: Optional[str] = None def code(self) -> str: return "D018" def message(self) -> str: - msg = f"The legacy behavior controlled by `{self.flag_name}` is deprecated.\n" - - if self.deprecation_version: - msg = ( - f"The legacy behavior is expected to be retired in `{self.deprecation_version}`.\n" - ) - - msg += f"The new behavior can be turned on by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n" + msg = ( + f"The behavior controlled by `{self.flag_name}` is currently turned off.\n" + f"This behavior can be turned on by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n" + ) - if self.deprecation_message: - msg += f"{self.deprecation_message}.\n" + if self.description: + msg += f"{self.description}.\n" - docs_url = self.docs_url or f"https://docs.getdbt.com/search?q={self.flag_name}" + docs_url = ( + self.docs_url or "https://docs.getdbt.com/reference/global-configs/behavior-changes" + ) msg += f"Visit {docs_url} for more information." return warning_tag(msg) diff --git a/dbt_common/events/types_pb2.py b/dbt_common/events/types_pb2.py index 6b3afc5..6574462 100644 --- a/dbt_common/events/types_pb2.py +++ b/dbt_common/events/types_pb2.py @@ -15,7 +15,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -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\"\x8e\x01\n\x18\x42\x65haviorDeprecationEvent\x12\x11\n\tflag_name\x18\x01 \x01(\t\x12\x13\n\x0b\x66lag_source\x18\x02 \x01(\t\x12\x1b\n\x13\x64\x65precation_version\x18\x03 \x01(\t\x12\x1b\n\x13\x64\x65precation_message\x18\x04 \x01(\t\x12\x10\n\x08\x64ocs_url\x18\x05 \x01(\t\"x\n\x1b\x42\x65haviorDeprecationEventMsg\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.proto_types.EventInfo\x12\x33\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32%.proto_types.BehaviorDeprecationEvent\"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') +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') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -30,48 +30,48 @@ _globals['_EVENTINFO_EXTRAENTRY']._serialized_end=335 _globals['_GENERICMESSAGE']._serialized_start=337 _globals['_GENERICMESSAGE']._serialized_end=391 - _globals['_BEHAVIORDEPRECATIONEVENT']._serialized_start=394 - _globals['_BEHAVIORDEPRECATIONEVENT']._serialized_end=536 - _globals['_BEHAVIORDEPRECATIONEVENTMSG']._serialized_start=538 - _globals['_BEHAVIORDEPRECATIONEVENTMSG']._serialized_end=658 - _globals['_RETRYEXTERNALCALL']._serialized_start=660 - _globals['_RETRYEXTERNALCALL']._serialized_end=709 - _globals['_RETRYEXTERNALCALLMSG']._serialized_start=711 - _globals['_RETRYEXTERNALCALLMSG']._serialized_end=817 - _globals['_RECORDRETRYEXCEPTION']._serialized_start=819 - _globals['_RECORDRETRYEXCEPTION']._serialized_end=854 - _globals['_RECORDRETRYEXCEPTIONMSG']._serialized_start=856 - _globals['_RECORDRETRYEXCEPTIONMSG']._serialized_end=968 - _globals['_SYSTEMCOULDNOTWRITE']._serialized_start=970 - _globals['_SYSTEMCOULDNOTWRITE']._serialized_end=1034 - _globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_start=1036 - _globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_end=1146 - _globals['_SYSTEMEXECUTINGCMD']._serialized_start=1148 - _globals['_SYSTEMEXECUTINGCMD']._serialized_end=1181 - _globals['_SYSTEMEXECUTINGCMDMSG']._serialized_start=1183 - _globals['_SYSTEMEXECUTINGCMDMSG']._serialized_end=1291 - _globals['_SYSTEMSTDOUT']._serialized_start=1293 - _globals['_SYSTEMSTDOUT']._serialized_end=1321 - _globals['_SYSTEMSTDOUTMSG']._serialized_start=1323 - _globals['_SYSTEMSTDOUTMSG']._serialized_end=1419 - _globals['_SYSTEMSTDERR']._serialized_start=1421 - _globals['_SYSTEMSTDERR']._serialized_end=1449 - _globals['_SYSTEMSTDERRMSG']._serialized_start=1451 - _globals['_SYSTEMSTDERRMSG']._serialized_end=1547 - _globals['_SYSTEMREPORTRETURNCODE']._serialized_start=1549 - _globals['_SYSTEMREPORTRETURNCODE']._serialized_end=1593 - _globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_start=1595 - _globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_end=1711 - _globals['_FORMATTING']._serialized_start=1713 - _globals['_FORMATTING']._serialized_end=1738 - _globals['_FORMATTINGMSG']._serialized_start=1740 - _globals['_FORMATTINGMSG']._serialized_end=1832 - _globals['_NOTE']._serialized_start=1834 - _globals['_NOTE']._serialized_end=1853 - _globals['_NOTEMSG']._serialized_start=1855 - _globals['_NOTEMSG']._serialized_end=1935 - _globals['_PRINTEVENT']._serialized_start=1937 - _globals['_PRINTEVENT']._serialized_end=1962 - _globals['_PRINTEVENTMSG']._serialized_start=1964 - _globals['_PRINTEVENTMSG']._serialized_end=2056 + _globals['_BEHAVIORCHANGEEVENT']._serialized_start=393 + _globals['_BEHAVIORCHANGEEVENT']._serialized_end=493 + _globals['_BEHAVIORCHANGEEVENTMSG']._serialized_start=495 + _globals['_BEHAVIORCHANGEEVENTMSG']._serialized_end=605 + _globals['_RETRYEXTERNALCALL']._serialized_start=607 + _globals['_RETRYEXTERNALCALL']._serialized_end=656 + _globals['_RETRYEXTERNALCALLMSG']._serialized_start=658 + _globals['_RETRYEXTERNALCALLMSG']._serialized_end=764 + _globals['_RECORDRETRYEXCEPTION']._serialized_start=766 + _globals['_RECORDRETRYEXCEPTION']._serialized_end=801 + _globals['_RECORDRETRYEXCEPTIONMSG']._serialized_start=803 + _globals['_RECORDRETRYEXCEPTIONMSG']._serialized_end=915 + _globals['_SYSTEMCOULDNOTWRITE']._serialized_start=917 + _globals['_SYSTEMCOULDNOTWRITE']._serialized_end=981 + _globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_start=983 + _globals['_SYSTEMCOULDNOTWRITEMSG']._serialized_end=1093 + _globals['_SYSTEMEXECUTINGCMD']._serialized_start=1095 + _globals['_SYSTEMEXECUTINGCMD']._serialized_end=1128 + _globals['_SYSTEMEXECUTINGCMDMSG']._serialized_start=1130 + _globals['_SYSTEMEXECUTINGCMDMSG']._serialized_end=1238 + _globals['_SYSTEMSTDOUT']._serialized_start=1240 + _globals['_SYSTEMSTDOUT']._serialized_end=1268 + _globals['_SYSTEMSTDOUTMSG']._serialized_start=1270 + _globals['_SYSTEMSTDOUTMSG']._serialized_end=1366 + _globals['_SYSTEMSTDERR']._serialized_start=1368 + _globals['_SYSTEMSTDERR']._serialized_end=1396 + _globals['_SYSTEMSTDERRMSG']._serialized_start=1398 + _globals['_SYSTEMSTDERRMSG']._serialized_end=1494 + _globals['_SYSTEMREPORTRETURNCODE']._serialized_start=1496 + _globals['_SYSTEMREPORTRETURNCODE']._serialized_end=1540 + _globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_start=1542 + _globals['_SYSTEMREPORTRETURNCODEMSG']._serialized_end=1658 + _globals['_FORMATTING']._serialized_start=1660 + _globals['_FORMATTING']._serialized_end=1685 + _globals['_FORMATTINGMSG']._serialized_start=1687 + _globals['_FORMATTINGMSG']._serialized_end=1779 + _globals['_NOTE']._serialized_start=1781 + _globals['_NOTE']._serialized_end=1800 + _globals['_NOTEMSG']._serialized_start=1802 + _globals['_NOTEMSG']._serialized_end=1882 + _globals['_PRINTEVENT']._serialized_start=1884 + _globals['_PRINTEVENT']._serialized_end=1909 + _globals['_PRINTEVENTMSG']._serialized_start=1911 + _globals['_PRINTEVENTMSG']._serialized_end=2003 # @@protoc_insertion_point(module_scope) diff --git a/tests/unit/test_behavior_flags.py b/tests/unit/test_behavior_flags.py index 73c550d..fe640e3 100644 --- a/tests/unit/test_behavior_flags.py +++ b/tests/unit/test_behavior_flags.py @@ -8,8 +8,8 @@ def test_behavior_default() -> None: behavior = Behavior( [ - {"name": "default_false_flag", "default": False}, - {"name": "default_true_flag", "default": True}, + {"name": "default_false_flag", "default": False, "description": "This flag is false."}, + {"name": "default_true_flag", "default": True, "description": "This flag is true."}, ], {}, ) @@ -21,12 +21,28 @@ def test_behavior_default() -> None: def test_behavior_user_override() -> None: behavior = Behavior( [ - {"name": "flag_default_false", "default": False}, - {"name": "flag_default_false_override_false", "default": False}, - {"name": "flag_default_false_override_true", "default": False}, - {"name": "flag_default_true", "default": True}, - {"name": "flag_default_true_override_false", "default": True}, - {"name": "flag_default_true_override_true", "default": True}, + {"name": "flag_default_false", "default": False, "description": "This flag is false."}, + { + "name": "flag_default_false_override_false", + "default": False, + "description": "This flag is false.", + }, + { + "name": "flag_default_false_override_true", + "default": False, + "description": "This flag is true.", + }, + {"name": "flag_default_true", "default": True, "description": "This flag is true."}, + { + "name": "flag_default_true_override_false", + "default": True, + "description": "This flag is false.", + }, + { + "name": "flag_default_true_override_true", + "default": True, + "description": "This flag is true.", + }, ], { "flag_default_false_override_false": False, @@ -47,7 +63,11 @@ def test_behavior_user_override() -> None: def test_behavior_unregistered_flag_raises_correct_exception() -> None: behavior = Behavior( [ - {"name": "behavior_flag_exists", "default": False}, + { + "name": "behavior_flag_exists", + "default": False, + "description": "This flag is false.", + }, ], {}, ) @@ -60,8 +80,8 @@ def test_behavior_unregistered_flag_raises_correct_exception() -> None: def test_behavior_flag_can_be_used_as_conditional() -> None: behavior = Behavior( [ - {"name": "flag_false", "default": False}, - {"name": "flag_true", "default": True}, + {"name": "flag_false", "default": False, "description": "This flag is false."}, + {"name": "flag_true", "default": True, "description": "This flag is true."}, ], {}, ) @@ -70,11 +90,13 @@ def test_behavior_flag_can_be_used_as_conditional() -> None: assert True if behavior.flag_true else False -def test_behavior_flags_emit_deprecation_event_on_evaluation(event_catcher: EventCatcher) -> None: +def test_behavior_flags_emit_behavior_change_event_on_evaluation( + event_catcher: EventCatcher, +) -> None: behavior = Behavior( [ - {"name": "flag_false", "default": False}, - {"name": "flag_true", "default": True}, + {"name": "flag_false", "default": False, "description": "This flag is false."}, + {"name": "flag_true", "default": True, "description": "This flag is true."}, ], {}, ) @@ -90,21 +112,25 @@ def test_behavior_flags_emit_deprecation_event_on_evaluation(event_catcher: Even assert len(event_catcher.caught_events) == 1 -def test_behavior_flags_emit_correct_deprecation_event(event_catcher: EventCatcher) -> None: - behavior = Behavior([{"name": "flag_false", "default": False}], {}) +def test_behavior_flags_emit_correct_behavior_change_event(event_catcher: EventCatcher) -> None: + behavior = Behavior( + [{"name": "flag_false", "default": False, "description": "This flag is false."}], {} + ) # trigger the evaluation if behavior.flag_false: pass msg = event_catcher.caught_events[0] - assert msg.info.name == "BehaviorDeprecationEvent" + assert msg.info.name == "BehaviorChangeEvent" assert msg.data.flag_name == "flag_false" assert msg.data.flag_source == __name__ # defaults to the calling module -def test_behavior_flags_no_deprecation_event_on_no_warn(event_catcher: EventCatcher) -> None: - behavior = Behavior([{"name": "flag_false", "default": False}], {}) +def test_behavior_flags_no_behavior_change_event_on_no_warn(event_catcher: EventCatcher) -> None: + behavior = Behavior( + [{"name": "flag_false", "default": False, "description": "This flag is false."}], {} + ) # trigger the evaluation with no_warn, no event should fire if behavior.flag_false.no_warn: diff --git a/tests/unit/test_events.py b/tests/unit/test_events.py index 9a9abc6..f89a2b2 100644 --- a/tests/unit/test_events.py +++ b/tests/unit/test_events.py @@ -62,7 +62,7 @@ class TestEventJSONSerialization: # N.B. Events instantiated here include the module prefix in order to # avoid having the entire list twice in the code. # D - Deprecations ====================== - types.BehaviorDeprecationEvent(flag_name="Do you have a flag?", flag_source="dbt_common"), + types.BehaviorChangeEvent(flag_name="Do you have a flag?", flag_source="dbt_common"), # M - Deps generation ====================== types.RetryExternalCall(attempt=0, max=0), types.RecordRetryException(exc=""), From b650b1e8b5dddd4fbfa6c77bdf972c50c4974185 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Tue, 17 Sep 2024 17:58:25 -0400 Subject: [PATCH 2/6] update behavior deprecation flags to be behavior change flags --- dbt_common/behavior_flags.py | 6 ++++++ dbt_common/events/types.py | 7 ++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dbt_common/behavior_flags.py b/dbt_common/behavior_flags.py index 91694f5..a7ef1c3 100644 --- a/dbt_common/behavior_flags.py +++ b/dbt_common/behavior_flags.py @@ -22,6 +22,12 @@ class BehaviorFlag(TypedDict): default: default setting, starts as False, becomes True after a bake-in period description: an additional message to send when the flag evaluates to False docs_url: the url to the relevant docs on docs.getdbt.com + + *Note*: + While `description` and `docs_url` are both listed as `NotRequired`, at least one of them is required. + This is validated when the flag is rendered in `BehaviorFlagRendered` below. + The goal of this restriction is to provide the end user with context so they can make an informed decision + about if, and when, to enable the behavior flag. """ name: str diff --git a/dbt_common/events/types.py b/dbt_common/events/types.py index 2fc01a1..75e705c 100644 --- a/dbt_common/events/types.py +++ b/dbt_common/events/types.py @@ -42,7 +42,7 @@ class BehaviorChangeEvent(WarnLevel): flag_name: str flag_source: str description: Optional[str] = None - docs_url: Optional[str] = None + docs_url: Optional[str] = "https://docs.getdbt.com/reference/global-configs/behavior-changes" def code(self) -> str: return "D018" @@ -56,10 +56,7 @@ def message(self) -> str: if self.description: msg += f"{self.description}.\n" - docs_url = ( - self.docs_url or "https://docs.getdbt.com/reference/global-configs/behavior-changes" - ) - msg += f"Visit {docs_url} for more information." + msg += f"Visit {self.docs_url} for more information." return warning_tag(msg) From 1a298f134f2eecb50e436cdc207d745d0cc622f5 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Wed, 18 Sep 2024 11:06:18 -0400 Subject: [PATCH 3/6] update the default message for the behavior change event --- dbt_common/events/types.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dbt_common/events/types.py b/dbt_common/events/types.py index 75e705c..861c666 100644 --- a/dbt_common/events/types.py +++ b/dbt_common/events/types.py @@ -48,15 +48,15 @@ def code(self) -> str: return "D018" def message(self) -> str: - msg = ( - f"The behavior controlled by `{self.flag_name}` is currently turned off.\n" - f"This behavior can be turned on by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n" - ) - if self.description: - msg += f"{self.description}.\n" + msg = f"{self.description}.\n" + else: + msg = f"The behavior controlled by `{self.flag_name}` is currently turned off.\n" - msg += f"Visit {self.docs_url} for more information." + msg += ( + f"You may opt into the new behavior sooner by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n" + f"Visit {self.docs_url} for more information." + ) return warning_tag(msg) From 20f537d2ca86f69f67c130b71c557b77d1238c99 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Wed, 18 Sep 2024 11:49:57 -0400 Subject: [PATCH 4/6] move defaults into the behavior flag to work with the eventing system --- dbt_common/behavior_flags.py | 9 +++++++-- dbt_common/events/types.py | 17 ++--------------- tests/unit/test_behavior_flags.py | 28 ++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/dbt_common/behavior_flags.py b/dbt_common/behavior_flags.py index a7ef1c3..a2eb097 100644 --- a/dbt_common/behavior_flags.py +++ b/dbt_common/behavior_flags.py @@ -51,11 +51,16 @@ def __init__(self, flag: BehaviorFlag, user_overrides: Dict[str, Any]) -> None: self.name = flag["name"] self.setting = user_overrides.get(flag["name"], flag["default"]) + + default_description = ( + f"""The behavior controlled by `{flag["name"]}` is currently turned off.\n""" + ) + default_docs_url = "https://docs.getdbt.com/reference/global-configs/behavior-changes" self._behavior_change_event = BehaviorChangeEvent( flag_name=flag["name"], flag_source=flag.get("source", self._default_source()), - description=flag.get("description"), - docs_url=flag.get("docs_url"), + description=flag.get("description", default_description), + docs_url=flag.get("docs_url", default_docs_url), ) @staticmethod diff --git a/dbt_common/events/types.py b/dbt_common/events/types.py index 861c666..b203730 100644 --- a/dbt_common/events/types.py +++ b/dbt_common/events/types.py @@ -1,5 +1,3 @@ -from typing import Optional - from dbt_common.events.base_types import ( DebugLevel, InfoLevel, @@ -39,27 +37,16 @@ class BehaviorChangeEvent(WarnLevel): - flag_name: str - flag_source: str - description: Optional[str] = None - docs_url: Optional[str] = "https://docs.getdbt.com/reference/global-configs/behavior-changes" - def code(self) -> str: return "D018" def message(self) -> str: - if self.description: - msg = f"{self.description}.\n" - else: - msg = f"The behavior controlled by `{self.flag_name}` is currently turned off.\n" - - msg += ( + return warning_tag( + f"{self.description}.\n" f"You may opt into the new behavior sooner by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n" f"Visit {self.docs_url} for more information." ) - return warning_tag(msg) - # ======================================================= # M - Deps generation diff --git a/tests/unit/test_behavior_flags.py b/tests/unit/test_behavior_flags.py index fe640e3..6be38d0 100644 --- a/tests/unit/test_behavior_flags.py +++ b/tests/unit/test_behavior_flags.py @@ -112,10 +112,24 @@ def test_behavior_flags_emit_behavior_change_event_on_evaluation( assert len(event_catcher.caught_events) == 1 -def test_behavior_flags_emit_correct_behavior_change_event(event_catcher: EventCatcher) -> None: - behavior = Behavior( - [{"name": "flag_false", "default": False, "description": "This flag is false."}], {} - ) +@pytest.mark.parametrize( + "flag,event", + [ + ( + {"name": "flag_false", "default": False, "description": "This flag is false."}, + { + "flag_name": "flag_false", + "flag_source": __name__, + "description": "This flag is false.", + "docs_url": "https://docs.getdbt.com/reference/global-configs/behavior-changes", + }, + ) + ], +) +def test_behavior_flags_emit_correct_behavior_change_event( + event_catcher: EventCatcher, flag, event +) -> None: + behavior = Behavior([flag], {}) # trigger the evaluation if behavior.flag_false: @@ -123,8 +137,10 @@ def test_behavior_flags_emit_correct_behavior_change_event(event_catcher: EventC msg = event_catcher.caught_events[0] assert msg.info.name == "BehaviorChangeEvent" - assert msg.data.flag_name == "flag_false" - assert msg.data.flag_source == __name__ # defaults to the calling module + assert msg.data.flag_name == event["flag_name"] + assert msg.data.flag_source == event["flag_source"] + assert msg.data.description == event["description"] + assert msg.data.docs_url == event["docs_url"] def test_behavior_flags_no_behavior_change_event_on_no_warn(event_catcher: EventCatcher) -> None: From 16e33d0939ac702cc66fb32f1ced5a87935a2d41 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Wed, 18 Sep 2024 11:54:14 -0400 Subject: [PATCH 5/6] move defaults into the behavior flag to work with the eventing system --- tests/unit/test_behavior_flags.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_behavior_flags.py b/tests/unit/test_behavior_flags.py index 6be38d0..3c0ccce 100644 --- a/tests/unit/test_behavior_flags.py +++ b/tests/unit/test_behavior_flags.py @@ -1,7 +1,7 @@ import pytest from dbt_common.behavior_flags import Behavior -from dbt_common.exceptions.base import CompilationError +from dbt_common.exceptions.base import CompilationError, DbtInternalError from tests.unit.utils import EventCatcher @@ -123,7 +123,16 @@ def test_behavior_flags_emit_behavior_change_event_on_evaluation( "description": "This flag is false.", "docs_url": "https://docs.getdbt.com/reference/global-configs/behavior-changes", }, - ) + ), + ( + {"name": "flag_false", "default": False, "docs_url": "https://docs.getdbt.com"}, + { + "flag_name": "flag_false", + "flag_source": __name__, + "description": "The behavior controlled by `flag_false` is currently turned off.\n", + "docs_url": "https://docs.getdbt.com", + }, + ), ], ) def test_behavior_flags_emit_correct_behavior_change_event( @@ -157,3 +166,8 @@ def test_behavior_flags_no_behavior_change_event_on_no_warn(event_catcher: Event if behavior.flag_false: pass assert len(event_catcher.caught_events) == 1 + + +def test_behavior_flag_requires_description_or_docs_url(event_catcher: EventCatcher) -> None: + with pytest.raises(DbtInternalError): + Behavior([{"name": "flag_false", "default": False}], {}) From 215fe50fcd4947c2a37416e75eae78cf8f413abe Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Wed, 18 Sep 2024 11:57:53 -0400 Subject: [PATCH 6/6] remove extra period --- dbt_common/events/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbt_common/events/types.py b/dbt_common/events/types.py index b203730..e098c0c 100644 --- a/dbt_common/events/types.py +++ b/dbt_common/events/types.py @@ -42,7 +42,7 @@ def code(self) -> str: def message(self) -> str: return warning_tag( - f"{self.description}.\n" + f"{self.description}\n" f"You may opt into the new behavior sooner by setting `flags.{self.flag_name}` to `True` in `dbt_project.yml`.\n" f"Visit {self.docs_url} for more information." )