Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a special as_text filter to the native env (#2384) #2395

Merged
merged 3 commits into from
May 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- schema.yml files are now fully rendered in a context that is aware of vars declared in from dbt_project.yml files ([#2269](https://github.com/fishtown-analytics/dbt/issues/2269), [#2357](https://github.com/fishtown-analytics/dbt/pull/2357))
- Sources from dependencies can be overridden in schema.yml files ([#2287](https://github.com/fishtown-analytics/dbt/issues/2287), [#2357](https://github.com/fishtown-analytics/dbt/pull/2357))
- Implement persist_docs for both `relation` and `comments` on postgres and redshift, and extract them when getting the catalog. ([#2333](https://github.com/fishtown-analytics/dbt/issues/2333), [#2378](https://github.com/fishtown-analytics/dbt/pull/2378))
- Added a filter named `as_text` to the native environment rendering code that allows users to mark a value as always being a string ([#2384](https://github.com/fishtown-analytics/dbt/issues/2384), [#2395](https://github.com/fishtown-analytics/dbt/pull/2395))

### Fixes
- When a jinja value is undefined, give a helpful error instead of failing with cryptic "cannot pickle ParserMacroCapture" errors ([#2110](https://github.com/fishtown-analytics/dbt/issues/2110), [#2184](https://github.com/fishtown-analytics/dbt/pull/2184))
Expand Down
34 changes: 26 additions & 8 deletions core/dbt/clients/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from contextlib import contextmanager
from itertools import chain, islice
from typing import (
List, Union, Set, Optional, Dict, Any, Iterator, Type, NoReturn
List, Union, Set, Optional, Dict, Any, Iterator, Type, NoReturn, Tuple
)

import jinja2
Expand Down Expand Up @@ -104,6 +104,13 @@ class NativeSandboxEnvironment(MacroFuzzEnvironment):
code_generator_class = jinja2.nativetypes.NativeCodeGenerator


class TextMarker(str):
"""A special native-env marker that indicates that a value is text and is
not to be evaluated. Use this to prevent your numbery-strings from becoming
numbers!
"""


def quoted_native_concat(nodes):
"""This is almost native_concat from the NativeTemplate, except in the
special case of a single argument that is a quoted string and returns a
Expand All @@ -116,6 +123,8 @@ def quoted_native_concat(nodes):

if len(head) == 1:
raw = head[0]
if isinstance(raw, TextMarker):
return str(raw)
else:
raw = "".join([str(v) for v in chain(head, nodes)])

Expand All @@ -124,10 +133,7 @@ def quoted_native_concat(nodes):
except (ValueError, SyntaxError, MemoryError):
return raw

if len(head) == 1 and len(raw) > 2 and isinstance(result, str):
return _requote_result(raw, result)
else:
return result
return result


class NativeSandboxTemplate(jinja2.nativetypes.NativeTemplate): # mypy: ignore
Expand Down Expand Up @@ -423,12 +429,18 @@ def get_environment(
args['extensions'].append(DocumentationExtension)

env_cls: Type[jinja2.Environment]
text_filter: Type
if native:
env_cls = NativeSandboxEnvironment
text_filter = TextMarker
else:
env_cls = MacroFuzzEnvironment
text_filter = str

env = env_cls(**args)
env.filters['as_text'] = text_filter

return env_cls(**args)
return env


@contextmanager
Expand Down Expand Up @@ -541,9 +553,15 @@ def add_rendered_test_kwargs(
"""
looks_like_func = r'^\s*(env_var|ref|var|source|doc)\s*\(.+\)\s*$'

# we never care about the keypath
def _convert_function(value: Any, _: Any) -> Any:
def _convert_function(
value: Any, keypath: Tuple[Union[str, int], ...]
) -> Any:
if isinstance(value, str):
if keypath == ('column_name',):
# special case: Don't render column names as native, make them
# be strings
return value

if re.match(looks_like_func, value) is not None:
# curly braces to make rendering happy
value = f'{{{{ {value} }}}}'
Expand Down
4 changes: 2 additions & 2 deletions test/integration/042_sources_test/models/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ sources:
tables:
- name: test_table
identifier: source
loaded_at_field: "{{ var('test_loaded_at') }}"
loaded_at_field: "{{ var('test_loaded_at') | as_text }}"
freshness:
error_after: {count: 18, period: hour}
tags:
Expand Down Expand Up @@ -63,7 +63,7 @@ sources:
- id_column
- name: disabled_test_table
freshness: null
loaded_at_field: "{{ var('test_loaded_at') }}"
loaded_at_field: "{{ var('test_loaded_at') | as_text }}"
- name: other_source
schema: "{{ var('test_run_schema') }}"
quoting:
Expand Down
33 changes: 33 additions & 0 deletions test/unit/test_jinja.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest

from dbt.clients.jinja import get_rendered
from dbt.clients.jinja import get_template
from dbt.clients.jinja import extract_toplevel_blocks
from dbt.exceptions import CompilationException
Expand All @@ -13,6 +14,36 @@ def test_do(self):
mod = template.make_module()
self.assertEqual(mod.my_dict, {'a': 1})

def test_regular_render(self):
s = '{{ "some_value" }}'
value = get_rendered(s, {}, native=False)
assert value == 'some_value'
s = '{{ 1991 }}'
value = get_rendered(s, {}, native=False)
assert value == '1991'

s = '{{ "some_value" | as_text }}'
value = get_rendered(s, {}, native=True)
assert value == 'some_value'
s = '{{ 1991 | as_text }}'
value = get_rendered(s, {}, native=True)
assert value == '1991'

def test_native_render(self):
s = '{{ "some_value" }}'
value = get_rendered(s, {}, native=True)
assert value == 'some_value'
s = '{{ 1991 }}'
value = get_rendered(s, {}, native=True)
assert value == 1991

s = '{{ "some_value" | as_text }}'
value = get_rendered(s, {}, native=True)
assert value == 'some_value'
s = '{{ 1991 | as_text }}'
value = get_rendered(s, {}, native=True)
assert value == '1991'


class TestBlockLexer(unittest.TestCase):
def test_basic(self):
Expand Down Expand Up @@ -380,3 +411,5 @@ def test_if_endfor_newlines(self):
hi
{% endmaterialization %}
'''