Пример #1
0
    def parse_models_entry(self, model_dict, path, package_name, root_dir):
        model_name = model_dict['name']
        refs = ParserRef()
        for column in model_dict.get('columns', []):
            column_tests = self._parse_column(model_dict, column, package_name,
                                              root_dir, path, refs)
            for node in column_tests:
                yield 'test', node

        for test in model_dict.get('tests', []):
            try:
                node = self.build_test_node(model_dict, package_name, test,
                                            root_dir, path)
            except dbt.exceptions.CompilationException as exc:
                dbt.exceptions.warn_or_error(
                    'in {}: {}'.format(path, exc.msg), test
                )
                continue
            yield 'test', node

        context = {'doc': dbt.context.parser.docs(model_dict, refs.docrefs)}
        description = model_dict.get('description', '')
        get_rendered(description, context)

        patch = ParsedNodePatch(
            name=model_name,
            original_file_path=path,
            description=description,
            columns=refs.column_info,
            docrefs=refs.docrefs
        )
        yield 'patch', patch
Пример #2
0
    def update_parsed_node(self, parsed_node: IntermediateNode,
                           config: ContextConfig) -> None:
        """Given the ContextConfig used for parsing and the parsed node,
        generate and set the true values to use, overriding the temporary parse
        values set in _build_intermediate_parsed_node.
        """
        config_dict = config.build_config_dict()

        # Set tags on node provided in config blocks
        model_tags = config_dict.get('tags', [])
        parsed_node.tags.extend(model_tags)

        parsed_node.unrendered_config = config.build_config_dict(
            rendered=False)

        # do this once before we parse the node database/schema/alias, so
        # parsed_node.config is what it would be if they did nothing
        self.update_parsed_node_config(parsed_node, config_dict)
        self.update_parsed_node_name(parsed_node, config_dict)

        # at this point, we've collected our hooks. Use the node context to
        # render each hook and collect refs/sources
        hooks = list(
            itertools.chain(parsed_node.config.pre_hook,
                            parsed_node.config.post_hook))
        # skip context rebuilding if there aren't any hooks
        if not hooks:
            return
        # we could cache the original context from parsing this node. Is that
        # worth the cost in memory/complexity?
        context = self._context_for(parsed_node, config)
        for hook in hooks:
            get_rendered(hook.sql, context, parsed_node, capture_macros=True)
Пример #3
0
def _process_docs_for_node(
    context: Dict[str, Any],
    node: ManifestNode,
):
    node.description = get_rendered(node.description, context)
    for column_name, column in node.columns.items():
        column.description = get_rendered(column.description, context)
Пример #4
0
    def parse_models_entry(self, model_dict, path, package_name, root_dir):
        model_name = model_dict['name']
        refs = ParserRef()
        for column in model_dict.get('columns', []):
            column_tests = self._parse_column(model_dict, column, package_name,
                                              root_dir, path, refs)
            for node in column_tests:
                yield 'test', node

        for test in model_dict.get('tests', []):
            try:
                node = self.build_test_node(model_dict, package_name, test,
                                            root_dir, path)
            except dbt.exceptions.CompilationException as exc:
                dbt.exceptions.warn_or_error('in {}: {}'.format(path, exc.msg),
                                             test)
                continue
            yield 'test', node

        context = {'doc': dbt.context.parser.docs(model_dict, refs.docrefs)}
        description = model_dict.get('description', '')
        get_rendered(description, context)

        patch = ParsedNodePatch(name=model_name,
                                original_file_path=path,
                                description=description,
                                columns=refs.column_info,
                                docrefs=refs.docrefs)
        yield 'patch', patch
Пример #5
0
    def parse_exposure(self, unparsed: UnparsedExposure) -> ParsedExposure:
        package_name = self.project.project_name
        unique_id = f'{NodeType.Exposure}.{package_name}.{unparsed.name}'
        path = self.yaml.path.relative_path

        fqn = self.schema_parser.get_fqn_prefix(path)
        fqn.append(unparsed.name)

        parsed = ParsedExposure(
            package_name=package_name,
            root_path=self.project.project_root,
            path=path,
            original_file_path=self.yaml.path.original_file_path,
            unique_id=unique_id,
            fqn=fqn,
            name=unparsed.name,
            type=unparsed.type,
            url=unparsed.url,
            description=unparsed.description,
            owner=unparsed.owner,
            maturity=unparsed.maturity,
        )
        ctx = generate_parse_exposure(
            parsed,
            self.root_project,
            self.schema_parser.macro_manifest,
            package_name,
        )
        depends_on_jinja = '\n'.join('{{ ' + line + '}}'
                                     for line in unparsed.depends_on)
        get_rendered(depends_on_jinja, ctx, parsed, capture_macros=True)
        # parsed now has a populated refs/sources
        return parsed
