diff --git a/dbt_common/contracts/config/base.py b/dbt_common/contracts/config/base.py index b571cb2..74e80dc 100644 --- a/dbt_common/contracts/config/base.py +++ b/dbt_common/contracts/config/base.py @@ -115,6 +115,30 @@ def same_contents(cls, unrendered: Dict[str, Any], other: Dict[str, Any]) -> boo "object": ["snapshot_meta_column_names"], } + @classmethod + def update_from( + cls, + orig_dict: Dict[str, Any], + new_dict: Dict[str, Any], + adapter_config_cls: Type[BaseConfig], + ) -> Dict[str, Any]: + """Update and validate config given a dict. + + Given a dict of keys, update the current config from them, validate + it, and return a new config with the updated values + """ + + self_merged = cls._merge_dicts(orig_dict, new_dict) + new_dict.update(self_merged) + + adapter_merged = adapter_config_cls._merge_dicts(orig_dict, new_dict) + new_dict.update(adapter_merged) + + # any remaining fields must be "clobber" + orig_dict.update(new_dict) + + return orig_dict + @classmethod def _merge_dicts(cls, src: Dict[str, Any], data: Dict[str, Any]) -> Dict[str, Any]: """Mutate input to return merge results. @@ -150,30 +174,6 @@ def _merge_dicts(cls, src: Dict[str, Any], data: Dict[str, Any]) -> Dict[str, An ) return result - def update_from( - self: T, data: Dict[str, Any], config_cls: Type[BaseConfig], validate: bool = True - ) -> T: - """Update and validate config given a dict. - - Given a dict of keys, update the current config from them, validate - it, and return a new config with the updated values - """ - dct = self.to_dict(omit_none=False) - - self_merged = self._merge_dicts(dct, data) - dct.update(self_merged) - - adapter_merged = config_cls._merge_dicts(dct, data) - dct.update(adapter_merged) - - # any remaining fields must be "clobber" - dct.update(data) - - # any validation failures must have come from the update - if validate: - self.validate(dct) - return self.from_dict(dct) - def finalize_and_validate(self: T) -> T: dct = self.to_dict(omit_none=False) self.validate(dct) diff --git a/dbt_common/dataclass_schema.py b/dbt_common/dataclass_schema.py index 0aad4d5..1c5b515 100644 --- a/dbt_common/dataclass_schema.py +++ b/dbt_common/dataclass_schema.py @@ -15,11 +15,15 @@ from mashumaro.jsonschema import build_json_schema # following includes DataClassDictMixin -from mashumaro.mixins.msgpack import DataClassMessagePackMixin +from mashumaro.mixins.msgpack import DataClassMessagePackMixin, MessagePackDialect import functools +class FixedMessagePackDialect(MessagePackDialect): + no_copy_collections = () + + class ValidationError(jsonschema.ValidationError): pass @@ -49,6 +53,7 @@ class dbtMashConfig(MashBaseConfig): } serialize_by_alias = True lazy_compilation = True + dialect = FixedMessagePackDialect # This class pulls in DataClassDictMixin from Mashumaro. The 'to_dict' diff --git a/pyproject.toml b/pyproject.toml index 5fa7fdd..42dfa22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "isodate>=0.6,<0.7", "jsonschema>=4.0,<5.0", "Jinja2>=3.1.3,<4", - "mashumaro[msgpack]>=3.9,<4.0", + "mashumaro[msgpack]>=3.15,<4.0", "pathspec>=0.9,<0.13", "protobuf>=5.0,<6.0", "python-dateutil>=2.0,<3.0", diff --git a/tests/unit/test_model_config.py b/tests/unit/test_model_config.py index 9d1de1b..d914213 100644 --- a/tests/unit/test_model_config.py +++ b/tests/unit/test_model_config.py @@ -90,7 +90,10 @@ def test_update_from() -> None: "grants": {"two": "fine", "+one": "some"}, "snapshot_table_column_names": {"first_column": "dbt_ack", "second_column": "dbt_more"}, } - updated_obj = initial_obj.update_from(update_dct.copy(), SubstituteAdapterConfig) + updated_dict = initial_obj.update_from( + initial_obj.to_dict(), update_dct.copy(), SubstituteAdapterConfig + ) + updated_obj = ThingWithMergeBehavior.from_dict(updated_dict) assert updated_obj.default_behavior == 3 assert updated_obj.tags == ["one", "two", "five"]