Skip to content

Commit 949c0eb

Browse files
authored
Handle warnings from dbt ls commands (Issue #12) (#15)
* Handle warnings from dbt ls commands Signed-off-by: Robert Astel <[email protected]> * Reformat with Black Signed-off-by: Robert Astel <[email protected]> * Make json default for dbt ls, do not rerun dbt ls unnecessarily Signed-off-by: Robert Astel <[email protected]>
1 parent 3819164 commit 949c0eb

File tree

5 files changed

+66
-37
lines changed

5 files changed

+66
-37
lines changed

dbt_invoke/internal/_utils.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import platform
66

77
import yaml
8-
98
from dbt.task.base import get_nearest_project_dir
109

1110
MACROS = {
@@ -21,6 +20,9 @@
2120
"\n{% endmacro %}\n"
2221
)
2322
}
23+
DBT_GLOBAL_ARGS = {
24+
'log-format': 'json',
25+
}
2426
DBT_LS_ARG_HELP = (
2527
'An argument for listing dbt resources (run "dbt ls --help" for details)'
2628
)
@@ -126,7 +128,7 @@ def dbt_ls(
126128
ctx,
127129
supported_resource_types=None,
128130
hide=True,
129-
output='path',
131+
output='json',
130132
logger=None,
131133
**kwargs,
132134
):
@@ -161,16 +163,36 @@ def dbt_ls(
161163
default_arguments.append(f'{get_cli_kwargs(resource_type=rt)}')
162164
default_arguments = ' '.join(default_arguments)
163165
arguments = get_cli_kwargs(**kwargs)
164-
all_arguments = f'{default_arguments} {arguments} --output {output}'
165-
command = f"dbt ls {all_arguments}"
166+
dbt_command_cli_args = f'{default_arguments} {arguments} --output {output}'
167+
dbt_global_cli_args = get_cli_kwargs(**DBT_GLOBAL_ARGS)
168+
command = f"dbt {dbt_global_cli_args} ls {dbt_command_cli_args}"
166169
logger.debug(f'Running command: {command}')
167170
result = ctx.run(command, hide=hide)
168171
result_lines = result.stdout.splitlines()
169-
if output == 'json':
170-
result_lines = [
171-
json.loads(result_json) for result_json in result_lines
172-
]
173-
return result_lines
172+
result_lines_filtered = list()
173+
for line in result_lines:
174+
# Because we set the dbt global arg "--log-format json", if
175+
# line is valid json then it may be an actual result or it
176+
# may be some other output from dbt, like a warning.
177+
try:
178+
line_dict = json.loads(line)
179+
# If line is not valid json, then it should be an actual
180+
# result. This is because even when the "dbt ls" command
181+
# arg "--output" is not set to json, non-result logs will
182+
# still be in json format (due to the dbt global arg
183+
# "--log-format json").
184+
except ValueError:
185+
result_lines_filtered.append(line)
186+
continue
187+
# If 'resource_type' is in line_dict, then this is likely
188+
# an actual result and not something else like a warning.
189+
if 'resource_type' in line_dict:
190+
result_lines_filtered.append(line_dict)
191+
# Else, if 'resource_type' is not in line_dict, this may be
192+
# a warning from dbt, so log it.
193+
else:
194+
logger.warning(f'Extra output from "dbt ls" command: {line}')
195+
return result_lines_filtered
174196

175197

176198
def get_cli_kwargs(**kwargs):
@@ -227,19 +249,16 @@ def dbt_run_operation(
227249
"""
228250
if not logger:
229251
logger = get_logger('')
230-
dbt_kwargs = {
252+
dbt_command_args = {
231253
'project_dir': project_dir or ctx.config['project_path'],
232254
'profiles_dir': profiles_dir,
233255
'profile': profile,
234256
'target': target,
235257
'vars': vars,
236258
'bypass_cache': bypass_cache,
237259
}
238-
dbt_cli_kwargs = get_cli_kwargs(**dbt_kwargs)
239-
240-
dbt_global_kwargs = {'log-format': 'json'}
241-
dbt_global_cli_kwargs = get_cli_kwargs(**dbt_global_kwargs)
242-
260+
dbt_command_cli_args = get_cli_kwargs(**dbt_command_args)
261+
dbt_global_cli_args = get_cli_kwargs(**DBT_GLOBAL_ARGS)
243262
macro_kwargs = json.dumps(kwargs, sort_keys=False)
244263
if platform.system().lower().startswith('win'):
245264
# Format YAML string for Windows Command Prompt
@@ -253,7 +272,7 @@ def dbt_run_operation(
253272
macro_kwargs = macro_kwargs.replace("'", """'"'"'""")
254273
macro_kwargs = f"'{macro_kwargs}'"
255274
command = (
256-
f"dbt {dbt_global_cli_kwargs} run-operation {dbt_cli_kwargs}"
275+
f"dbt {dbt_global_cli_args} run-operation {dbt_command_cli_args}"
257276
f" {macro_name} --args {macro_kwargs}"
258277
)
259278
logger.debug(f'Running command: {command}')

dbt_invoke/internal/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.3'
1+
__version__ = '0.1.4'

dbt_invoke/properties.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import traceback
33
from concurrent.futures import ThreadPoolExecutor, as_completed
44
from pathlib import Path
5+
import ast
56

67
from invoke import task
78

89
from dbt_invoke.internal import _utils
9-
import ast
1010

1111
_LOGGER = _utils.get_logger('dbt-invoke')
1212
_MACRO_NAME = '_log_columns_list'
@@ -258,27 +258,37 @@ def _transform_ls_results(ctx, **kwargs):
258258
"""
259259
# Run dbt ls to retrieve resource path and json information
260260
_LOGGER.info('Searching for matching resources...')
261-
result_lines_path = _utils.dbt_ls(
262-
ctx,
263-
supported_resource_types=_SUPPORTED_RESOURCE_TYPES,
264-
logger=_LOGGER,
265-
**kwargs,
266-
)
267-
result_lines_dict = _utils.dbt_ls(
261+
potential_results = _utils.dbt_ls(
268262
ctx,
269263
supported_resource_types=_SUPPORTED_RESOURCE_TYPES,
270264
logger=_LOGGER,
271265
output='json',
272266
**kwargs,
273267
)
274-
results = dict(zip(result_lines_path, result_lines_dict))
275-
# Filter dictionary for existing files and supported resource types
276-
results = {
277-
k: v
278-
for k, v in results.items()
279-
if v['resource_type'] in _SUPPORTED_RESOURCE_TYPES
280-
and Path(ctx.config['project_path'], k).exists()
281-
}
268+
potential_result_paths = None
269+
results = dict()
270+
for i, potential_result in enumerate(potential_results):
271+
if 'original_file_path' in potential_result:
272+
potential_result_path = potential_result['original_file_path']
273+
# Before dbt version 0.20.0, original_file_path was not
274+
# included in the json response of "dbt ls". For older
275+
# versions of dbt, we need to run "dbt ls" with the
276+
# "--output path" argument in order to retrieve paths
277+
else:
278+
if potential_result_paths is None:
279+
potential_result_paths = _utils.dbt_ls(
280+
ctx,
281+
supported_resource_types=_SUPPORTED_RESOURCE_TYPES,
282+
logger=_LOGGER,
283+
output='path',
284+
**kwargs,
285+
)
286+
assert len(potential_result_paths) == len(
287+
potential_results
288+
), 'Length of results differs from length of result details'
289+
potential_result_path = potential_result_paths[i]
290+
if Path(ctx.config['project_path'], potential_result_path).exists():
291+
results[potential_result_path] = potential_result
282292
_LOGGER.info(
283293
f"Found {len(results)} matching resources in dbt project"
284294
f' "{ctx.config["project_name"]}"'

tests/test.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
import unittest
33
from pathlib import Path
44
from unittest.mock import patch
5+
import sys
6+
import pkg_resources
7+
import shutil
58

69
import invoke
7-
import sys
810

911
from dbt_invoke import properties
1012
from dbt_invoke.internal import _utils
1113

12-
import pkg_resources
13-
import shutil
14-
1514
PARENT_DIR = Path(__file__).parent
1615

1716

tests/test_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def test_dbt_ls(self):
4242
project_dir=self.project_dir,
4343
profiles_dir=self.profiles_dir,
4444
supported_resource_types=SUPPORTED_RESOURCE_TYPES,
45+
output='path',
4546
logger=self.logger,
4647
**dbt_ls_kwargs,
4748
)

0 commit comments

Comments
 (0)