text: import os
  from dataclasses import dataclass
  from typing import Iterable, Iterator, Union, List, Tuple
  
  from dbt.contracts.graph.manifest import FilePath
  from dbt.contracts.graph.parsed import ParsedHookNode
  from dbt.exceptions import InternalException
  from dbt.node_types import NodeType, RunHookType
  from dbt.source_config import SourceConfig
  from dbt.parser.base import SimpleParser
  from dbt.parser.search import FileBlock
  from dbt.utils import get_pseudo_hook_path
  
  
  @dataclass
  class HookBlock(FileBlock):
      project: str
      value: str
      index: int
      hook_type: RunHookType
  
      @property
      def contents(self):
          return self.value
  
      @property
      def name(self):
          return '{}-{!s}-{!s}'.format(self.project, self.hook_type, self.index)
  
  
  class HookSearcher(Iterable[HookBlock]):
      def __init__(self, project, source_file, hook_type) -> None:
          self.project = project
          self.source_file = source_file
          self.hook_type = hook_type
  
      def _hook_list(
          self, hooks: Union[str, List[str], Tuple[str, ...]]
      ) -> List[str]:
          if isinstance(hooks, tuple):
              hooks = list(hooks)
          elif not isinstance(hooks, list):
              hooks = [hooks]
          return hooks
  
      def get_hook_defs(self) -> List[str]:
          if self.hook_type == RunHookType.Start:
              hooks = self.project.on_run_start
          elif self.hook_type == RunHookType.End:
              hooks = self.project.on_run_end
          else:
              raise InternalException(
                  'hook_type must be one of "{}" or "{}" (got {})'
                  .format(RunHookType.Start, RunHookType.End, self.hook_type)
              )
          return self._hook_list(hooks)
  
      def __iter__(self) -> Iterator[HookBlock]:
          hooks = self.get_hook_defs()
          for index, hook in enumerate(hooks):
              yield HookBlock(
                  file=self.source_file,
                  project=self.project.project_name,
                  value=hook,
                  index=index,
                  hook_type=self.hook_type,
              )
  
  
  class HookParser(SimpleParser[HookBlock, ParsedHookNode]):
      def transform(self, node):
          return node
  
      def get_paths(self):
          searched_path = '.'
          relative_path = 'dbt_project.yml'
          absolute_path = os.path.normcase(os.path.abspath(os.path.join(
              self.project.project_root, searched_path, relative_path
          )))
          path = FilePath(
              searched_path='.',
              relative_path='relative_path',
              absolute_path=absolute_path,
          )
          return [path]
  
      def parse_from_dict(self, dct, validate=True) -> ParsedHookNode:
          return ParsedHookNode.from_dict(dct, validate=validate)
  
      @classmethod
      def get_compiled_path(cls, block: HookBlock):
          return get_pseudo_hook_path(block.name)
  
      def _create_parsetime_node(
          self,
          block: HookBlock,
          path: str,
          config: SourceConfig,
          name=None,
          **kwargs,
      ) -> ParsedHookNode:
  
          return super()._create_parsetime_node(
              block=block, path=path, config=config,
              index=block.index, name=name,
              tags=[str(block.hook_type)]
          )
  
      @property
      def resource_type(self) -> NodeType:
          return NodeType.Operation
  
      def parse_file(self, block: FileBlock) -> None:
          for hook_type in RunHookType:
              for hook in HookSearcher(self.project, block.file, hook_type):
                  self.parse_node(hook)
  