diff --git a/.changes/unreleased/Fixes-20230919-140514.yaml b/.changes/unreleased/Fixes-20230919-140514.yaml new file mode 100644 index 00000000000..7990e1fabf8 --- /dev/null +++ b/.changes/unreleased/Fixes-20230919-140514.yaml @@ -0,0 +1,6 @@ +kind: Fixes +body: Support global flags passed in after subcommands +time: 2023-09-19T14:05:14.600303+01:00 +custom: + Author: aranke + Issue: "6497" diff --git a/core/dbt/cli/main.py b/core/dbt/cli/main.py index 773362ebf40..2af95c4754d 100644 --- a/core/dbt/cli/main.py +++ b/core/dbt/cli/main.py @@ -1,3 +1,4 @@ +import functools from copy import copy from dataclasses import dataclass from typing import Callable, List, Optional, Union @@ -118,6 +119,42 @@ def invoke(self, args: List[str], **kwargs) -> dbtRunnerResult: ) +# approach from https://github.com/pallets/click/issues/108#issuecomment-280489786 +def global_flags(func): + @p.cache_selected_only + @p.debug + @p.deprecated_print + @p.enable_legacy_logger + @p.fail_fast + @p.log_cache_events + @p.log_file_max_bytes + @p.log_format_file + @p.log_level + @p.log_level_file + @p.log_path + @p.macro_debugging + @p.partial_parse + @p.populate_cache + @p.print + @p.printer_width + @p.quiet + @p.record_timing_info + @p.send_anonymous_usage_stats + @p.single_threaded + @p.static_parser + @p.use_colors + @p.use_colors_file + @p.use_experimental_parser + @p.version + @p.version_check + @p.write_json + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + + return wrapper + + # dbt @click.group( context_settings={"help_option_names": ["-h", "--help"]}, @@ -126,36 +163,10 @@ def invoke(self, args: List[str], **kwargs) -> dbtRunnerResult: epilog="Specify one of these sub-commands and you can find more help from there.", ) @click.pass_context -@p.cache_selected_only -@p.debug -@p.deprecated_print -@p.enable_legacy_logger -@p.fail_fast -@p.log_cache_events -@p.log_file_max_bytes -@p.log_format -@p.log_format_file -@p.log_level -@p.log_level_file -@p.log_path -@p.macro_debugging -@p.partial_parse -@p.populate_cache -@p.print -@p.printer_width -@p.quiet -@p.record_timing_info -@p.send_anonymous_usage_stats -@p.single_threaded -@p.static_parser -@p.use_colors -@p.use_colors_file -@p.use_experimental_parser -@p.version -@p.version_check +@global_flags @p.warn_error @p.warn_error_options -@p.write_json +@p.log_format def cli(ctx, **kwargs): """An ELT tool for managing your SQL transformations and data models. For more documentation on these commands, visit: docs.getdbt.com @@ -165,10 +176,10 @@ def cli(ctx, **kwargs): # dbt build @cli.command("build") @click.pass_context +@global_flags @p.defer @p.deprecated_defer @p.exclude -@p.fail_fast @p.favor_state @p.deprecated_favor_state @p.full_refresh @@ -187,7 +198,6 @@ def cli(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -210,6 +220,7 @@ def build(ctx, **kwargs): # dbt clean @cli.command("clean") @click.pass_context +@global_flags @p.profile @p.profiles_dir @p.project_dir @@ -232,6 +243,7 @@ def clean(ctx, **kwargs): # dbt docs @cli.group() @click.pass_context +@global_flags def docs(ctx, **kwargs): """Generate or serve the documentation website for your project""" @@ -239,6 +251,7 @@ def docs(ctx, **kwargs): # dbt docs generate @docs.command("generate") @click.pass_context +@global_flags @p.compile_docs @p.defer @p.deprecated_defer @@ -256,7 +269,6 @@ def docs(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -279,6 +291,7 @@ def docs_generate(ctx, **kwargs): # dbt docs serve @docs.command("serve") @click.pass_context +@global_flags @p.browser @p.port @p.profile @@ -307,6 +320,7 @@ def docs_serve(ctx, **kwargs): # dbt compile @cli.command("compile") @click.pass_context +@global_flags @p.defer @p.deprecated_defer @p.exclude @@ -328,7 +342,6 @@ def docs_serve(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -352,6 +365,7 @@ def compile(ctx, **kwargs): # dbt show @cli.command("show") @click.pass_context +@global_flags @p.defer @p.deprecated_defer @p.exclude @@ -374,7 +388,6 @@ def compile(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -398,13 +411,13 @@ def show(ctx, **kwargs): # dbt debug @cli.command("debug") @click.pass_context +@global_flags @p.config_dir @p.profile @p.profiles_dir_exists_false @p.project_dir @p.target @p.vars -@p.version_check @requires.postflight @requires.preflight def debug(ctx, **kwargs): @@ -423,6 +436,7 @@ def debug(ctx, **kwargs): # dbt deps @cli.command("deps") @click.pass_context +@global_flags @p.profile @p.profiles_dir_exists_false @p.project_dir @@ -443,6 +457,7 @@ def deps(ctx, **kwargs): # dbt init @cli.command("init") @click.pass_context +@global_flags # for backwards compatibility, accept 'project_name' as an optional positional argument @click.argument("project_name", required=False) @p.profile @@ -465,6 +480,7 @@ def init(ctx, **kwargs): # dbt list @cli.command("list") @click.pass_context +@global_flags @p.exclude @p.indirect_selection @p.models @@ -509,6 +525,7 @@ def list(ctx, **kwargs): # dbt parse @cli.command("parse") @click.pass_context +@global_flags @p.profile @p.profiles_dir @p.project_dir @@ -516,7 +533,6 @@ def list(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -533,12 +549,12 @@ def parse(ctx, **kwargs): # dbt run @cli.command("run") @click.pass_context +@global_flags @p.defer @p.deprecated_defer @p.favor_state @p.deprecated_favor_state @p.exclude -@p.fail_fast @p.full_refresh @p.profile @p.profiles_dir @@ -551,7 +567,6 @@ def parse(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -574,6 +589,7 @@ def run(ctx, **kwargs): # dbt run operation @cli.command("run-operation") @click.pass_context +@global_flags @click.argument("macro") @p.args @p.profile @@ -604,6 +620,7 @@ def run_operation(ctx, **kwargs): # dbt seed @cli.command("seed") @click.pass_context +@global_flags @p.exclude @p.full_refresh @p.profile @@ -618,7 +635,6 @@ def run_operation(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile @@ -640,6 +656,7 @@ def seed(ctx, **kwargs): # dbt snapshot @cli.command("snapshot") @click.pass_context +@global_flags @p.defer @p.deprecated_defer @p.exclude @@ -678,6 +695,7 @@ def snapshot(ctx, **kwargs): # dbt source @cli.group() @click.pass_context +@global_flags def source(ctx, **kwargs): """Manage your project's sources""" @@ -685,6 +703,7 @@ def source(ctx, **kwargs): # dbt source freshness @source.command("freshness") @click.pass_context +@global_flags @p.exclude @p.output_path # TODO: Is this ok to re-use? We have three different output params, how much can we consolidate? @p.profile @@ -726,10 +745,10 @@ def freshness(ctx, **kwargs): # dbt test @cli.command("test") @click.pass_context +@global_flags @p.defer @p.deprecated_defer @p.exclude -@p.fail_fast @p.favor_state @p.deprecated_favor_state @p.indirect_selection @@ -745,7 +764,6 @@ def freshness(ctx, **kwargs): @p.target_path @p.threads @p.vars -@p.version_check @requires.postflight @requires.preflight @requires.profile diff --git a/tests/unit/test_cli_flags.py b/tests/unit/test_cli_flags.py index 12ffafe5be5..101e669e7a4 100644 --- a/tests/unit/test_cli_flags.py +++ b/tests/unit/test_cli_flags.py @@ -1,10 +1,10 @@ -import pytest - -import click from multiprocessing import get_context from pathlib import Path from typing import List, Optional +import click +import pytest + from dbt.cli.exceptions import DbtUsageException from dbt.cli.main import cli from dbt.contracts.project import UserConfig @@ -352,3 +352,16 @@ def test_duplicate_flags_raises_error(self): with pytest.raises(DbtUsageException): Flags(context) + + def test_global_flag_at_child_context(self): + parent_context_a = self.make_dbt_context("parent_context_a", ["--no-use-colors"]) + child_context_a = self.make_dbt_context("child_context_a", ["run"], parent_context_a) + flags_a = Flags(child_context_a) + + parent_context_b = self.make_dbt_context("parent_context_b", ["run"]) + child_context_b = self.make_dbt_context( + "child_context_b", ["--no-use-colors"], parent_context_b + ) + flags_b = Flags(child_context_b) + + assert flags_a.USE_COLORS == flags_b.USE_COLORS