Пример #6
0
def test_jinja_rendering(value, text_expectation, native_expectation):
    foo_value = yaml.safe_load(value)['foo']
    ctx = {'a_str': '100', 'a_int': 100, 'b_str': 'hello'}
    with text_expectation as text_result:
        assert text_result == get_rendered(foo_value, ctx, native=False)

    with native_expectation as native_result:
        assert native_result == get_rendered(foo_value, ctx, native=True)
Пример #7
0
def collect_docrefs(
    target: UnparsedSchemaYaml,
    refs: ParserRef,
    column_name: Optional[str],
    *descriptions: str,
) -> None:
    context = {'doc': docs(target, refs.docrefs, column_name)}
    for description in descriptions:
        get_rendered(description, context)
Пример #8
0
    def test_regular_render(self):
        s = '{{ "some_value" | as_native }}'
        value = get_rendered(s, {}, native=False)
        assert value == 'some_value'
        s = '{{ 1991 | as_native }}'
        value = get_rendered(s, {}, native=False)
        assert value == '1991'

        s = '{{ "some_value" | as_text }}'
        value = get_rendered(s, {}, native=False)
        assert value == 'some_value'
        s = '{{ 1991 | as_text }}'
        value = get_rendered(s, {}, native=False)
        assert value == '1991'
Пример #9
0
    def render_with_context(
        self, parsed_node: IntermediateNode, config: SourceConfig
    ) -> None:
        """Given the parsed node and a SourceConfig to use during parsing,
        render the node's sql wtih macro capture enabled.

        Note: this mutates the config object when config() calls are rendered.
        """
        context = dbt.context.parser.generate(
            parsed_node, self.root_project, self.macro_manifest, config
        )

        get_rendered(parsed_node.raw_sql, context, parsed_node,
                     capture_macros=True)
Пример #10
0
    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'
Пример #11
0
def _process_docs_for_source(
    context: Dict[str, Any],
    source: ParsedSourceDefinition,
):
    table_description = source.description
    source_description = source.source_description
    table_description = get_rendered(table_description, context)
    source_description = get_rendered(source_description, context)
    source.description = table_description
    source.source_description = source_description

    for column in source.columns.values():
        column_desc = column.description
        column_desc = get_rendered(column_desc, context)
        column.description = column_desc
Пример #12
0
    def _compile_node(
        self,
        node: ManifestNode,
        manifest: Manifest,
        extra_context: Optional[Dict[str, Any]] = None,
    ) -> NonSourceCompiledNode:
        if extra_context is None:
            extra_context = {}

        logger.debug("Compiling {}".format(node.unique_id))

        data = node.to_dict(omit_none=True)
        data.update({
            'compiled': False,
            'compiled_sql': None,
            'extra_ctes_injected': False,
            'extra_ctes': [],
        })
        compiled_node = _compiled_type_for(node).from_dict(data)

        context = self._create_node_context(compiled_node, manifest,
                                            extra_context)

        compiled_node.compiled_sql = jinja.get_rendered(
            node.raw_sql,
            context,
            node,
        )

        compiled_node.relation_name = self._get_relation_name(node)

        compiled_node.compiled = True

        return compiled_node
Пример #13
0
    def render_with_context(self, parsed_node: IntermediateNode,
                            config: ContextConfigType) -> None:
        """Given the parsed node and a ContextConfigType to use during parsing,
        render the node's sql wtih macro capture enabled.

        Note: this mutates the config object when config() calls are rendered.
        """
        # during parsing, we don't have a connection, but we might need one, so
        # we have to acquire it.
        with get_adapter(self.root_project).connection_for(parsed_node):
            context = self._context_for(parsed_node, config)

            get_rendered(parsed_node.raw_sql,
                         context,
                         parsed_node,
                         capture_macros=True)
