diff --git a/core/dbt/adapters/base/impl.py b/core/dbt/adapters/base/impl.py index 1f579fb9ab9..a77094c2301 100644 --- a/core/dbt/adapters/base/impl.py +++ b/core/dbt/adapters/base/impl.py @@ -744,9 +744,16 @@ def execute_macro(self, macro_name, manifest=None, project=None, macro = manifest.find_macro_by_name(macro_name, project) if macro is None: + if project is None: + package_name = 'any package' + else: + package_name = 'the "{}" package'.format(project) + + # The import of dbt.context.runtime below shadows 'dbt' + import dbt.exceptions raise dbt.exceptions.RuntimeException( - 'Could not find macro with name {} in project {}' - .format(macro_name, project) + 'dbt could not find a macro with the name "{}" in {}' + .format(macro_name, package_name) ) # This causes a reference cycle, as dbt.context.runtime.generate() diff --git a/core/dbt/main.py b/core/dbt/main.py index 8a8a001c1a5..7f9f36255e9 100644 --- a/core/dbt/main.py +++ b/core/dbt/main.py @@ -20,6 +20,7 @@ import dbt.task.generate as generate_task import dbt.task.serve as serve_task import dbt.task.freshness as freshness_task +import dbt.task.run_operation as run_operation_task from dbt.adapters.factory import reset_adapters import dbt.tracking @@ -723,6 +724,33 @@ def parse_args(args): _build_test_subparser(subs, base_subparser) _build_source_snapshot_freshness_subparser(source_subs, base_subparser) + sub = subs.add_parser( + 'run-operation', + parents=[base_subparser], + help=""" + (beta) Run the named macro with any supplied arguments. This + subcommand is unstable and subject to change in a future release + of dbt. Please use it with caution""" + ) + sub.add_argument( + '--macro', + required=True, + help=""" + Specify the macro to invoke. dbt will call this macro with the + supplied arguments and then exit""" + ) + sub.add_argument( + '--args', + type=str, + default='{}', + help=""" + Supply arguments to the macro. This dictionary will be mapped + to the keyword arguments defined in the selected macro. This + argument should be a YAML string, eg. '{my_variable: my_value}'""" + ) + sub.set_defaults(cls=run_operation_task.RunOperationTask, + which='run-operation') + if len(args) == 0: p.print_help() sys.exit(1) diff --git a/core/dbt/task/run_operation.py b/core/dbt/task/run_operation.py new file mode 100644 index 00000000000..a2ce4ff5c9b --- /dev/null +++ b/core/dbt/task/run_operation.py @@ -0,0 +1,40 @@ +from dbt.logger import GLOBAL_LOGGER as logger + +from dbt.task.base_task import BaseTask +from dbt.adapters.factory import get_adapter +from dbt.loader import GraphLoader + +import dbt +import dbt.utils +import dbt.exceptions + + +class RunOperationTask(BaseTask): + def _get_macro_parts(self): + macro_name = self.args.macro + if '.' in macro_name: + package_name, macro_name = macro_name.split(".", 1) + else: + package_name = None + + return package_name, macro_name + + def _get_kwargs(self): + return dbt.utils.parse_cli_vars(self.args.args) + + def run(self): + manifest = GraphLoader.load_all(self.config) + adapter = get_adapter(self.config) + + package_name, macro_name = self._get_macro_parts() + macro_kwargs = self._get_kwargs() + + res = adapter.execute_macro( + macro_name, + project=package_name, + kwargs=macro_kwargs, + manifest=manifest, + connection_name="macro_{}".format(macro_name) + ) + + return res