class Project(HyphenatedJsonSchemaMixin, Replaceable): name: Name version: Union[SemverString, float] project_root: Optional[str] = None source_paths: Optional[List[str]] = None macro_paths: Optional[List[str]] = None data_paths: Optional[List[str]] = None test_paths: Optional[List[str]] = None analysis_paths: Optional[List[str]] = None docs_paths: Optional[List[str]] = None target_path: Optional[str] = None snapshot_paths: Optional[List[str]] = None clean_targets: Optional[List[str]] = None profile: Optional[str] = None log_path: Optional[str] = None modules_path: Optional[str] = None quoting: Optional[Quoting] = None on_run_start: Optional[List[str]] = field(default_factory=list_str) on_run_end: Optional[List[str]] = field(default_factory=list_str) require_dbt_version: Optional[Union[List[str], str]] = None models: Dict[str, Any] = field(default_factory=dict) seeds: Dict[str, Any] = field(default_factory=dict) snapshots: Dict[str, Any] = field(default_factory=dict) packages: List[PackageSpec] = field(default_factory=list) query_comment: Optional[Union[str, NoValue]] = NoValue() @classmethod def from_dict(cls, data, validate=True): result = super().from_dict(data, validate=validate) if result.name in BANNED_PROJECT_NAMES: raise ValidationError( 'Invalid project name: {} is a reserved word'.format( result.name)) return result
def _get_comment_macro(self): if ( self.config.query_comment != NoValue() and self.config.query_comment ): return self.config.query_comment else: return super()._get_comment_macro()
class Project(HyphenatedDbtClassMixin, Replaceable): name: Name version: Union[SemverString, float] config_version: int project_root: Optional[str] = None source_paths: Optional[List[str]] = None macro_paths: Optional[List[str]] = None data_paths: Optional[List[str]] = None test_paths: Optional[List[str]] = None analysis_paths: Optional[List[str]] = None docs_paths: Optional[List[str]] = None asset_paths: Optional[List[str]] = None target_path: Optional[str] = None snapshot_paths: Optional[List[str]] = None clean_targets: Optional[List[str]] = None profile: Optional[str] = None log_path: Optional[str] = None modules_path: Optional[str] = None quoting: Optional[Quoting] = None on_run_start: Optional[List[str]] = field(default_factory=list_str) on_run_end: Optional[List[str]] = field(default_factory=list_str) require_dbt_version: Optional[Union[List[str], str]] = None models: Dict[str, Any] = field(default_factory=dict) seeds: Dict[str, Any] = field(default_factory=dict) snapshots: Dict[str, Any] = field(default_factory=dict) analyses: Dict[str, Any] = field(default_factory=dict) sources: Dict[str, Any] = field(default_factory=dict) tests: Dict[str, Any] = field(default_factory=dict) vars: Optional[Dict[str, Any]] = field( default=None, metadata=dict( description='map project names to their vars override dicts', ), ) packages: List[PackageSpec] = field(default_factory=list) query_comment: Optional[Union[QueryComment, NoValue, str]] = NoValue() @classmethod def validate(cls, data): super().validate(data) if data['name'] in BANNED_PROJECT_NAMES: raise ValidationError( f"Invalid project name: {data['name']} is a reserved word" )
def to_project_config(self, with_packages=False): """Return a dict representation of the config that could be written to disk with `yaml.safe_dump` to get this configuration. :param with_packages bool: If True, include the serialized packages file in the root. :returns dict: The serialized profile. """ result = deepcopy({ 'name': self.project_name, 'version': self.version, 'project-root': self.project_root, 'profile': self.profile_name, 'source-paths': self.source_paths, 'macro-paths': self.macro_paths, 'data-paths': self.data_paths, 'test-paths': self.test_paths, 'analysis-paths': self.analysis_paths, 'docs-paths': self.docs_paths, 'target-path': self.target_path, 'snapshot-paths': self.snapshot_paths, 'clean-targets': self.clean_targets, 'log-path': self.log_path, 'quoting': self.quoting, 'models': self.models, 'on-run-start': self.on_run_start, 'on-run-end': self.on_run_end, 'seeds': self.seeds, 'snapshots': self.snapshots, 'require-dbt-version': [ v.to_version_string() for v in self.dbt_version ], }) if with_packages: result.update(self.packages.to_dict()) if self.query_comment != NoValue(): result['query-comment'] = self.query_comment return result
def from_project_config(cls, project_dict, packages_dict=None): """Create a project from its project and package configuration, as read by yaml.safe_load(). :param project_dict dict: The dictionary as read from disk :param packages_dict Optional[dict]: If it exists, the packages file as read from disk. :raises DbtProjectError: If the project is missing or invalid, or if the packages file exists and is invalid. :returns Project: The project, with defaults populated. """ try: project_dict = cls._preprocess(project_dict) except RecursionException: raise DbtProjectError( 'Cycle detected: Project input has a reference to itself', project=project_dict ) # just for validation. try: ProjectContract.from_dict(project_dict) except ValidationError as e: raise DbtProjectError(validator_error_message(e)) from e # name/version are required in the Project definition, so we can assume # they are present name = project_dict['name'] version = project_dict['version'] # this is added at project_dict parse time and should always be here # once we see it. project_root = project_dict['project-root'] # this is only optional in the sense that if it's not present, it needs # to have been a cli argument. profile_name = project_dict.get('profile') # these are optional source_paths = project_dict.get('source-paths', ['models']) macro_paths = project_dict.get('macro-paths', ['macros']) data_paths = project_dict.get('data-paths', ['data']) test_paths = project_dict.get('test-paths', ['test']) analysis_paths = project_dict.get('analysis-paths', []) docs_paths = project_dict.get('docs-paths', source_paths[:]) target_path = project_dict.get('target-path', 'target') snapshot_paths = project_dict.get('snapshot-paths', ['snapshots']) # should this also include the modules path by default? clean_targets = project_dict.get('clean-targets', [target_path]) log_path = project_dict.get('log-path', 'logs') modules_path = project_dict.get('modules-path', 'dbt_modules') # in the default case we'll populate this once we know the adapter type quoting = project_dict.get('quoting', {}) models = project_dict.get('models', {}) on_run_start = project_dict.get('on-run-start', []) on_run_end = project_dict.get('on-run-end', []) seeds = project_dict.get('seeds', {}) snapshots = project_dict.get('snapshots', {}) dbt_raw_version = project_dict.get('require-dbt-version', '>=0.0.0') query_comment = project_dict.get('query-comment', NoValue()) try: dbt_version = _parse_versions(dbt_raw_version) except SemverException as e: raise DbtProjectError(str(e)) from e try: packages = package_config_from_data(packages_dict) except ValidationError as e: raise DbtProjectError(validator_error_message(e)) from e project = cls( project_name=name, version=version, project_root=project_root, profile_name=profile_name, source_paths=source_paths, macro_paths=macro_paths, data_paths=data_paths, test_paths=test_paths, analysis_paths=analysis_paths, docs_paths=docs_paths, target_path=target_path, snapshot_paths=snapshot_paths, clean_targets=clean_targets, log_path=log_path, modules_path=modules_path, quoting=quoting, models=models, on_run_start=on_run_start, on_run_end=on_run_end, seeds=seeds, snapshots=snapshots, dbt_version=dbt_version, packages=packages, query_comment=query_comment, ) # sanity check - this means an internal issue project.validate() return project