Пример #14
0
    def __init__(
        self,
        test: Dict[str, Any],
        target: Target,
        package_name: str,
        render_ctx: Dict[str, Any],
        column_name: str = None,
    ) -> None:
        test_name, test_args = self.extract_test_args(test, column_name)
        self.args: Dict[str, Any] = test_args
        self.package_name: str = package_name
        self.target: Target = target

        match = self.TEST_NAME_PATTERN.match(test_name)
        if match is None:
            raise_compiler_error(
                'Test name string did not match expected pattern: {}'.format(
                    test_name))

        groups = match.groupdict()
        self.name: str = groups['test_name']
        self.namespace: str = groups['test_namespace']
        self.modifiers: Dict[str, Any] = {}
        for key, default in self.MODIFIER_ARGS.items():
            value = self.args.pop(key, default)
            if isinstance(value, str):
                value = get_rendered(value, render_ctx)
            self.modifiers[key] = value

        if self.namespace is not None:
            self.package_name = self.namespace

        compiled_name, fqn_name = self.get_test_name()
        self.compiled_name: str = compiled_name
        self.fqn_name: str = fqn_name
Пример #15
0
    def render_with_context(
        self,
        node: ParsedSchemaTestNode,
        config: ContextConfigType,
    ) -> None:
        """Given the parsed node and a ContextConfigType to use during
        parsing, collect all the refs that might be squirreled away in the test
        arguments. This includes the implicit "model" argument.
        """
        # make a base context that doesn't have the magic kwargs field
        context = self._context_for(node, config)
        # update it with the rendered test kwargs (which collects any refs)
        add_rendered_test_kwargs(context, node, capture_macros=True)

        # the parsed node is not rendered in the native context.
        get_rendered(node.raw_sql, context, node, capture_macros=True)
Пример #16
0
    def get_rendered_var(self, var_name):
        raw = self.merged[var_name]
        # if bool/int/float/etc are passed in, don't compile anything
        if not isinstance(raw, str):
            return raw

        return get_rendered(raw, self.context)
Пример #17
0
 def render_value(self, value, keypath=None):
     # keypath is ignored.
     # if it wasn't read as a string, ignore it
     if not isinstance(value, compat.basestring):
         return value
     # force the result of rendering into this python version's native
     # string type
     return compat.to_native_string(get_rendered(value, self.context))
Пример #18
0
 def render_value(self, value, keypath=None):
     # keypath is ignored.
     # if it wasn't read as a string, ignore it
     if not isinstance(value, compat.basestring):
         return value
     # force the result of rendering into this python version's native
     # string type
     return compat.to_native_string(get_rendered(value, self.context))
Пример #19
0
    def render_with_context(self, parsed_node: IntermediateNode,
                            config: ContextConfig) -> None:
        # Given the parsed node and a ContextConfig to use during parsing,
        # render the node's sql wtih macro capture enabled.
        # Note: this mutates the config object when config calls are rendered.

        # during parsing, we don't have a connection, but we might need one, so
        # we have to acquire it.
        with get_adapter(self.root_project).connection_for(parsed_node):
            context = self._context_for(parsed_node, config)

            # this goes through the process of rendering, but just throws away
            # the rendered result. The "macro capture" is the point?
            get_rendered(parsed_node.raw_sql,
                         context,
                         parsed_node,
                         capture_macros=True)
Пример #20
0
    def generate_source_node(self, source, table, path, package_name, root_dir,
                             refs):
        unique_id = self.get_path(NodeType.Source, package_name, source.name,
                                  table.name)

        context = {'doc': dbt.context.parser.docs(source, refs.docrefs)}
        description = table.get('description', '')
        source_description = source.get('description', '')
        get_rendered(description, context)
        get_rendered(source_description, context)

        freshness = dbt.utils.deep_merge(source.get('freshness', {}),
                                         table.get('freshness', {}))

        loaded_at_field = table.get('loaded_at_field',
                                    source.get('loaded_at_field'))

        # use 'or {}' to allow quoting: null
        source_quoting = source.get('quoting') or {}
        table_quoting = table.get('quoting') or {}
        quoting = dbt.utils.deep_merge(source_quoting, table_quoting)

        default_database = self.root_project_config.credentials.database
        return ParsedSourceDefinition(
            package_name=package_name,
            database=source.get('database', default_database),
            schema=source.get('schema', source.name),
            identifier=table.get('identifier', table.name),
            root_path=root_dir,
            path=path,
            original_file_path=path,
            columns=refs.column_info,
            unique_id=unique_id,
            name=table.name,
            description=description,
            source_name=source.name,
            source_description=source_description,
            loader=source.get('loader', ''),
            docrefs=refs.docrefs,
            loaded_at_field=loaded_at_field,
            freshness=freshness,
            quoting=quoting,
            resource_type=NodeType.Source,
            fqn=[package_name, source.name, table.name])
