Skip to content

Commit

Permalink
remove UnsetProfileConfig
Browse files Browse the repository at this point in the history
  • Loading branch information
MichelleArk committed Jan 7, 2023
1 parent 9bb1250 commit 5b70dee
Show file tree
Hide file tree
Showing 6 changed files with 17 additions and 281 deletions.
5 changes: 1 addition & 4 deletions core/dbt/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,7 @@ def debug(ctx, **kwargs):
@requires.project
def deps(ctx, **kwargs):
"""Pull the most recent version of the dependencies listed in packages.yml"""
flags = ctx.obj["flags"]
project = ctx.obj["project"]

task = DepsTask.from_project(project, flags.VARS)
task = DepsTask(ctx.obj["flags"], ctx.obj["project"])

results = task.run()
success = task.interpret_results(results)
Expand Down
2 changes: 1 addition & 1 deletion core/dbt/config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# all these are just exports, they need "noqa" so flake8 will not complain.
from .profile import Profile, read_user_config # noqa
from .project import Project, IsFQNResource, PartialProject # noqa
from .runtime import RuntimeConfig, UnsetProfileConfig # noqa
from .runtime import RuntimeConfig # noqa
195 changes: 9 additions & 186 deletions core/dbt/config/runtime.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import itertools
import os
from copy import deepcopy
from dataclasses import dataclass, field
from dataclasses import dataclass
from pathlib import Path
from typing import (
Any,
Expand Down Expand Up @@ -415,8 +415,8 @@ def _connection_keys(self):
return ()


# This is used by UnsetProfileConfig, for commands which do
# not require a profile, i.e. dbt deps and clean
# This is used by commands which do not require
# a profile, i.e. dbt deps and clean
class UnsetProfile(Profile):
def __init__(self):
self.credentials = UnsetCredentials()
Expand All @@ -435,189 +435,12 @@ def __getattribute__(self, name):
return Profile.__getattribute__(self, name)


# This class is used by the dbt deps and clean commands, because they don't
# require a functioning profile.
@dataclass
class UnsetProfileConfig(RuntimeConfig):
"""This class acts a lot _like_ a RuntimeConfig, except if your profile is
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.
del self.profile_name
del self.target_name
# don't call super().__post_init__(), as that calls validate(), and
# this object isn't very valid

def __getattribute__(self, name):
# Override __getattribute__ to check that the attribute isn't 'banned'.
if name in {"profile_name", "target_name"}:
raise RuntimeException(f'Error: disallowed attribute "{name}" - no profile!')

# avoid every attribute access triggering infinite recursion
return RuntimeConfig.__getattribute__(self, name)

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,
"metrics": self.metrics,
"exposures": self.exposures,
"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,
project: Project,
profile: Profile,
args: Any,
dependencies: Optional[Mapping[str, "RuntimeConfig"]] = None,
) -> "RuntimeConfig":
"""Instantiate a RuntimeConfig from its components.
:param profile: Ignored.
:param project: A parsed dbt Project.
:param args: The parsed command-line arguments.
:returns RuntimeConfig: The new configuration.
"""
cli_vars: Dict[str, Any] = getattr(args, "vars", {})

return cls(
project_name=project.project_name,
version=project.version,
project_root=project.project_root,
model_paths=project.model_paths,
macro_paths=project.macro_paths,
seed_paths=project.seed_paths,
test_paths=project.test_paths,
analysis_paths=project.analysis_paths,
docs_paths=project.docs_paths,
asset_paths=project.asset_paths,
target_path=project.target_path,
snapshot_paths=project.snapshot_paths,
clean_targets=project.clean_targets,
log_path=project.log_path,
packages_install_path=project.packages_install_path,
quoting=project.quoting, # we never use this anyway.
models=project.models,
on_run_start=project.on_run_start,
on_run_end=project.on_run_end,
dispatch=project.dispatch,
seeds=project.seeds,
snapshots=project.snapshots,
dbt_version=project.dbt_version,
packages=project.packages,
manifest_selectors=project.manifest_selectors,
selectors=project.selectors,
query_comment=project.query_comment,
sources=project.sources,
tests=project.tests,
metrics=project.metrics,
exposures=project.exposures,
vars=project.vars,
config_version=project.config_version,
unrendered=project.unrendered,
project_env_vars=project.project_env_vars,
profile_env_vars=profile.profile_env_vars,
profile_name="",
target_name="",
user_config=UserConfig(),
threads=getattr(args, "threads", 1),
credentials=UnsetCredentials(),
args=args,
cli_vars=cli_vars,
dependencies=dependencies,
)

@classmethod
def get_profile(
cls,
project_root: str,
cli_vars: Dict[str, Any],
args: Any,
) -> Profile:
"""
Moving all logic regarding constructing a complete UnsetProfile to this function
This way we can have a clean load_profile function to call by the new CLI and remove
all logic for UnsetProfile once we migrate to new click CLI
"""

profile = UnsetProfile()
# The profile (for warehouse connection) is not needed, but we want
# to get the UserConfig, which is also in profiles.yml
user_config = read_user_config(project_root)
profile.user_config = user_config
profile_renderer = ProfileRenderer(cli_vars)
profile.profile_env_vars = profile_renderer.ctx_obj.env_vars
return profile

@classmethod
def from_args(cls: Type[RuntimeConfig], args: Any) -> "RuntimeConfig":
"""Given arguments, read in dbt_project.yml from the current directory,
read in packages.yml if it exists, and use them to find the profile to
load.
:param args: The arguments as parsed from the cli.
:raises DbtProjectError: If the project is invalid or missing.
:raises DbtProfileError: If the profile is invalid or missing.
:raises ValidationException: If the cli variables are invalid.
"""
project, profile = cls.collect_parts(args)

return cls.from_parts(project=project, profile=profile, args=args)
UNUSED_RESOURCE_CONFIGURATION_PATH_MESSAGE = """\
Configuration paths exist in your dbt_project.yml file which do not \
apply to any resources.
There are {} unused configuration paths:
{}
"""


def _is_config_used(path, fqns):
Expand Down
3 changes: 1 addition & 2 deletions core/dbt/task/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ def set_log_format(cls):
@classmethod
def from_args(cls, args):
try:
# This is usually RuntimeConfig but will be UnsetProfileConfig
# for the clean or deps tasks
# This is usually RuntimeConfig
config = cls.ConfigType.from_args(args)
except dbt.exceptions.DbtProjectError as exc:
fire_event(DbtProjectError())
Expand Down
54 changes: 5 additions & 49 deletions core/dbt/task/deps.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
from typing import Dict, Any, Optional

from dbt import flags
from typing import Any, Optional

import dbt.utils
import dbt.deprecations
import dbt.exceptions

from dbt.config.profile import read_user_config
from dbt.config.runtime import load_project, UnsetProfile
from dbt.config.renderer import DbtProjectYamlRenderer
from dbt.config.utils import parse_cli_vars
from dbt.deps.base import downloads_directory
from dbt.deps.resolver import resolve_packages
from dbt.deps.registry import RegistryPinnedPackage
Expand All @@ -32,20 +27,13 @@


from dbt.config import Project
from dbt.task.base import NoneConfig


class DepsTask(BaseTask):
ConfigType = NoneConfig

def __init__(
self,
args: Any,
project: Project,
cli_vars: Dict[str, Any],
):
def __init__(self, args: Any, project: Project):
move_to_nearest_project_dir(project.project_root)
super().__init__(args=args, config=None, project=project)
self.cli_vars = cli_vars
self.cli_vars = args.vars

def track_package_install(
self, package_name: str, source_type: str, version: Optional[str]
Expand Down Expand Up @@ -103,36 +91,4 @@ def run(self) -> None:
)
if packages_to_upgrade:
fire_event(EmptyLine())
fire_event(DepsNotifyUpdatesAvailable(packages=ListOfStrings(packages_to_upgrade)))

@classmethod
def _get_unset_profile(cls) -> UnsetProfile:
profile = UnsetProfile()
# The profile (for warehouse connection) is not needed, but we want
# to get the UserConfig, which is also in profiles.yml
user_config = read_user_config(flags.PROFILES_DIR)
profile.user_config = user_config
return profile

@classmethod
def from_args(cls, args):
# deps needs to move to the project directory, as it does put files
# into the modules directory
nearest_project_dir = move_to_nearest_project_dir(args.project_dir)

# N.B. parse_cli_vars is embedded into the param when using click.
# replace this with:
# cli_vars: Dict[str, Any] = getattr(args, "vars", {})
# when this task is refactored for click
cli_vars: Dict[str, Any] = parse_cli_vars(getattr(args, "vars", "{}"))
project_root: str = args.project_dir or nearest_project_dir
profile: UnsetProfile = cls._get_unset_profile()
project = load_project(project_root, args.version_check, profile, cli_vars)

return cls(args, project, cli_vars)

@classmethod
def from_project(cls, project: Project, cli_vars: Dict[str, Any]) -> "DepsTask":
move_to_nearest_project_dir(project.project_root)
# TODO: remove args=None once BaseTask does not require args
return cls(None, project, cli_vars)
fire_event(DepsNotifyUpdatesAvailable(packages=ListOfStrings(packages_to_upgrade)))
39 changes: 0 additions & 39 deletions test/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,45 +1194,6 @@ 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()
Expand Down

0 comments on commit 5b70dee

Please sign in to comment.