dagster-dbt 0.28.0__tar.gz → 0.28.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dagster_dbt-0.28.0/dagster_dbt.egg-info → dagster_dbt-0.28.2}/PKG-INFO +4 -4
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/asset_decorator.py +2 -2
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/asset_utils.py +45 -1
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/asset_defs.py +2 -2
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/utils.py +2 -2
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/asset_decorator.py +2 -1
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/compat.py +1 -2
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/components/dbt_project/component.py +99 -50
- dagster_dbt-0.28.2/dagster_dbt/components/dbt_project/scaffolder.py +65 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/core/dbt_cli_invocation.py +1 -2
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/core/dbt_event_iterator.py +2 -2
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/core/resource.py +8 -5
- dagster_dbt-0.28.2/dagster_dbt/dbt_project_manager.py +172 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/pyproject.toml.jinja +1 -1
- dagster_dbt-0.28.2/dagster_dbt/version.py +1 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2/dagster_dbt.egg-info}/PKG-INFO +4 -4
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt.egg-info/SOURCES.txt +1 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt.egg-info/requires.txt +2 -1
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/setup.py +3 -3
- dagster_dbt-0.28.0/dagster_dbt/components/dbt_project/scaffolder.py +0 -50
- dagster_dbt-0.28.0/dagster_dbt/version.py +0 -1
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/LICENSE +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/MANIFEST.in +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/README.md +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/asset_specs.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cli/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cli/app.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/cli.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/ops.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/resources.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud/types.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/cli_invocation.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/client.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/resources.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/run_handler.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/sensor_builder.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/cloud_v2/types.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/components/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/components/dbt_project/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/core/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/core/dbt_cli_event.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/core/utils.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/dagster_dbt_translator.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/dbt_core_version.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/dbt_manifest.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/dbt_manifest_asset_selection.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/dbt_project.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/dbt_version.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/errors.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/freshness_builder.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/__init__.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/scaffold/__init__.py.jinja +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/scaffold/assets.py.jinja +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/scaffold/definitions.py.jinja +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/scaffold/project.py.jinja +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/scaffold/schedules.py.jinja +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/include/setup.py.jinja +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/metadata_set.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/py.typed +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt/utils.py +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt.egg-info/dependency_links.txt +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt.egg-info/entry_points.txt +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt.egg-info/not-zip-safe +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/dagster_dbt.egg-info/top_level.txt +0 -0
- {dagster_dbt-0.28.0 → dagster_dbt-0.28.2}/setup.cfg +0 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dagster-dbt
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.2
|
|
4
4
|
Summary: A Dagster integration for dbt
|
|
5
5
|
Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-dbt
|
|
6
6
|
Author: Dagster Labs
|
|
7
7
|
Author-email: hello@dagsterlabs.com
|
|
8
8
|
License: Apache-2.0
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
14
|
Classifier: Operating System :: OS Independent
|
|
16
|
-
Requires-Python: >=3.
|
|
15
|
+
Requires-Python: >=3.10,<3.14
|
|
17
16
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: dagster==1.12.
|
|
17
|
+
Requires-Dist: dagster==1.12.2
|
|
19
18
|
Requires-Dist: dbt-core<1.11,>=1.7
|
|
19
|
+
Requires-Dist: gitpython
|
|
20
20
|
Requires-Dist: Jinja2
|
|
21
21
|
Requires-Dist: networkx
|
|
22
22
|
Requires-Dist: orjson
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
3
5
|
import textwrap
|
|
4
6
|
from collections import defaultdict
|
|
5
7
|
from collections.abc import Iterable, Mapping, Sequence
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import TYPE_CHECKING, AbstractSet, Annotated, Any, Final, Optional, Union # noqa: UP035
|
|
8
10
|
|
|
11
|
+
import yaml
|
|
9
12
|
from dagster import (
|
|
10
13
|
AssetCheckKey,
|
|
11
14
|
AssetCheckSpec,
|
|
@@ -32,6 +35,7 @@ from dagster._core.definitions.metadata import TableMetadataSet
|
|
|
32
35
|
from dagster._core.errors import DagsterInvalidPropertyError
|
|
33
36
|
from dagster._core.types.dagster_type import Nothing
|
|
34
37
|
from dagster._record import ImportFrom, record
|
|
38
|
+
from dagster_shared.record import replace
|
|
35
39
|
|
|
36
40
|
from dagster_dbt.dbt_project import DbtProject
|
|
37
41
|
from dagster_dbt.metadata_set import DbtMetadataSet
|
|
@@ -55,6 +59,10 @@ DBT_DEFAULT_SELECTOR = ""
|
|
|
55
59
|
DBT_INDIRECT_SELECTION_ENV: Final[str] = "DBT_INDIRECT_SELECTION"
|
|
56
60
|
DBT_EMPTY_INDIRECT_SELECTION: Final[str] = "empty"
|
|
57
61
|
|
|
62
|
+
# Threshold for switching to selector file to avoid CLI argument length limits
|
|
63
|
+
# https://github.com/dagster-io/dagster/issues/16997
|
|
64
|
+
_SELECTION_ARGS_THRESHOLD: Final[int] = 200
|
|
65
|
+
|
|
58
66
|
DUPLICATE_ASSET_KEY_ERROR_MESSAGE = (
|
|
59
67
|
"The following dbt resources are configured with identical Dagster asset keys."
|
|
60
68
|
" Please ensure that each dbt resource generates a unique Dagster asset key."
|
|
@@ -443,6 +451,10 @@ def get_updated_cli_invocation_params_for_context(
|
|
|
443
451
|
[assets_def]
|
|
444
452
|
)
|
|
445
453
|
|
|
454
|
+
# Get project_dir from dbt_project if available
|
|
455
|
+
project_dir = Path(dbt_project.project_dir) if dbt_project else None
|
|
456
|
+
target_project = dbt_project
|
|
457
|
+
|
|
446
458
|
selection_args, indirect_selection_override = get_subset_selection_for_context(
|
|
447
459
|
context=context,
|
|
448
460
|
manifest=manifest,
|
|
@@ -452,17 +464,49 @@ def get_updated_cli_invocation_params_for_context(
|
|
|
452
464
|
dagster_dbt_translator=dagster_dbt_translator,
|
|
453
465
|
current_dbt_indirect_selection_env=indirect_selection,
|
|
454
466
|
)
|
|
467
|
+
if (
|
|
468
|
+
selection_args[0] == "--select"
|
|
469
|
+
and project_dir
|
|
470
|
+
and len(resources := selection_args[1].split(" ")) > _SELECTION_ARGS_THRESHOLD
|
|
471
|
+
):
|
|
472
|
+
temp_project_dir = tempfile.mkdtemp()
|
|
473
|
+
shutil.copytree(project_dir, temp_project_dir, dirs_exist_ok=True)
|
|
474
|
+
selectors_path = Path(temp_project_dir) / "selectors.yml"
|
|
475
|
+
|
|
476
|
+
# Delete any existing selectors, we need to create our own
|
|
477
|
+
if selectors_path.exists():
|
|
478
|
+
selectors_path.unlink()
|
|
479
|
+
|
|
480
|
+
selector_name = f"dagster_run_{context.run_id}"
|
|
481
|
+
temp_selectors = {
|
|
482
|
+
"selectors": [
|
|
483
|
+
{
|
|
484
|
+
"name": selector_name,
|
|
485
|
+
"definition": {"union": list(resources)},
|
|
486
|
+
}
|
|
487
|
+
]
|
|
488
|
+
}
|
|
489
|
+
selectors_path.write_text(yaml.safe_dump(temp_selectors))
|
|
490
|
+
logger.info(
|
|
491
|
+
f"DBT selection of {len(resources)} resources exceeds threshold of {_SELECTION_ARGS_THRESHOLD}. "
|
|
492
|
+
"This may exceed system argument length limits. "
|
|
493
|
+
f"Executing materialization against temporary copy of DBT project at {temp_project_dir} with ephemeral selector."
|
|
494
|
+
)
|
|
495
|
+
selection_args = ["--selector", selector_name]
|
|
496
|
+
target_project = replace(dbt_project, project_dir=temp_project_dir)
|
|
455
497
|
|
|
456
498
|
indirect_selection = (
|
|
457
499
|
indirect_selection_override if indirect_selection_override else indirect_selection
|
|
458
500
|
)
|
|
501
|
+
else:
|
|
502
|
+
target_project = dbt_project
|
|
459
503
|
|
|
460
504
|
return DbtCliInvocationPartialParams(
|
|
461
505
|
manifest=manifest,
|
|
462
506
|
dagster_dbt_translator=dagster_dbt_translator,
|
|
463
507
|
selection_args=selection_args,
|
|
464
508
|
indirect_selection=indirect_selection,
|
|
465
|
-
dbt_project=
|
|
509
|
+
dbt_project=target_project,
|
|
466
510
|
)
|
|
467
511
|
|
|
468
512
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import shlex
|
|
3
3
|
from argparse import ArgumentParser, Namespace
|
|
4
|
-
from collections.abc import Mapping, Sequence
|
|
4
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
5
5
|
from contextlib import suppress
|
|
6
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Optional, Union, cast
|
|
7
7
|
|
|
8
8
|
import dagster._check as check
|
|
9
9
|
from dagster import (
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import TYPE_CHECKING, Any
|
|
3
|
+
from typing import TYPE_CHECKING, Any, TypeAlias
|
|
4
4
|
|
|
5
5
|
from packaging import version
|
|
6
|
-
from typing_extensions import TypeAlias
|
|
7
6
|
|
|
8
7
|
# it's unclear exactly which dbt import adds a handler to the root logger, but something certainly does!
|
|
9
8
|
# on this line, we keep track of the set of handlers that are on the root logger BEFORE any dbt imports
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import json
|
|
3
|
-
import shutil
|
|
4
3
|
from collections.abc import Iterator, Mapping
|
|
5
4
|
from contextlib import contextmanager
|
|
6
5
|
from contextvars import ContextVar
|
|
7
|
-
from dataclasses import dataclass, field
|
|
6
|
+
from dataclasses import dataclass, field, replace
|
|
8
7
|
from functools import cached_property
|
|
9
8
|
from pathlib import Path
|
|
10
|
-
from typing import Annotated, Any, Literal, Optional, Union
|
|
9
|
+
from typing import Annotated, Any, Literal, Optional, TypeAlias, Union
|
|
11
10
|
|
|
12
11
|
import dagster as dg
|
|
13
12
|
from dagster._annotations import public
|
|
@@ -26,10 +25,17 @@ from dagster.components.utils.translation import (
|
|
|
26
25
|
)
|
|
27
26
|
from dagster_shared import check
|
|
28
27
|
from dagster_shared.serdes.objects.models.defs_state_info import DefsStateManagementType
|
|
29
|
-
from typing_extensions import TypeAlias
|
|
30
28
|
|
|
31
|
-
from dagster_dbt.
|
|
32
|
-
|
|
29
|
+
from dagster_dbt.asset_utils import (
|
|
30
|
+
DAGSTER_DBT_EXCLUDE_METADATA_KEY,
|
|
31
|
+
DAGSTER_DBT_SELECT_METADATA_KEY,
|
|
32
|
+
DAGSTER_DBT_SELECTOR_METADATA_KEY,
|
|
33
|
+
DBT_DEFAULT_EXCLUDE,
|
|
34
|
+
DBT_DEFAULT_SELECT,
|
|
35
|
+
DBT_DEFAULT_SELECTOR,
|
|
36
|
+
build_dbt_specs,
|
|
37
|
+
get_node,
|
|
38
|
+
)
|
|
33
39
|
from dagster_dbt.components.dbt_project.scaffolder import DbtProjectComponentScaffolder
|
|
34
40
|
from dagster_dbt.core.resource import DbtCliResource
|
|
35
41
|
from dagster_dbt.dagster_dbt_translator import (
|
|
@@ -40,6 +46,12 @@ from dagster_dbt.dagster_dbt_translator import (
|
|
|
40
46
|
from dagster_dbt.dbt_manifest import validate_manifest
|
|
41
47
|
from dagster_dbt.dbt_manifest_asset_selection import DbtManifestAssetSelection
|
|
42
48
|
from dagster_dbt.dbt_project import DbtProject
|
|
49
|
+
from dagster_dbt.dbt_project_manager import (
|
|
50
|
+
DbtProjectArgsManager,
|
|
51
|
+
DbtProjectManager,
|
|
52
|
+
NoopDbtProjectManager,
|
|
53
|
+
RemoteGitDbtProjectManager,
|
|
54
|
+
)
|
|
43
55
|
from dagster_dbt.utils import ASSET_RESOURCE_TYPES
|
|
44
56
|
|
|
45
57
|
|
|
@@ -63,29 +75,18 @@ class DbtProjectArgs(dg.Resolvable):
|
|
|
63
75
|
state_path: Optional[str] = None
|
|
64
76
|
|
|
65
77
|
|
|
66
|
-
def resolve_dbt_project(context: ResolutionContext, model) ->
|
|
67
|
-
if isinstance(model,
|
|
68
|
-
return
|
|
69
|
-
context.resolve_source_relative_path(
|
|
70
|
-
context.resolve_value(model, as_type=str),
|
|
71
|
-
)
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
args = DbtProjectArgs.resolve_from_model(context, model)
|
|
75
|
-
|
|
76
|
-
kwargs = {} # use optionally splatted kwargs to avoid redefining default value
|
|
77
|
-
if args.target_path:
|
|
78
|
-
kwargs["target_path"] = args.target_path
|
|
78
|
+
def resolve_dbt_project(context: ResolutionContext, model) -> DbtProjectManager:
|
|
79
|
+
if isinstance(model, RemoteGitDbtProjectManager.model()):
|
|
80
|
+
return RemoteGitDbtProjectManager.resolve_from_model(context, model)
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
project_dir=context.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
state_path=args.state_path,
|
|
85
|
-
packaged_project_dir=args.packaged_project_dir,
|
|
86
|
-
profile=args.profile,
|
|
87
|
-
**kwargs,
|
|
82
|
+
args = (
|
|
83
|
+
DbtProjectArgs(project_dir=context.resolve_value(model, as_type=str))
|
|
84
|
+
if isinstance(model, str)
|
|
85
|
+
else DbtProjectArgs.resolve_from_model(context, model)
|
|
88
86
|
)
|
|
87
|
+
# resolve the project_dir relative to where this component is defined
|
|
88
|
+
args = replace(args, project_dir=context.resolve_source_relative_path(args.project_dir))
|
|
89
|
+
return DbtProjectArgsManager(args)
|
|
89
90
|
|
|
90
91
|
|
|
91
92
|
DbtMetadataAddons: TypeAlias = Literal["column_metadata", "row_count"]
|
|
@@ -128,10 +129,10 @@ class DbtProjectComponent(StateBackedComponent, dg.Resolvable):
|
|
|
128
129
|
"""
|
|
129
130
|
|
|
130
131
|
project: Annotated[
|
|
131
|
-
DbtProject,
|
|
132
|
+
Union[DbtProject, DbtProjectManager],
|
|
132
133
|
Resolver(
|
|
133
134
|
resolve_dbt_project,
|
|
134
|
-
model_field_type=Union[str, DbtProjectArgs.model()],
|
|
135
|
+
model_field_type=Union[str, DbtProjectArgs.model(), RemoteGitDbtProjectManager.model()],
|
|
135
136
|
description="The path to the dbt project or a mapping defining a DbtProject",
|
|
136
137
|
examples=[
|
|
137
138
|
"{{ project_root }}/path/to/dbt_project",
|
|
@@ -199,6 +200,13 @@ class DbtProjectComponent(StateBackedComponent, dg.Resolvable):
|
|
|
199
200
|
examples=["tag:skip_dagster"],
|
|
200
201
|
),
|
|
201
202
|
] = DBT_DEFAULT_EXCLUDE
|
|
203
|
+
selector: Annotated[
|
|
204
|
+
str,
|
|
205
|
+
Resolver.default(
|
|
206
|
+
description="The dbt selector for models in the project you want to include.",
|
|
207
|
+
examples=["custom_selector"],
|
|
208
|
+
),
|
|
209
|
+
] = DBT_DEFAULT_SELECTOR
|
|
202
210
|
translation: Annotated[
|
|
203
211
|
Optional[TranslationFn[Mapping[str, Any]]],
|
|
204
212
|
TranslationFnResolver(template_vars_for_translation_fn=lambda data: {"node": data}),
|
|
@@ -224,11 +232,38 @@ class DbtProjectComponent(StateBackedComponent, dg.Resolvable):
|
|
|
224
232
|
@property
|
|
225
233
|
def defs_state_config(self) -> DefsStateConfig:
|
|
226
234
|
return DefsStateConfig(
|
|
227
|
-
key=f"
|
|
235
|
+
key=f"DbtProjectComponent[{self._project_manager.defs_state_discriminator}]",
|
|
228
236
|
management_type=DefsStateManagementType.LOCAL_FILESYSTEM,
|
|
229
237
|
refresh_if_dev=self.prepare_if_dev,
|
|
230
238
|
)
|
|
231
239
|
|
|
240
|
+
@property
|
|
241
|
+
def op_config_schema(self) -> Optional[type[dg.Config]]:
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def config_cls(self) -> Optional[type[dg.Config]]:
|
|
246
|
+
"""Internal property that returns the config schema for the op.
|
|
247
|
+
|
|
248
|
+
Delegates to op_config_schema for backwards compatibility and consistency
|
|
249
|
+
with other component types.
|
|
250
|
+
"""
|
|
251
|
+
return self.op_config_schema
|
|
252
|
+
|
|
253
|
+
def _get_op_spec(self, project: DbtProject) -> OpSpec:
|
|
254
|
+
default = self.op or OpSpec(name=project.name)
|
|
255
|
+
# always inject required tags
|
|
256
|
+
return default.model_copy(
|
|
257
|
+
update=dict(
|
|
258
|
+
tags={
|
|
259
|
+
**(default.tags or {}),
|
|
260
|
+
**({DAGSTER_DBT_SELECT_METADATA_KEY: self.select} if self.select else {}),
|
|
261
|
+
**({DAGSTER_DBT_EXCLUDE_METADATA_KEY: self.exclude} if self.exclude else {}),
|
|
262
|
+
**({DAGSTER_DBT_SELECTOR_METADATA_KEY: self.selector} if self.selector else {}),
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
|
|
232
267
|
@cached_property
|
|
233
268
|
def translator(self) -> "DagsterDbtTranslator":
|
|
234
269
|
return DbtProjectComponentTranslator(self, self.translation_settings)
|
|
@@ -309,46 +344,60 @@ class DbtProjectComponent(StateBackedComponent, dg.Resolvable):
|
|
|
309
344
|
return self._base_translator.get_asset_check_spec(asset_spec, manifest, unique_id, project)
|
|
310
345
|
|
|
311
346
|
@cached_property
|
|
312
|
-
def
|
|
313
|
-
|
|
347
|
+
def _project_manager(self) -> DbtProjectManager:
|
|
348
|
+
if isinstance(self.project, DbtProject):
|
|
349
|
+
return NoopDbtProjectManager(self.project)
|
|
350
|
+
else:
|
|
351
|
+
return self.project
|
|
352
|
+
|
|
353
|
+
@cached_property
|
|
354
|
+
def dbt_project(self) -> DbtProject:
|
|
355
|
+
return self._project_manager.get_project(None)
|
|
314
356
|
|
|
315
357
|
def get_asset_selection(
|
|
316
358
|
self, select: str, exclude: str = DBT_DEFAULT_EXCLUDE
|
|
317
359
|
) -> DbtManifestAssetSelection:
|
|
318
360
|
return DbtManifestAssetSelection.build(
|
|
319
|
-
manifest=self.
|
|
361
|
+
manifest=self.dbt_project.manifest_path,
|
|
320
362
|
dagster_dbt_translator=self.translator,
|
|
321
363
|
select=select,
|
|
322
364
|
exclude=exclude,
|
|
323
365
|
)
|
|
324
366
|
|
|
325
367
|
def write_state_to_path(self, state_path: Path) -> None:
|
|
326
|
-
|
|
327
|
-
self.project.preparer.prepare(self.project)
|
|
328
|
-
# move the manifest to the correct path
|
|
329
|
-
shutil.copyfile(self.project.manifest_path, state_path)
|
|
368
|
+
self._project_manager.prepare(state_path)
|
|
330
369
|
|
|
331
370
|
def build_defs_from_state(
|
|
332
371
|
self, context: dg.ComponentLoadContext, state_path: Optional[Path]
|
|
333
372
|
) -> dg.Definitions:
|
|
334
|
-
|
|
335
|
-
shutil.copyfile(state_path, self.project.manifest_path)
|
|
373
|
+
project = self._project_manager.get_project(state_path)
|
|
336
374
|
|
|
337
375
|
res_ctx = context.resolution_context
|
|
338
376
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
project
|
|
342
|
-
name=self.op.name if self.op else self.project.name,
|
|
343
|
-
op_tags=self.op.tags if self.op else None,
|
|
344
|
-
dagster_dbt_translator=self.translator,
|
|
377
|
+
asset_specs, check_specs = build_dbt_specs(
|
|
378
|
+
translator=validate_translator(self.translator),
|
|
379
|
+
manifest=validate_manifest(project.manifest_path),
|
|
345
380
|
select=self.select,
|
|
346
381
|
exclude=self.exclude,
|
|
347
|
-
|
|
382
|
+
selector=self.selector,
|
|
383
|
+
project=project,
|
|
384
|
+
io_manager_key=None,
|
|
385
|
+
)
|
|
386
|
+
op_spec = self._get_op_spec(project)
|
|
387
|
+
|
|
388
|
+
@dg.multi_asset(
|
|
389
|
+
specs=asset_specs,
|
|
390
|
+
check_specs=check_specs,
|
|
391
|
+
can_subset=True,
|
|
392
|
+
name=op_spec.name,
|
|
393
|
+
op_tags=op_spec.tags,
|
|
394
|
+
backfill_policy=op_spec.backfill_policy,
|
|
395
|
+
pool=op_spec.pool,
|
|
396
|
+
config_schema=self.config_cls.to_fields_dict() if self.config_cls else None,
|
|
348
397
|
)
|
|
349
398
|
def _fn(context: dg.AssetExecutionContext):
|
|
350
399
|
with _set_resolution_context(res_ctx):
|
|
351
|
-
yield from self.execute(context=context, dbt=
|
|
400
|
+
yield from self.execute(context=context, dbt=DbtCliResource(project))
|
|
352
401
|
|
|
353
402
|
return dg.Definitions(assets=[_fn])
|
|
354
403
|
|
|
@@ -437,7 +486,7 @@ class DbtProjectComponent(StateBackedComponent, dg.Resolvable):
|
|
|
437
486
|
|
|
438
487
|
@cached_property
|
|
439
488
|
def _validated_manifest(self):
|
|
440
|
-
return validate_manifest(self.
|
|
489
|
+
return validate_manifest(self.dbt_project.manifest_path)
|
|
441
490
|
|
|
442
491
|
@cached_property
|
|
443
492
|
def _validated_translator(self):
|
|
@@ -460,7 +509,7 @@ class DbtProjectComponent(StateBackedComponent, dg.Resolvable):
|
|
|
460
509
|
return dagster_dbt_translator.get_asset_spec(
|
|
461
510
|
manifest,
|
|
462
511
|
next(iter(matching_model_ids)),
|
|
463
|
-
self.
|
|
512
|
+
self.dbt_project,
|
|
464
513
|
).key
|
|
465
514
|
|
|
466
515
|
|
|
@@ -492,4 +541,4 @@ def get_projects_from_dbt_component(components: Path) -> list[DbtProject]:
|
|
|
492
541
|
of_type=DbtProjectComponent
|
|
493
542
|
)
|
|
494
543
|
|
|
495
|
-
return [component.
|
|
544
|
+
return [component.dbt_project for component in project_components]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import dagster._check as check
|
|
6
|
+
from dagster._core.errors import DagsterInvalidInvocationError
|
|
7
|
+
from dagster.components.component.component_scaffolder import Scaffolder
|
|
8
|
+
from dagster.components.component_scaffolding import scaffold_component
|
|
9
|
+
from dagster.components.scaffold.scaffold import ScaffoldRequest
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DbtScaffoldParams(BaseModel):
|
|
14
|
+
init: bool = Field(default=False)
|
|
15
|
+
project_path: Optional[str] = None
|
|
16
|
+
git_url: Optional[str] = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DbtProjectComponentScaffolder(Scaffolder[DbtScaffoldParams]):
|
|
20
|
+
@classmethod
|
|
21
|
+
def get_scaffold_params(cls) -> type[DbtScaffoldParams]:
|
|
22
|
+
return DbtScaffoldParams
|
|
23
|
+
|
|
24
|
+
def _init_dbt_project(self) -> None:
|
|
25
|
+
try:
|
|
26
|
+
from dbt.cli.main import dbtRunner
|
|
27
|
+
except ImportError:
|
|
28
|
+
raise DagsterInvalidInvocationError(
|
|
29
|
+
"dbt-core is not installed. Please install dbt to scaffold this component."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
dbtRunner().invoke(["init"])
|
|
33
|
+
|
|
34
|
+
def _git_url_params(self, git_url: str, project_path: Optional[str]) -> dict[str, Any]:
|
|
35
|
+
repo_relative_path = {"repo_relative_path": project_path} if project_path else {}
|
|
36
|
+
return {"project": {"repo_url": git_url, **repo_relative_path}}
|
|
37
|
+
|
|
38
|
+
def scaffold(self, request: ScaffoldRequest[DbtScaffoldParams]) -> None:
|
|
39
|
+
project_root = request.project_root or Path(os.getcwd())
|
|
40
|
+
check.param_invariant(
|
|
41
|
+
not (request.params.init and (request.params.git_url or request.params.project_path)),
|
|
42
|
+
"init and git_url/project_path cannot be set at the same time.",
|
|
43
|
+
)
|
|
44
|
+
if request.params.init:
|
|
45
|
+
self._init_dbt_project()
|
|
46
|
+
subpaths = [
|
|
47
|
+
path for path in project_root.iterdir() if path.is_dir() and path.name != "logs"
|
|
48
|
+
]
|
|
49
|
+
check.invariant(len(subpaths) == 1, "Expected exactly one subpath to be created.")
|
|
50
|
+
# this path should be relative to this directory
|
|
51
|
+
params = {"project": subpaths[0].name}
|
|
52
|
+
elif request.params.git_url is not None:
|
|
53
|
+
# project_path is the path to the dbt project within the git repository
|
|
54
|
+
project_path = request.params.project_path
|
|
55
|
+
repo_relative_path = {"repo_relative_path": project_path} if project_path else {}
|
|
56
|
+
params = {"project": {"repo_url": request.params.git_url, **repo_relative_path}}
|
|
57
|
+
elif request.params.project_path is not None:
|
|
58
|
+
# project_path represents a local directory
|
|
59
|
+
project_root_tmpl = "{{ project_root }}"
|
|
60
|
+
rel_path = os.path.relpath(request.params.project_path, start=project_root)
|
|
61
|
+
path_str = f"{project_root_tmpl}/{rel_path}"
|
|
62
|
+
params = {"project": path_str}
|
|
63
|
+
else:
|
|
64
|
+
params = {"project": None}
|
|
65
|
+
scaffold_component(request, params)
|
|
@@ -8,7 +8,7 @@ import sys
|
|
|
8
8
|
from collections.abc import Iterator, Mapping, Sequence
|
|
9
9
|
from dataclasses import dataclass, field, replace
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any, Final, NamedTuple, Optional, Union, cast
|
|
11
|
+
from typing import Any, Final, Literal, NamedTuple, Optional, Union, cast
|
|
12
12
|
|
|
13
13
|
import orjson
|
|
14
14
|
from dagster import (
|
|
@@ -24,7 +24,6 @@ from dagster import (
|
|
|
24
24
|
from dagster._annotations import public
|
|
25
25
|
from dagster._core.errors import DagsterExecutionInterruptedError
|
|
26
26
|
from packaging import version
|
|
27
|
-
from typing_extensions import Literal
|
|
28
27
|
|
|
29
28
|
from dagster_dbt.compat import BaseAdapter, BaseColumn, BaseRelation
|
|
30
29
|
from dagster_dbt.core.dbt_cli_event import (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from collections.abc import Iterator
|
|
1
|
+
from collections.abc import Callable, Iterator
|
|
2
2
|
from concurrent.futures import ThreadPoolExecutor
|
|
3
|
-
from typing import TYPE_CHECKING, Any,
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
4
4
|
|
|
5
5
|
from dagster import (
|
|
6
6
|
AssetCheckResult,
|
|
@@ -428,7 +428,7 @@ class DbtCliResource(ConfigurableResource):
|
|
|
428
428
|
adapter = get_adapter(config)
|
|
429
429
|
manifest = ManifestLoader.load_macros(
|
|
430
430
|
config,
|
|
431
|
-
adapter.connections.set_query_header,
|
|
431
|
+
adapter.connections.set_query_header,
|
|
432
432
|
base_macros_only=True,
|
|
433
433
|
)
|
|
434
434
|
adapter.set_macro_resolver(manifest)
|
|
@@ -627,8 +627,12 @@ class DbtCliResource(ConfigurableResource):
|
|
|
627
627
|
dagster_dbt_translator = updated_params.dagster_dbt_translator
|
|
628
628
|
selection_args = updated_params.selection_args
|
|
629
629
|
indirect_selection = updated_params.indirect_selection
|
|
630
|
-
|
|
631
630
|
target_path = target_path or self._get_unique_target_path(context=context)
|
|
631
|
+
project_dir = Path(
|
|
632
|
+
updated_params.dbt_project.project_dir
|
|
633
|
+
if updated_params.dbt_project
|
|
634
|
+
else self.project_dir
|
|
635
|
+
)
|
|
632
636
|
env = {
|
|
633
637
|
# Allow IO streaming when running in Windows.
|
|
634
638
|
# Also, allow it to be overriden by the current environment.
|
|
@@ -659,7 +663,7 @@ class DbtCliResource(ConfigurableResource):
|
|
|
659
663
|
**({"DBT_PROFILES_DIR": self.profiles_dir} if self.profiles_dir else {}),
|
|
660
664
|
# The DBT_PROJECT_DIR environment variable is set to the path containing the dbt project
|
|
661
665
|
# See https://docs.getdbt.com/reference/dbt_project.yml for more information.
|
|
662
|
-
|
|
666
|
+
"DBT_PROJECT_DIR": str(project_dir),
|
|
663
667
|
}
|
|
664
668
|
|
|
665
669
|
# set dbt indirect selection if needed to execute specific dbt tests due to asset check
|
|
@@ -683,14 +687,13 @@ class DbtCliResource(ConfigurableResource):
|
|
|
683
687
|
*profile_args,
|
|
684
688
|
*selection_args,
|
|
685
689
|
]
|
|
686
|
-
project_dir = Path(self.project_dir)
|
|
687
690
|
|
|
688
691
|
if not target_path.is_absolute():
|
|
689
692
|
target_path = project_dir.joinpath(target_path)
|
|
690
693
|
|
|
691
694
|
# run dbt --version to get the dbt core version
|
|
692
695
|
adapter: Optional[BaseAdapter] = None
|
|
693
|
-
with pushd(
|
|
696
|
+
with pushd(str(project_dir)):
|
|
694
697
|
# we do not need to initialize the adapter if we are using the fusion engine
|
|
695
698
|
if self._cli_version.major < 2:
|
|
696
699
|
try:
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import asdict, dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING, Annotated, Optional
|
|
7
|
+
from urllib.parse import quote, urlparse, urlunparse
|
|
8
|
+
|
|
9
|
+
from dagster._core.errors import DagsterInvalidDefinitionError
|
|
10
|
+
from dagster.components.resolved.base import Resolvable
|
|
11
|
+
from dagster.components.resolved.model import Resolver
|
|
12
|
+
|
|
13
|
+
from dagster_dbt.dbt_project import DbtProject
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from dagster_dbt.components.dbt_project.component import DbtProjectArgs
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DbtProjectManager(ABC):
|
|
20
|
+
"""Helper class that wraps a dbt project that may or may not be available on local disk at the time
|
|
21
|
+
it is instantiated. Provides methods for syncing the project to a local path and for instantiating the
|
|
22
|
+
final DbtProject object when ready.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def defs_state_discriminator(self) -> str: ...
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def sync(self, state_path: Path) -> None: ...
|
|
31
|
+
|
|
32
|
+
def _local_project_dir(self, state_path: Path) -> Path:
|
|
33
|
+
return state_path.parent / "project"
|
|
34
|
+
|
|
35
|
+
def prepare(self, state_path: Path) -> None:
|
|
36
|
+
"""Syncs the project to the given local path and ensures the manifest is generated
|
|
37
|
+
and the dependencies are installed.
|
|
38
|
+
"""
|
|
39
|
+
# ensure local dir is empty
|
|
40
|
+
local_dir = self._local_project_dir(state_path)
|
|
41
|
+
shutil.rmtree(local_dir, ignore_errors=True)
|
|
42
|
+
local_dir.mkdir()
|
|
43
|
+
|
|
44
|
+
# ensure project exists in the dir and is compiled
|
|
45
|
+
self.sync(state_path)
|
|
46
|
+
|
|
47
|
+
# indicate that project has been prepared
|
|
48
|
+
state_path.touch()
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def get_project(self, state_path: Optional[Path]) -> "DbtProject": ...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class NoopDbtProjectManager(DbtProjectManager):
|
|
56
|
+
"""Wraps a DbtProject that has already been fully instantiated. Used for cases where a
|
|
57
|
+
user directly provides a DbtProject to the DbtProjectComponent.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
project: "DbtProject"
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def defs_state_discriminator(self) -> str:
|
|
64
|
+
return self.project.name
|
|
65
|
+
|
|
66
|
+
def sync(self, state_path: Path) -> None:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
def get_project(self, state_path: Optional[Path]) -> DbtProject:
|
|
70
|
+
return self.project
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class DbtProjectArgsManager(DbtProjectManager):
|
|
75
|
+
"""Wraps DbtProjectArgs provided to the DbtProjectComponent. Avoids instantiating the DbtProject object
|
|
76
|
+
immediately as this would cause errors in cases where the project_dir has not yet been synced.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
args: "DbtProjectArgs"
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def defs_state_discriminator(self) -> str:
|
|
83
|
+
return Path(self.args.project_dir).stem
|
|
84
|
+
|
|
85
|
+
def sync(self, state_path: Path) -> None:
|
|
86
|
+
# we prepare the project in the original project directory rather than the new one
|
|
87
|
+
# so that code that does not have access to the local state path can still access
|
|
88
|
+
# the manifest.json file.
|
|
89
|
+
project = self.get_project(None)
|
|
90
|
+
project.preparer.prepare(project)
|
|
91
|
+
shutil.copytree(
|
|
92
|
+
self.args.project_dir, self._local_project_dir(state_path), dirs_exist_ok=True
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def get_project(self, state_path: Optional[Path]) -> "DbtProject":
|
|
96
|
+
kwargs = asdict(self.args)
|
|
97
|
+
|
|
98
|
+
project_dir = self._local_project_dir(state_path) if state_path else self.args.project_dir
|
|
99
|
+
return DbtProject(
|
|
100
|
+
project_dir=project_dir,
|
|
101
|
+
# allow default values on DbtProject to take precedence
|
|
102
|
+
**{k: v for k, v in kwargs.items() if v is not None and k != "project_dir"},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class RemoteGitDbtProjectManager(DbtProjectManager, Resolvable):
|
|
108
|
+
"""Wraps a remote git repository containing a dbt project."""
|
|
109
|
+
|
|
110
|
+
repo_url: str
|
|
111
|
+
repo_relative_path: Annotated[
|
|
112
|
+
str,
|
|
113
|
+
Resolver.default(
|
|
114
|
+
description="The relative path to the dbt project within the repository.",
|
|
115
|
+
examples=["dbt/my_dbt_project"],
|
|
116
|
+
),
|
|
117
|
+
] = "."
|
|
118
|
+
token: Annotated[
|
|
119
|
+
Optional[str],
|
|
120
|
+
Resolver.default(
|
|
121
|
+
description="Token for authenticating to the provided repository.",
|
|
122
|
+
examples=["'{{ env.GITHUB_TOKEN }}'"],
|
|
123
|
+
),
|
|
124
|
+
] = None
|
|
125
|
+
profile: Annotated[
|
|
126
|
+
Optional[str],
|
|
127
|
+
Resolver.default(description="The profile to use for the dbt project."),
|
|
128
|
+
] = None
|
|
129
|
+
target: Annotated[
|
|
130
|
+
Optional[str],
|
|
131
|
+
Resolver.default(
|
|
132
|
+
description="The target to use for the dbt project.",
|
|
133
|
+
examples=["dev", "prod"],
|
|
134
|
+
),
|
|
135
|
+
] = None
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def defs_state_discriminator(self) -> str:
|
|
139
|
+
return self.repo_url
|
|
140
|
+
|
|
141
|
+
def _get_clone_url(self) -> str:
|
|
142
|
+
# in github action environments, the token will be available automatically as an env var
|
|
143
|
+
raw_token = self.token or os.getenv("GITHUB_TOKEN")
|
|
144
|
+
if raw_token is None:
|
|
145
|
+
return self.repo_url
|
|
146
|
+
else:
|
|
147
|
+
# insert the token into the url
|
|
148
|
+
token = quote(raw_token, safe="")
|
|
149
|
+
parts = urlparse(self.repo_url)
|
|
150
|
+
return urlunparse(parts._replace(netloc=f"{token}@{parts.netloc}"))
|
|
151
|
+
|
|
152
|
+
def sync(self, state_path: Path) -> None:
|
|
153
|
+
# defer git import to avoid side-effects on import
|
|
154
|
+
from git import Repo
|
|
155
|
+
|
|
156
|
+
Repo.clone_from(self._get_clone_url(), self._local_project_dir(state_path), depth=1)
|
|
157
|
+
project = self.get_project(state_path)
|
|
158
|
+
project.preparer.prepare(project)
|
|
159
|
+
|
|
160
|
+
def get_project(self, state_path: Optional[Path]) -> "DbtProject":
|
|
161
|
+
if state_path is None:
|
|
162
|
+
raise DagsterInvalidDefinitionError(
|
|
163
|
+
"Attempted to `get_project()` on a `RemoteGitDbtProjectWrapper` without a path. "
|
|
164
|
+
"This can happen when calling unsupported methods on the `DbtProjectComponent`."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
local_dir = self._local_project_dir(state_path)
|
|
168
|
+
return DbtProject(
|
|
169
|
+
project_dir=local_dir / self.repo_relative_path,
|
|
170
|
+
profile=self.profile,
|
|
171
|
+
target=self.target,
|
|
172
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.28.2"
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dagster-dbt
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.2
|
|
4
4
|
Summary: A Dagster integration for dbt
|
|
5
5
|
Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-dbt
|
|
6
6
|
Author: Dagster Labs
|
|
7
7
|
Author-email: hello@dagsterlabs.com
|
|
8
8
|
License: Apache-2.0
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
14
|
Classifier: Operating System :: OS Independent
|
|
16
|
-
Requires-Python: >=3.
|
|
15
|
+
Requires-Python: >=3.10,<3.14
|
|
17
16
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: dagster==1.12.
|
|
17
|
+
Requires-Dist: dagster==1.12.2
|
|
19
18
|
Requires-Dist: dbt-core<1.11,>=1.7
|
|
19
|
+
Requires-Dist: gitpython
|
|
20
20
|
Requires-Dist: Jinja2
|
|
21
21
|
Requires-Dist: networkx
|
|
22
22
|
Requires-Dist: orjson
|
|
@@ -13,6 +13,7 @@ dagster_dbt/dbt_core_version.py
|
|
|
13
13
|
dagster_dbt/dbt_manifest.py
|
|
14
14
|
dagster_dbt/dbt_manifest_asset_selection.py
|
|
15
15
|
dagster_dbt/dbt_project.py
|
|
16
|
+
dagster_dbt/dbt_project_manager.py
|
|
16
17
|
dagster_dbt/dbt_version.py
|
|
17
18
|
dagster_dbt/errors.py
|
|
18
19
|
dagster_dbt/freshness_builder.py
|
|
@@ -28,7 +28,6 @@ setup(
|
|
|
28
28
|
description="A Dagster integration for dbt",
|
|
29
29
|
url="https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-dbt",
|
|
30
30
|
classifiers=[
|
|
31
|
-
"Programming Language :: Python :: 3.9",
|
|
32
31
|
"Programming Language :: Python :: 3.10",
|
|
33
32
|
"Programming Language :: Python :: 3.11",
|
|
34
33
|
"Programming Language :: Python :: 3.12",
|
|
@@ -38,11 +37,12 @@ setup(
|
|
|
38
37
|
],
|
|
39
38
|
packages=find_packages(exclude=["dagster_dbt_tests*"]),
|
|
40
39
|
include_package_data=True,
|
|
41
|
-
python_requires=">=3.
|
|
40
|
+
python_requires=">=3.10,<3.14",
|
|
42
41
|
install_requires=[
|
|
43
|
-
"dagster==1.12.
|
|
42
|
+
"dagster==1.12.2",
|
|
44
43
|
# Follow the version support constraints for dbt Core: https://docs.getdbt.com/docs/dbt-versions/core
|
|
45
44
|
f"dbt-core>=1.7,<{DBT_CORE_VERSION_UPPER_BOUND}",
|
|
45
|
+
"gitpython",
|
|
46
46
|
"Jinja2",
|
|
47
47
|
"networkx",
|
|
48
48
|
"orjson",
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
import dagster._check as check
|
|
6
|
-
from dagster._core.errors import DagsterInvalidInvocationError
|
|
7
|
-
from dagster.components.component.component_scaffolder import Scaffolder
|
|
8
|
-
from dagster.components.component_scaffolding import scaffold_component
|
|
9
|
-
from dagster.components.scaffold.scaffold import ScaffoldRequest
|
|
10
|
-
from pydantic import BaseModel, Field
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class DbtScaffoldParams(BaseModel):
|
|
14
|
-
init: bool = Field(default=False)
|
|
15
|
-
project_path: Optional[str] = None
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class DbtProjectComponentScaffolder(Scaffolder[DbtScaffoldParams]):
|
|
19
|
-
@classmethod
|
|
20
|
-
def get_scaffold_params(cls) -> type[DbtScaffoldParams]:
|
|
21
|
-
return DbtScaffoldParams
|
|
22
|
-
|
|
23
|
-
def scaffold(self, request: ScaffoldRequest[DbtScaffoldParams]) -> None:
|
|
24
|
-
project_root = request.project_root or os.getcwd()
|
|
25
|
-
if request.params.project_path:
|
|
26
|
-
project_root_tmpl = "{{ project_root }}"
|
|
27
|
-
rel_path = os.path.relpath(request.params.project_path, start=project_root)
|
|
28
|
-
path_str = f"{project_root_tmpl}/{rel_path}"
|
|
29
|
-
|
|
30
|
-
elif request.params.init:
|
|
31
|
-
try:
|
|
32
|
-
from dbt.cli.main import dbtRunner
|
|
33
|
-
except ImportError:
|
|
34
|
-
raise DagsterInvalidInvocationError(
|
|
35
|
-
"dbt-core is not installed. Please install dbt to scaffold this component."
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
dbtRunner().invoke(["init"])
|
|
39
|
-
subpaths = [
|
|
40
|
-
path
|
|
41
|
-
for path in Path(project_root).iterdir()
|
|
42
|
-
if path.is_dir() and path.name != "logs"
|
|
43
|
-
]
|
|
44
|
-
check.invariant(len(subpaths) == 1, "Expected exactly one subpath to be created.")
|
|
45
|
-
# this path should be relative to this directory
|
|
46
|
-
path_str = subpaths[0].name
|
|
47
|
-
else:
|
|
48
|
-
path_str = None
|
|
49
|
-
|
|
50
|
-
scaffold_component(request, {"project": path_str})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.28.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|