Skip to content

Commit

Permalink
Partial parsing bug with empty schema file - ensure None is not passe…
Browse files Browse the repository at this point in the history
…d to load_yaml_text (#6494) (#6505)

Co-authored-by: Gerda Shank <[email protected]>
  • Loading branch information
leahwicz and gshank authored Jan 4, 2023
1 parent 6defc86 commit 6e9ff28
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 15 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20230101-223405.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Bug when partial parsing with an empty schema file
time: 2023-01-01T22:34:05.97322-05:00
custom:
Author: gshank
Issue: "4850"
3 changes: 1 addition & 2 deletions core/dbt/context/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,9 +636,8 @@ def flags(self) -> Any:
{% endif %}
This supports all flags defined in flags submodule (core/dbt/flags.py)
TODO: Replace with object that provides read-only access to flag values
"""
return flags
return flags.get_flag_obj()

@contextmember
@staticmethod
Expand Down
39 changes: 29 additions & 10 deletions core/dbt/flags.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
# Do not import the os package because we expose this package in jinja
from os import name as os_name, path as os_path, getcwd as os_getcwd, getenv as os_getenv
import multiprocessing
from argparse import Namespace

if os.name != "nt":
if os_name != "nt":
# https://bugs.python.org/issue41567
import multiprocessing.popen_spawn_posix # type: ignore
from pathlib import Path
Expand All @@ -10,14 +12,14 @@
# PROFILES_DIR must be set before the other flags
# It also gets set in main.py and in set_from_args because the rpc server
# doesn't go through exactly the same main arg processing.
GLOBAL_PROFILES_DIR = os.path.join(os.path.expanduser("~"), ".dbt")
LOCAL_PROFILES_DIR = os.getcwd()
GLOBAL_PROFILES_DIR = os_path.join(os_path.expanduser("~"), ".dbt")
LOCAL_PROFILES_DIR = os_getcwd()
# Use the current working directory if there is a profiles.yml file present there
if os.path.exists(Path(LOCAL_PROFILES_DIR) / Path("profiles.yml")):
if os_path.exists(Path(LOCAL_PROFILES_DIR) / Path("profiles.yml")):
DEFAULT_PROFILES_DIR = LOCAL_PROFILES_DIR
else:
DEFAULT_PROFILES_DIR = GLOBAL_PROFILES_DIR
PROFILES_DIR = os.path.expanduser(os.getenv("DBT_PROFILES_DIR", DEFAULT_PROFILES_DIR))
PROFILES_DIR = os_path.expanduser(os_getenv("DBT_PROFILES_DIR", DEFAULT_PROFILES_DIR))

STRICT_MODE = False # Only here for backwards compatibility
FULL_REFRESH = False # subcommand
Expand Down Expand Up @@ -91,7 +93,7 @@ def env_set_truthy(key: str) -> Optional[str]:
"""Return the value if it was set to a "truthy" string value or None
otherwise.
"""
value = os.getenv(key)
value = os_getenv(key)
if not value or value.lower() in ("0", "false", "f"):
return None
return value
Expand All @@ -104,7 +106,7 @@ def env_set_bool(env_value):


def env_set_path(key: str) -> Optional[Path]:
value = os.getenv(key)
value = os_getenv(key)
if value is None:
return value
else:
Expand Down Expand Up @@ -184,7 +186,7 @@ def get_flag_value(flag, args, user_config):
if flag in ["PRINTER_WIDTH", "EVENT_BUFFER_SIZE"]: # must be ints
flag_value = int(flag_value)
if flag == "PROFILES_DIR":
flag_value = os.path.abspath(flag_value)
flag_value = os_path.abspath(flag_value)

return flag_value

Expand All @@ -208,7 +210,7 @@ def _load_flag_value(flag, args, user_config):
def _get_flag_value_from_env(flag):
# Environment variables use pattern 'DBT_{flag name}'
env_flag = _get_env_flag(flag)
env_value = os.getenv(env_flag)
env_value = os_getenv(env_flag)
if env_value is None or env_value == "":
return None

Expand Down Expand Up @@ -245,4 +247,21 @@ def get_flag_dict():
"event_buffer_size": EVENT_BUFFER_SIZE,
"quiet": QUIET,
"no_print": NO_PRINT,
"cache_selected_only": CACHE_SELECTED_ONLY,
"target_path": TARGET_PATH,
"log_path": LOG_PATH,
}


# This is used by core/dbt/context/base.py to return a flag object
# in Jinja.
def get_flag_obj():
new_flags = Namespace()
for k, v in get_flag_dict().items():
setattr(new_flags, k.upper(), v)
# The following 3 are CLI arguments only so they're not full-fledged flags,
# but we put in flags for users.
setattr(new_flags, "FULL_REFRESH", FULL_REFRESH)
setattr(new_flags, "STORE_FAILURES", STORE_FAILURES)
setattr(new_flags, "WHICH", WHICH)
return new_flags
3 changes: 2 additions & 1 deletion core/dbt/parser/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ def yaml_from_file(source_file: SchemaSourceFile) -> Dict[str, Any]:
"""If loading the yaml fails, raise an exception."""
path = source_file.path.relative_path
try:
return load_yaml_text(source_file.contents, source_file.path)
# source_file.contents can sometimes be None
return load_yaml_text(source_file.contents or "", source_file.path)
except ValidationException as e:
reason = validator_error_message(e)
raise ParsingException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ def test_postgres_skip_macros(self):
# initial run so we have a msgpack file
self.setup_directories()
self.copy_file('test-files/model_one.sql', 'models/model_one.sql')
# use empty_schema file for bug #4850
self.copy_file('test-files/empty_schema.yml', 'models/eschema.yml')
results = self.run_dbt()

# add a new ref override macro
Expand Down
15 changes: 13 additions & 2 deletions tests/functional/context_methods/test_builtin_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,19 @@ def test_builtin_invocation_args_dict_function(self, project):
expected = "invocation_result: {'debug': True, 'log_format': 'json', 'write_json': True, 'use_colors': True, 'printer_width': 80, 'version_check': True, 'partial_parse': True, 'static_parser': True, 'profiles_dir': "
assert expected in str(result)

expected = "'send_anonymous_usage_stats': False, 'event_buffer_size': 100000, 'quiet': False, 'no_print': False, 'macro': 'validate_invocation', 'args': '{my_variable: test_variable}', 'which': 'run-operation', 'rpc_method': 'run-operation', 'indirect_selection': 'eager'}"
assert expected in str(result)
expected = (
"'send_anonymous_usage_stats': False",
"'quiet': False",
"'no_print': False",
"'cache_selected_only': False",
"'macro': 'validate_invocation'",
"'args': '{my_variable: test_variable}'",
"'which': 'run-operation'",
"'rpc_method': 'run-operation'",
"'indirect_selection': 'eager'",
)
for element in expected:
assert element in str(result)

def test_builtin_dbt_metadata_envs_function(self, project, monkeypatch):
envs = {
Expand Down

0 comments on commit 6e9ff28

Please sign in to comment.