diff --git a/.changes/unreleased/Under the Hood-20240122-163546.yaml b/.changes/unreleased/Under the Hood-20240122-163546.yaml new file mode 100644 index 00000000..0d32e2e3 --- /dev/null +++ b/.changes/unreleased/Under the Hood-20240122-163546.yaml @@ -0,0 +1,6 @@ +kind: Under the Hood +body: Clean up macro contexts. +time: 2024-01-22T16:35:46.907999-05:00 +custom: + Author: peterallenwebb + Issue: "35" diff --git a/.github/ISSUE_TEMPLATE/implementation-ticket.yml b/.github/ISSUE_TEMPLATE/implementation-ticket.yml index 9bc709e9..18b24046 100644 --- a/.github/ISSUE_TEMPLATE/implementation-ticket.yml +++ b/.github/ISSUE_TEMPLATE/implementation-ticket.yml @@ -30,6 +30,16 @@ body: What is the definition of done for this ticket? Include any relevant edge cases and/or test cases validations: required: true + - type: textarea + attributes: + label: Suggested Tests + description: | + Provide scenarios to test. Link to existing similar tests if appropriate. + placeholder: | + 1. Test with no version specified in the schema file and use selection logic on a versioned model for a specific version. Expect pass. + 2. Test with a version specified in the schema file that is no valid. Expect ParsingError. + validations: + required: true - type: textarea attributes: label: Impact to Other Teams diff --git a/.github/ISSUE_TEMPLATE/slash-command-template.md b/.github/ISSUE_TEMPLATE/slash-command-template.md new file mode 100644 index 00000000..059b2498 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/slash-command-template.md @@ -0,0 +1,23 @@ +## Short description + + + +## Acceptance criteria + + + +## Suggested Tests + + + +## Impact to Other Teams + + + +## Will backports be required? + + + +## Context + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c3b8366b..035240f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,10 +38,10 @@ jobs: steps: - name: Check out the repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' diff --git a/.github/workflows/ci_code_quality.yml b/.github/workflows/ci_code_quality.yml index fb7c77ab..50694261 100644 --- a/.github/workflows/ci_code_quality.yml +++ b/.github/workflows/ci_code_quality.yml @@ -38,10 +38,10 @@ jobs: steps: - name: Check out the repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index c30cc327..faba96fa 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -43,10 +43,10 @@ jobs: steps: - name: "Check out the repository" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Set up Python ${{ matrix.python-version }}" - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/dbt_common/clients/jinja.py b/dbt_common/clients/jinja.py index a35984d7..264d1cf5 100644 --- a/dbt_common/clients/jinja.py +++ b/dbt_common/clients/jinja.py @@ -3,9 +3,10 @@ import os import tempfile from ast import literal_eval +from collections import ChainMap from contextlib import contextmanager from itertools import chain, islice -from typing import List, Union, Set, Optional, Dict, Any, Iterator, Type, Callable +from typing import Any, Callable, Dict, Iterator, List, Mapping, Optional, Union, Set, Type from typing_extensions import Protocol import jinja2 # type: ignore @@ -101,6 +102,41 @@ def _compile(self, source, filename): return super()._compile(source, filename) # type: ignore +class MacroFuzzTemplate(jinja2.nativetypes.NativeTemplate): + environment_class = MacroFuzzEnvironment + + def new_context( + self, + vars: Optional[Dict[str, Any]] = None, + shared: bool = False, + locals: Optional[Mapping[str, Any]] = None, + ) -> jinja2.runtime.Context: + # This custom override makes the assumption that the locals and shared + # parameters are not used, so enforce that. + if shared or locals: + raise Exception("The MacroFuzzTemplate.new_context() override cannot use the shared or locals parameters.") + + parent = ChainMap(vars, self.globals) if self.globals else vars + + return self.environment.context_class(self.environment, parent, self.name, self.blocks) + + def render(self, *args: Any, **kwargs: Any) -> Any: + if kwargs or len(args) != 1: + raise Exception("The MacroFuzzTemplate.render() override requires exactly one argument.") + + ctx = self.new_context(args[0]) + + try: + return self.environment_class.concat( # type: ignore + self.root_render_func(ctx) # type: ignore + ) + except Exception: + return self.environment.handle_exception() + + +MacroFuzzEnvironment.template_class = MacroFuzzTemplate + + class NativeSandboxEnvironment(MacroFuzzEnvironment): code_generator_class = jinja2.nativetypes.NativeCodeGenerator @@ -174,7 +210,7 @@ def render(self, *args, **kwargs): with :func:`ast.literal_eval`, the parsed value is returned. Otherwise, the string is returned. """ - vars = dict(*args, **kwargs) + vars = args[0] try: return quoted_native_concat(self.root_render_func(self.new_context(vars))) @@ -229,7 +265,7 @@ def get_macro(self): # make_module is in jinja2.environment. It returns a TemplateModule module = template.make_module(vars=self.context, shared=False) macro = module.__dict__[get_dbt_macro_name(name)] - module.__dict__.update(self.context) + return macro @contextmanager