Пример #21
0
    def generate_source_node(self, source, table, path, package_name, root_dir,
                             refs):
        unique_id = self.get_path(NodeType.Source, package_name,
                                  source.name, table.name)

        context = {'doc': dbt.context.parser.docs(source, refs.docrefs)}
        description = table.get('description', '')
        source_description = source.get('description', '')
        get_rendered(description, context)
        get_rendered(source_description, context)

        freshness = dbt.utils.deep_merge(source.get('freshness', {}),
                                         table.get('freshness', {}))

        loaded_at_field = table.get('loaded_at_field',
                                    source.get('loaded_at_field'))

        # use 'or {}' to allow quoting: null
        source_quoting = source.get('quoting') or {}
        table_quoting = table.get('quoting') or {}
        quoting = dbt.utils.deep_merge(source_quoting, table_quoting)

        default_database = self.root_project_config.credentials.database
        return ParsedSourceDefinition(
            package_name=package_name,
            database=source.get('database', default_database),
            schema=source.get('schema', source.name),
            identifier=table.get('identifier', table.name),
            root_path=root_dir,
            path=path,
            original_file_path=path,
            columns=refs.column_info,
            unique_id=unique_id,
            name=table.name,
            description=description,
            source_name=source.name,
            source_description=source_description,
            loader=source.get('loader', ''),
            docrefs=refs.docrefs,
            loaded_at_field=loaded_at_field,
            freshness=freshness,
            quoting=quoting,
            resource_type=NodeType.Source
        )
Пример #22
0
    def _parse_column(self, target, column, package_name, root_dir, path,
                      refs):
        # this should yield ParsedNodes where resource_type == NodeType.Test
        column_name = column['name']
        description = column.get('description', '')

        refs.add(column_name, description)
        context = {
            'doc': dbt.context.parser.docs(target, refs.docrefs, column_name)
        }
        get_rendered(description, context)

        for test in column.get('tests', []):
            try:
                yield self.build_test_node(target, package_name, test,
                                           root_dir, path, column_name)
            except dbt.exceptions.CompilationException as exc:
                dbt.exceptions.warn_or_error('in {}: {}'.format(path, exc.msg),
                                             None)
                continue
Пример #23
0
 def render_value(self,
                  value: Any,
                  keypath: Optional[Keypath] = None) -> Any:
     # keypath is ignored.
     # if it wasn't read as a string, ignore it
     if not isinstance(value, str):
         return value
     try:
         with catch_jinja():
             return get_rendered(value, self.context, native=True)
     except CompilationException as exc:
         msg = f'Could not render {value}: {exc.msg}'
         raise CompilationException(msg) from exc
Пример #24
0
 def render_test_update(self, node, config, builder):
     macro_unique_id = self.macro_resolver.get_macro_id(
         node.package_name, 'test_' + builder.name)
     # Add the depends_on here so we can limit the macros added
     # to the context in rendering processing
     node.depends_on.add_macro(macro_unique_id)
     if (macro_unique_id
             in ['macro.dbt.test_not_null', 'macro.dbt.test_unique']):
         self.update_parsed_node(node, config)
         if builder.severity() is not None:
             node.unrendered_config['severity'] = builder.severity()
             node.config['severity'] = builder.severity()
         if builder.enabled() is not None:
             node.config['enabled'] = builder.enabled()
         # source node tests are processed at patch_source time
         if isinstance(builder.target, UnpatchedSourceDefinition):
             sources = [builder.target.fqn[-2], builder.target.fqn[-1]]
             node.sources.append(sources)
         else:  # all other nodes
             node.refs.append([builder.target.name])
     else:
         try:
             # make a base context that doesn't have the magic kwargs field
             context = generate_test_context(
                 node,
                 self.root_project,
                 self.manifest,
                 config,
                 self.macro_resolver,
             )
             # update with rendered test kwargs (which collects any refs)
             add_rendered_test_kwargs(context, node, capture_macros=True)
             # the parsed node is not rendered in the native context.
             get_rendered(node.raw_sql, context, node, capture_macros=True)
             self.update_parsed_node(node, config)
         except ValidationError as exc:
             # we got a ValidationError - probably bad types in config()
             msg = validator_error_message(exc)
             raise CompilationException(msg, node=node) from exc
