diff --git a/.changes/unreleased/Under the Hood-20220503-195212.yaml b/.changes/unreleased/Under the Hood-20220503-195212.yaml new file mode 100644 index 00000000000..63207fb5a15 --- /dev/null +++ b/.changes/unreleased/Under the Hood-20220503-195212.yaml @@ -0,0 +1,7 @@ +kind: Under the Hood +body: 'Fix: Call str and repr for UnsetProfileConfig without a RuntimeException' +time: 2022-05-03T19:52:12.793729384+02:00 +custom: + Author: tomasfarias + Issue: "5081" + PR: "5209" diff --git a/core/dbt/config/runtime.py b/core/dbt/config/runtime.py index 1a2bba178b3..8cb1f71bb5b 100644 --- a/core/dbt/config/runtime.py +++ b/core/dbt/config/runtime.py @@ -1,7 +1,7 @@ import itertools import os from copy import deepcopy -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from typing import Dict, Any, Optional, Mapping, Iterator, Iterable, Tuple, List, MutableSet, Type @@ -417,6 +417,9 @@ class UnsetProfileConfig(RuntimeConfig): missing, any access to profile members results in an exception. """ + profile_name: str = field(repr=False) + target_name: str = field(repr=False) + def __post_init__(self): # instead of futzing with InitVar overrides or rewriting __init__, just # `del` the attrs we don't want users touching. @@ -437,6 +440,56 @@ def to_target_dict(self): # re-override the poisoned profile behavior return DictDefaultEmptyStr({}) + def to_project_config(self, with_packages=False): + """Return a dict representation of the config that could be written to + disk with `yaml.safe_dump` to get this configuration. + + Overrides dbt.config.Project.to_project_config to omit undefined profile + attributes. + + :param with_packages bool: If True, include the serialized packages + file in the root. + :returns dict: The serialized profile. + """ + result = deepcopy( + { + "name": self.project_name, + "version": self.version, + "project-root": self.project_root, + "profile": "", + "model-paths": self.model_paths, + "macro-paths": self.macro_paths, + "seed-paths": self.seed_paths, + "test-paths": self.test_paths, + "analysis-paths": self.analysis_paths, + "docs-paths": self.docs_paths, + "asset-paths": self.asset_paths, + "target-path": self.target_path, + "snapshot-paths": self.snapshot_paths, + "clean-targets": self.clean_targets, + "log-path": self.log_path, + "quoting": self.quoting, + "models": self.models, + "on-run-start": self.on_run_start, + "on-run-end": self.on_run_end, + "dispatch": self.dispatch, + "seeds": self.seeds, + "snapshots": self.snapshots, + "sources": self.sources, + "tests": self.tests, + "vars": self.vars.to_dict(), + "require-dbt-version": [v.to_version_string() for v in self.dbt_version], + "config-version": self.config_version, + } + ) + if self.query_comment: + result["query-comment"] = self.query_comment.to_dict(omit_none=True) + + if with_packages: + result.update(self.packages.to_dict(omit_none=True)) + + return result + @classmethod def from_parts( cls, diff --git a/test/unit/test_config.py b/test/unit/test_config.py index eb92c8d54e2..8ca8238a7d0 100644 --- a/test/unit/test_config.py +++ b/test/unit/test_config.py @@ -317,7 +317,7 @@ def test_extra_path(self): 'model-paths': ['models'], 'source-paths': ['other-models'], }) - with self.assertRaises(dbt.exceptions.DbtProjectError) as exc: + with self.assertRaises(dbt.exceptions.DbtProjectError) as exc: project = project_from_config_norender(self.default_project_data) self.assertIn('source-paths and model-paths', str(exc.exception)) @@ -1230,6 +1230,45 @@ def test_from_args(self): self.assertEqual(config.project_name, 'my_test_project') +class TestUnsetProfileConfig(BaseConfigTest): + def setUp(self): + self.profiles_dir = '/invalid-profiles-path' + self.project_dir = '/invalid-root-path' + super().setUp() + self.default_project_data['project-root'] = self.project_dir + + def get_project(self): + return project_from_config_norender(self.default_project_data, verify_version=self.args.version_check) + + def get_profile(self): + renderer = empty_profile_renderer() + return dbt.config.Profile.from_raw_profiles( + self.default_profile_data, self.default_project_data['profile'], renderer + ) + + def test_str(self): + project = self.get_project() + profile = self.get_profile() + config = dbt.config.UnsetProfileConfig.from_parts(project, profile, {}) + + str(config) + + def test_repr(self): + project = self.get_project() + profile = self.get_profile() + config = dbt.config.UnsetProfileConfig.from_parts(project, profile, {}) + + repr(config) + + def test_to_project_config(self): + project = self.get_project() + profile = self.get_profile() + config = dbt.config.UnsetProfileConfig.from_parts(project, profile, {}) + project_config = config.to_project_config() + + self.assertEqual(project_config["profile"], "") + + class TestVariableRuntimeConfigFiles(BaseFileTest): def setUp(self): super().setUp()