import abc import os import tempfile from contextlib import contextmanager from typing import List, Optional, Generic, TypeVar from dbt.clients import system from dbt.contracts.project import ProjectPackageMetadata from dbt.logger import GLOBAL_LOGGER as logger DOWNLOADS_PATH = None def get_downloads_path(): return DOWNLOADS_PATH @contextmanager def downloads_directory(): global DOWNLOADS_PATH remove_downloads = False # the user might have set an environment variable. Set it to that, and do # not remove it when finished. if DOWNLOADS_PATH is None: DOWNLOADS_PATH = os.getenv('DBT_DOWNLOADS_DIR') remove_downloads = False # if we are making a per-run temp directory, remove it at the end of # successful runs if DOWNLOADS_PATH is None: DOWNLOADS_PATH = tempfile.mkdtemp(prefix='dbt-downloads-') remove_downloads = True system.make_directory(DOWNLOADS_PATH) logger.debug("Set downloads directory='{}'".format(DOWNLOADS_PATH)) yield DOWNLOADS_PATH if remove_downloads: system.rmtree(DOWNLOADS_PATH) DOWNLOADS_PATH = None class BasePackage(metaclass=abc.ABCMeta): @abc.abstractproperty def name(self) -> str: raise NotImplementedError def all_names(self) -> List[str]: return [self.name] @abc.abstractmethod def source_type(self) -> str: raise NotImplementedError class PinnedPackage(BasePackage): def __init__(self) -> None: self._cached_metadata: Optional[ProjectPackageMetadata] = None def __str__(self) -> str: version = self.get_version() if not version: return self.name return '{}@{}'.format(self.name, version) @abc.abstractmethod def get_version(self) -> Optional[str]: raise NotImplementedError @abc.abstractmethod def _fetch_metadata(self, project, renderer): raise NotImplementedError @abc.abstractmethod def install(self, project): raise NotImplementedError @abc.abstractmethod def nice_version_name(self): raise NotImplementedError def fetch_metadata(self, project, renderer): if not self._cached_metadata: self._cached_metadata = self._fetch_metadata(project, renderer) return self._cached_metadata def get_project_name(self, project, renderer): metadata = self.fetch_metadata(project, renderer) return metadata.name def get_installation_path(self, project, renderer): dest_dirname = self.get_project_name(project, renderer) return os.path.join(project.modules_path, dest_dirname) def get_subdirectory(self): return None SomePinned = TypeVar('SomePinned', bound=PinnedPackage) SomeUnpinned = TypeVar('SomeUnpinned', bound='UnpinnedPackage') class UnpinnedPackage(Generic[SomePinned], BasePackage): @abc.abstractclassmethod def from_contract(cls, contract): raise NotImplementedError @abc.abstractmethod def incorporate(self: SomeUnpinned, other: SomeUnpinned) -> SomeUnpinned: raise NotImplementedError @abc.abstractmethod def resolved(self) -> SomePinned: raise NotImplementedError