Skip to content

Commit

Permalink
✨ Add ability to handle long file extensions for yaml
Browse files Browse the repository at this point in the history
Add new `get_yaml_path` helper to allow handling of both yml and yaml
file extension for dbt project config files (currently `profiles` and
`dbt_project` files).

Small number of failing tests are due to hardcoded references to short
file extension in tests which expected an error to be raised.
  • Loading branch information
morgan-dgk committed Jan 31, 2025
1 parent 2e938d7 commit 5b57156
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20250131-164002.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Support both yaml and yml file extension for project config files
time: 2025-01-31T16:40:02.049453+11:00
custom:
Author: morgan-dgk
Issue: '#5002 #8738'
10 changes: 10 additions & 0 deletions core/dbt/clients/yaml_helper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from typing import Any, Dict, Optional

import yaml
Expand Down Expand Up @@ -66,3 +67,12 @@ def load_yaml_text(contents, path=None):
error = str(e)

raise dbt_common.exceptions.base.DbtValidationError(error)


def get_yaml_path(path: str) -> str:
if os.path.exists("{}.yml".format(path)):
return path + ".yml"
elif os.path.exists("{}.yaml".format(path)):
return path + ".yaml"
else:
return ""
4 changes: 2 additions & 2 deletions core/dbt/config/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Any, Dict, Optional, Tuple

from dbt.adapters.contracts.connection import Credentials, HasCredentials
from dbt.clients.yaml_helper import load_yaml_text
from dbt.clients.yaml_helper import get_yaml_path, load_yaml_text
from dbt.contracts.project import ProfileConfig
from dbt.events.types import MissingProfileTarget
from dbt.exceptions import (
Expand Down Expand Up @@ -31,7 +31,7 @@


def read_profile(profiles_dir: str) -> Dict[str, Any]:
path = os.path.join(profiles_dir, "profiles.yml")
path = get_yaml_path(os.path.join(profiles_dir, "profiles"))

contents = None
if os.path.isfile(path):
Expand Down
14 changes: 8 additions & 6 deletions core/dbt/config/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from dbt import deprecations
from dbt.adapters.contracts.connection import QueryComment
from dbt.clients.yaml_helper import load_yaml_text
from dbt.clients.yaml_helper import get_yaml_path, load_yaml_text
from dbt.config.selectors import SelectorDict
from dbt.config.utils import normalize_warn_error_options
from dbt.constants import (
Expand Down Expand Up @@ -99,8 +99,10 @@ def load_yml_dict(file_path):


def package_and_project_data_from_root(project_root):
packages_yml_dict = load_yml_dict(f"{project_root}/{PACKAGES_FILE_NAME}")
dependencies_yml_dict = load_yml_dict(f"{project_root}/{DEPENDENCIES_FILE_NAME}")
packages_yml_dict = load_yml_dict(get_yaml_path(f"{project_root}/{PACKAGES_FILE_NAME}"))
dependencies_yml_dict = load_yml_dict(
get_yaml_path(f"{project_root}/{DEPENDENCIES_FILE_NAME}")
)

if "packages" in packages_yml_dict and "packages" in dependencies_yml_dict:
msg = "The 'packages' key cannot be specified in both packages.yml and dependencies.yml"
Expand Down Expand Up @@ -184,7 +186,7 @@ def value_or(value: Optional[T], default: T) -> T:

def load_raw_project(project_root: str) -> Dict[str, Any]:
project_root = os.path.normpath(project_root)
project_yaml_filepath = os.path.join(project_root, DBT_PROJECT_FILE_NAME)
project_yaml_filepath = get_yaml_path(os.path.join(project_root, DBT_PROJECT_FILE_NAME))

# get the project.yml contents
if not path_exists(project_yaml_filepath):
Expand All @@ -197,7 +199,7 @@ def load_raw_project(project_root: str) -> Dict[str, Any]:
project_dict = _load_yaml(project_yaml_filepath)

if not isinstance(project_dict, dict):
raise DbtProjectError(f"{DBT_PROJECT_FILE_NAME} does not parse to a dictionary")
raise DbtProjectError(f"{project_yaml_filepath} does not parse to a dictionary")

if "tests" in project_dict and "data_tests" not in project_dict:
project_dict["data_tests"] = project_dict.pop("tests")
Expand Down Expand Up @@ -792,7 +794,7 @@ def read_project_flags(project_dir: str, profiles_dir: str) -> ProjectFlags:
# want to throw an error for non-existence of dbt_project.yml here
# because it breaks things.
project_root = os.path.normpath(project_dir)
project_yaml_filepath = os.path.join(project_root, DBT_PROJECT_FILE_NAME)
project_yaml_filepath = get_yaml_path(os.path.join(project_root, DBT_PROJECT_FILE_NAME))
if path_exists(project_yaml_filepath):
try:
project_dict = load_raw_project(project_root)
Expand Down
4 changes: 2 additions & 2 deletions core/dbt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"https://docs.getdbt.com/docs/package-management#section-specifying-package-versions"
)

DBT_PROJECT_FILE_NAME = "dbt_project.yml"
PACKAGES_FILE_NAME = "packages.yml"
DBT_PROJECT_FILE_NAME = "dbt_project"
PACKAGES_FILE_NAME = "packages"
DEPENDENCIES_FILE_NAME = "dependencies.yml"
PACKAGE_LOCK_FILE_NAME = "package-lock.yml"
MANIFEST_FILE_NAME = "manifest.json"
Expand Down
2 changes: 2 additions & 0 deletions core/dbt/task/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from dbt.artifacts.schemas.run import RunResult
from dbt.cli.flags import Flags
from dbt.clients.yaml_helper import get_yaml_path
from dbt.compilation import Compiler
from dbt.config import RuntimeConfig
from dbt.config.profile import read_profile
Expand Down Expand Up @@ -88,6 +89,7 @@ def get_nearest_project_dir(project_dir: Optional[str]) -> Path:
if project_dir:
cur_dir = Path(project_dir)
project_file = Path(project_dir) / DBT_PROJECT_FILE_NAME
project_file = Path(get_yaml_path(str(project_file)))
if project_file.is_file():
return cur_dir
else:
Expand Down

0 comments on commit 5b57156

Please sign in to comment.