Пример #25
0
    def _parse_column(self, target, column, package_name, root_dir, path,
                      refs):
        # this should yield ParsedNodes where resource_type == NodeType.Test
        column_name = column['name']
        description = column.get('description', '')

        refs.add(column_name, description)
        context = {
            'doc': dbt.context.parser.docs(target, refs.docrefs, column_name)
        }
        get_rendered(description, context)

        for test in column.get('tests', []):
            try:
                yield self.build_test_node(
                    target, package_name, test, root_dir,
                    path, column_name
                )
            except dbt.exceptions.CompilationException as exc:
                dbt.exceptions.warn_or_error(
                    'in {}: {}'.format(path, exc.msg), None
                )
                continue
Пример #26
0
    def parse_block(self,
                    block: BlockContents) -> Iterable[ParsedDocumentation]:
        unique_id = self.generate_unique_id(block.name)
        contents = get_rendered(block.contents, {}).strip()

        doc = ParsedDocumentation(
            root_path=self.project.project_root,
            path=block.file.path.relative_path,
            original_file_path=block.path.original_file_path,
            package_name=self.project.project_name,
            unique_id=unique_id,
            name=block.name,
            block_contents=contents,
        )
        return [doc]
Пример #27
0
    def __init__(
        self,
        test: Dict[str, Any],
        target: Testable,
        package_name: str,
        render_ctx: Dict[str, Any],
        column_name: str = None,
    ) -> None:
        test_name, test_args = self.extract_test_args(test, column_name)
        self.args: Dict[str, Any] = test_args
        if 'model' in self.args:
            raise_compiler_error(
                'Test arguments include "model", which is a reserved argument',
            )
        self.package_name: str = package_name
        self.target: Testable = target

        self.args['model'] = self.build_model_str()

        match = self.TEST_NAME_PATTERN.match(test_name)
        if match is None:
            raise_compiler_error(
                'Test name string did not match expected pattern: {}'.format(
                    test_name))

        groups = match.groupdict()
        self.name: str = groups['test_name']
        self.namespace: str = groups['test_namespace']
        self.modifiers: Dict[str, Any] = {}
        for key in self.MODIFIER_ARGS:
            value = self.args.pop(key, None)
            if isinstance(value, str):
                value = get_rendered(value, render_ctx, native=True)
            if value is not None:
                self.modifiers[key] = value

        if self.namespace is not None:
            self.package_name = self.namespace

        compiled_name, fqn_name = self.get_test_name()
        self.compiled_name: str = compiled_name
        self.fqn_name: str = fqn_name
Пример #28
0
    def __init__(self, test, target, column_name, package_name, render_ctx):
        test_name, test_args = self.extract_test_args(test, column_name)
        self.args = test_args
        self.package_name = package_name
        self.target = target

        match = self.TEST_NAME_PATTERN.match(test_name)
        if match is None:
            dbt.exceptions.raise_compiler_error(
                'Test name string did not match expected pattern: {}'.format(
                    test_name))

        groups = match.groupdict()
        self.name = groups['test_name']
        self.namespace = groups['test_namespace']
        self.modifiers = {}
        for key, default in self.MODIFIER_ARGS.items():
            value = self.args.pop(key, default)
            if isinstance(value, basestring):
                value = get_rendered(value, render_ctx)
            self.modifiers[key] = value

        if self.namespace is not None:
            self.package_name = self.namespace
Пример #29
0
def _process_docs_for_exposure(context: Dict[str, Any],
                               exposure: ParsedExposure) -> None:
    exposure.description = get_rendered(exposure.description, context)
Пример #30
0
def _process_docs_for_macro(context: Dict[str, Any],
                            macro: ParsedMacro) -> None:
    macro.description = get_rendered(macro.description, context)
    for arg in macro.arguments:
        arg.description = get_rendered(arg.description, context)
Пример #31
0
 def fn(string):
     return get_rendered(string, context, node)
Пример #32
0
 def render_value(self, value, keypath=None):
     # keypath is ignored.
     # if it wasn't read as a string, ignore it
     if not isinstance(value, str):
         return value
     return str(get_rendered(value, self.context))
Пример #33
0
 def render(self, string: str) -> str:
     return get_rendered(string, self._ctx, self.model)