Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add secondary_profiles to profile.py #11308

Merged
merged 9 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Under the Hood-20250214-123853.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Under the Hood
body: Add secondary profiles to profile.py
time: 2025-02-14T12:38:53.964266Z
custom:
Author: aranke
Issue: XPLAT-241
48 changes: 47 additions & 1 deletion core/dbt/config/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class Profile(HasCredentials):
credentials: Credentials
profile_env_vars: Dict[str, Any]
log_cache_events: bool
secondary_profiles: Dict[str, "Profile"]

def __init__(
self,
Expand All @@ -79,6 +80,7 @@ def __init__(
self.log_cache_events = (
get_flags().LOG_CACHE_EVENTS
) # never available on init, set for adapter instantiation via AdapterRequiredConfig
self.secondary_profiles = {}

def to_profile_info(self, serialize_credentials: bool = False) -> Dict[str, Any]:
"""Unlike to_project_config, this dict is not a mirror of any existing
Expand Down Expand Up @@ -257,6 +259,7 @@ def render_profile(
profile_name: str,
target_override: Optional[str],
renderer: ProfileRenderer,
infer_target_name: bool = False,
) -> Tuple[str, Dict[str, Any]]:
"""This is a containment zone for the hateful way we're rendering
profiles.
Expand All @@ -273,6 +276,9 @@ def render_profile(
elif "target" in raw_profile:
# render the target if it was parsed from yaml
target_name = renderer.render_value(raw_profile["target"])
elif infer_target_name and len(raw_profile["outputs"]) == 1:
target_name = next(iter(raw_profile["outputs"]))
fire_event(MissingProfileTarget(profile_name=profile_name, target_name=target_name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this event necessary in this case?

Copy link
Member Author

@aranke aranke Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event name is slightly misleading, but the message is useful for debugging purposes to indicate that we inferred a target_name:

class MissingProfileTarget(InfoLevel):
def code(self) -> str:
return "A005"
def message(self) -> str:
return f"target not specified in profile '{self.profile_name}', using '{self.target_name}'"

I'll add a comment for the same.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment here, let me know if any other changes are needed here.

else:
target_name = "default"
fire_event(MissingProfileTarget(profile_name=profile_name, target_name=target_name))
Expand Down Expand Up @@ -325,13 +331,53 @@ def from_raw_profile_info(
profile_data, profile_name, target_name
)

return cls.from_credentials(
profile = cls.from_credentials(
credentials=credentials,
profile_name=profile_name,
target_name=target_name,
threads=threads,
)

raw_secondary_profiles = profile_data.pop("secondary_profiles", [])

if raw_secondary_profiles:
for p in raw_secondary_profiles:
for secondary_profile_name, secondary_raw_profile in p.items():
if secondary_profile_name in profile.secondary_profiles:
raise DbtProfileError(
f"Secondary profile '{secondary_profile_name}' is already defined"
)

secondary_target_name, secondary_profile_data = cls.render_profile(
secondary_raw_profile,
secondary_profile_name,
target_override,
renderer,
infer_target_name=True,
)

if secondary_profile_data.get("secondary_profiles"):
raise DbtProfileError(
f"Secondary profile '{secondary_profile_name}' cannot have nested secondary profiles"
)

secondary_threads = secondary_profile_data.pop("threads", DEFAULT_THREADS)
if threads_override is not None:
secondary_threads = threads_override

secondary_credentials: Credentials = cls._credentials_from_profile(
secondary_profile_data, secondary_profile_name, secondary_target_name
)

profile.secondary_profiles[secondary_profile_name] = cls.from_credentials(
credentials=secondary_credentials,
profile_name=secondary_profile_name,
target_name=secondary_target_name,
threads=secondary_threads,
)

return profile

@classmethod
def from_raw_profiles(
cls,
Expand Down
1 change: 1 addition & 0 deletions core/dbt/config/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def from_parts(
project_env_vars=project.project_env_vars,
restrict_access=project.restrict_access,
profile_env_vars=profile.profile_env_vars,
secondary_profiles=profile.secondary_profiles,
profile_name=profile.profile_name,
target_name=profile.target_name,
threads=profile.threads,
Expand Down
Loading
Loading