dbt-selly/dbt-env/lib/python3.8/site-packages/dbt/parser/macros.py

113 lines
3.7 KiB
Python

from typing import Iterable, List
import jinja2
from dbt.clients import jinja
from dbt.contracts.graph.unparsed import UnparsedMacro
from dbt.contracts.graph.parsed import ParsedMacro
from dbt.contracts.files import FilePath, SourceFile
from dbt.exceptions import CompilationException
from dbt.logger import GLOBAL_LOGGER as logger
from dbt.node_types import NodeType
from dbt.parser.base import BaseParser
from dbt.parser.search import FileBlock, FilesystemSearcher
from dbt.utils import MACRO_PREFIX
class MacroParser(BaseParser[ParsedMacro]):
# This is only used when creating a MacroManifest separate
# from the normal parsing flow.
def get_paths(self) -> List[FilePath]:
return list(FilesystemSearcher(
project=self.project,
relative_dirs=self.project.macro_paths,
extension='.sql',
))
@property
def resource_type(self) -> NodeType:
return NodeType.Macro
@classmethod
def get_compiled_path(cls, block: FileBlock):
return block.path.relative_path
def parse_macro(
self, block: jinja.BlockTag, base_node: UnparsedMacro, name: str
) -> ParsedMacro:
unique_id = self.generate_unique_id(name)
return ParsedMacro(
path=base_node.path,
macro_sql=block.full_block,
original_file_path=base_node.original_file_path,
package_name=base_node.package_name,
root_path=base_node.root_path,
resource_type=base_node.resource_type,
name=name,
unique_id=unique_id,
)
def parse_unparsed_macros(
self, base_node: UnparsedMacro
) -> Iterable[ParsedMacro]:
try:
blocks: List[jinja.BlockTag] = [
t for t in
jinja.extract_toplevel_blocks(
base_node.raw_sql,
allowed_blocks={'macro', 'materialization', 'test'},
collect_raw_data=False,
)
if isinstance(t, jinja.BlockTag)
]
except CompilationException as exc:
exc.add_node(base_node)
raise
for block in blocks:
try:
ast = jinja.parse(block.full_block)
except CompilationException as e:
e.add_node(base_node)
raise
macro_nodes = list(ast.find_all(jinja2.nodes.Macro))
if len(macro_nodes) != 1:
# things have gone disastrously wrong, we thought we only
# parsed one block!
raise CompilationException(
f'Found multiple macros in {block.full_block}, expected 1',
node=base_node
)
macro_name = macro_nodes[0].name
if not macro_name.startswith(MACRO_PREFIX):
continue
name: str = macro_name.replace(MACRO_PREFIX, '')
node = self.parse_macro(block, base_node, name)
yield node
def parse_file(self, block: FileBlock):
assert isinstance(block.file, SourceFile)
source_file = block.file
assert isinstance(source_file.contents, str)
original_file_path = source_file.path.original_file_path
logger.debug("Parsing {}".format(original_file_path))
# this is really only used for error messages
base_node = UnparsedMacro(
path=original_file_path,
original_file_path=original_file_path,
package_name=self.project.project_name,
raw_sql=source_file.contents,
root_path=self.project.project_root,
resource_type=NodeType.Macro,
)
for node in self.parse_unparsed_macros(base_node):
self.manifest.add_macro(block.file, node)