airflow-dbt-python 3.0.1__tar.gz → 3.0.3__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.
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/PKG-INFO +38 -37
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/README.md +7 -7
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/__version__.py +1 -1
- airflow_dbt_python-3.0.3/airflow_dbt_python/hooks/__init__.py +1 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/dbt.py +5 -1
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/fs/__init__.py +3 -4
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/target.py +32 -2
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/operators/dbt.py +17 -9
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/utils/configs.py +9 -3
- airflow_dbt_python-3.0.3/airflow_dbt_python.egg-info/PKG-INFO +285 -0
- airflow_dbt_python-3.0.3/airflow_dbt_python.egg-info/SOURCES.txt +26 -0
- airflow_dbt_python-3.0.3/airflow_dbt_python.egg-info/dependency_links.txt +1 -0
- airflow_dbt_python-3.0.3/airflow_dbt_python.egg-info/requires.txt +42 -0
- airflow_dbt_python-3.0.3/airflow_dbt_python.egg-info/top_level.txt +1 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/pyproject.toml +32 -64
- airflow_dbt_python-3.0.3/setup.cfg +4 -0
- airflow_dbt_python-3.0.1/airflow_dbt_python/hooks/__init__.py +0 -1
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/LICENSE +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/__init__.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/fs/gcs.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/fs/git.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/fs/local.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/fs/s3.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/operators/__init__.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/utils/__init__.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/utils/enums.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/utils/env.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/utils/url.py +0 -0
- {airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/utils/version.py +0 -0
|
@@ -1,49 +1,51 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: airflow-dbt-python
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.3
|
|
4
4
|
Summary: A collection of Airflow operators, hooks, and utilities to execute dbt commands
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Author-email: Tomás Farías Santana <tomas@tomasfarias.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/tomasfarias/airflow-dbt-python
|
|
8
|
+
Project-URL: documentation, https://airflow-dbt-python.readthedocs.io
|
|
9
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
|
|
15
|
+
Requires-Python: <3.13,>=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: apache-airflow>=2.8
|
|
19
|
+
Requires-Dist: contextlib-chdir==1.0.2; python_version < "3.11"
|
|
20
|
+
Requires-Dist: dbt-core<2.0.0,>=1.8.0
|
|
17
21
|
Provides-Extra: airflow-providers
|
|
22
|
+
Requires-Dist: apache-airflow-providers-amazon>=3.0.0; extra == "airflow-providers"
|
|
23
|
+
Requires-Dist: apache-airflow-providers-google>=11.0.0; extra == "airflow-providers"
|
|
24
|
+
Requires-Dist: apache-airflow-providers-ssh>=3.0.0; extra == "airflow-providers"
|
|
25
|
+
Provides-Extra: adapters
|
|
26
|
+
Requires-Dist: dbt-bigquery<2.0.0,>=1.8.0; extra == "adapters"
|
|
27
|
+
Requires-Dist: dbt-redshift<2.0.0,>=1.8.0; extra == "adapters"
|
|
28
|
+
Requires-Dist: dbt-postgres<2.0.0,>=1.8.0; extra == "adapters"
|
|
29
|
+
Requires-Dist: dbt-snowflake<2.0.0,>=1.8.0; extra == "adapters"
|
|
30
|
+
Requires-Dist: dbt-spark[all]<2.0.0,>=1.8.0; extra == "adapters"
|
|
18
31
|
Provides-Extra: bigquery
|
|
32
|
+
Requires-Dist: dbt-bigquery<2.0.0,>=1.8.0; extra == "bigquery"
|
|
19
33
|
Provides-Extra: gcs
|
|
34
|
+
Requires-Dist: apache-airflow-providers-google>=11.0.0; extra == "gcs"
|
|
20
35
|
Provides-Extra: git
|
|
36
|
+
Requires-Dist: apache-airflow-providers-ssh>=3.0.0; extra == "git"
|
|
37
|
+
Requires-Dist: dulwich>=0.21; extra == "git"
|
|
21
38
|
Provides-Extra: postgres
|
|
39
|
+
Requires-Dist: dbt-postgres<2.0.0,>=1.8.0; extra == "postgres"
|
|
22
40
|
Provides-Extra: redshift
|
|
41
|
+
Requires-Dist: dbt-redshift<2.0.0,>=1.8.0; extra == "redshift"
|
|
23
42
|
Provides-Extra: s3
|
|
43
|
+
Requires-Dist: apache-airflow-providers-amazon>=3.0.0; extra == "s3"
|
|
24
44
|
Provides-Extra: snowflake
|
|
45
|
+
Requires-Dist: dbt-snowflake<2.0.0,>=1.8.0; extra == "snowflake"
|
|
25
46
|
Provides-Extra: spark
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
|
|
28
|
-
Requires-Dist: apache-airflow-providers-amazon (>=3.0.0) ; extra == "s3"
|
|
29
|
-
Requires-Dist: apache-airflow-providers-google (>=11.0.0) ; extra == "airflow-providers"
|
|
30
|
-
Requires-Dist: apache-airflow-providers-google (>=11.0.0) ; extra == "gcs"
|
|
31
|
-
Requires-Dist: apache-airflow-providers-ssh (>=3.0.0) ; extra == "airflow-providers"
|
|
32
|
-
Requires-Dist: apache-airflow-providers-ssh (>=3.0.0) ; extra == "git"
|
|
33
|
-
Requires-Dist: contextlib-chdir (==1.0.2) ; python_version < "3.11"
|
|
34
|
-
Requires-Dist: dbt-bigquery (>=1.8.0,<2.0.0) ; extra == "adapters"
|
|
35
|
-
Requires-Dist: dbt-bigquery (>=1.8.0,<2.0.0) ; extra == "bigquery"
|
|
36
|
-
Requires-Dist: dbt-core (>=1.8.0,<2.0.0)
|
|
37
|
-
Requires-Dist: dbt-postgres (>=1.8.0,<2.0.0) ; extra == "adapters"
|
|
38
|
-
Requires-Dist: dbt-postgres (>=1.8.0,<2.0.0) ; extra == "postgres"
|
|
39
|
-
Requires-Dist: dbt-redshift (>=1.8.0,<2.0.0) ; extra == "adapters"
|
|
40
|
-
Requires-Dist: dbt-redshift (>=1.8.0,<2.0.0) ; extra == "redshift"
|
|
41
|
-
Requires-Dist: dbt-snowflake (>=1.8.0,<2.0.0) ; extra == "adapters"
|
|
42
|
-
Requires-Dist: dbt-snowflake (>=1.8.0,<2.0.0) ; extra == "snowflake"
|
|
43
|
-
Requires-Dist: dbt-spark (>=1.8.0,<2.0.0) ; extra == "spark"
|
|
44
|
-
Requires-Dist: dbt-spark[all] (>=1.8.0,<2.0.0) ; extra == "adapters"
|
|
45
|
-
Requires-Dist: dulwich (>=0.21) ; extra == "git"
|
|
46
|
-
Description-Content-Type: text/markdown
|
|
47
|
+
Requires-Dist: dbt-spark<2.0.0,>=1.8.0; extra == "spark"
|
|
48
|
+
Dynamic: license-file
|
|
47
49
|
|
|
48
50
|
# airflow-dbt-python
|
|
49
51
|
|
|
@@ -101,17 +103,17 @@ git clone https://github.com/tomasfarias/airflow-dbt-python.git
|
|
|
101
103
|
cd airflow-dbt-python
|
|
102
104
|
```
|
|
103
105
|
|
|
104
|
-
And installing with *
|
|
106
|
+
And installing with *uv*:
|
|
105
107
|
|
|
106
108
|
``` shell
|
|
107
|
-
|
|
109
|
+
uv pip install .
|
|
108
110
|
```
|
|
109
111
|
|
|
110
112
|
## In AWS MWAA
|
|
111
113
|
|
|
112
114
|
Add *airflow-dbt-python* to your `requirements.txt` file and edit your Airflow environment to use this new `requirements.txt` file, or upload it as a plugin.
|
|
113
115
|
|
|
114
|
-
Read the [documentation](https://airflow-dbt-python.readthedocs.io/en/latest/getting_started
|
|
116
|
+
Read the [documentation](https://airflow-dbt-python.readthedocs.io/en/latest/getting_started/#installing-in-mwaa) for more a more detailed AWS MWAA installation breakdown.
|
|
115
117
|
|
|
116
118
|
## In GCP Cloud Composer
|
|
117
119
|
|
|
@@ -245,7 +247,7 @@ with DAG(
|
|
|
245
247
|
) as dag:
|
|
246
248
|
dbt_test = DbtTestOperator(
|
|
247
249
|
task_id="dbt_test",
|
|
248
|
-
|
|
250
|
+
selector="pre-run-tests",
|
|
249
251
|
)
|
|
250
252
|
|
|
251
253
|
dbt_seed = DbtSeedOperator(
|
|
@@ -268,17 +270,16 @@ More examples can be found in the [`examples/`](examples/) directory and the [do
|
|
|
268
270
|
|
|
269
271
|
# Development
|
|
270
272
|
|
|
271
|
-
See the [development documentation](https://airflow-dbt-python.readthedocs.io/en/latest/development
|
|
273
|
+
See the [development documentation](https://airflow-dbt-python.readthedocs.io/en/latest/development/) for a more in-depth dive into setting up a development environment, running the test-suite, and general commentary on working on *airflow-dbt-python*.
|
|
272
274
|
|
|
273
275
|
## Testing
|
|
274
276
|
|
|
275
|
-
Tests are run with *pytest*, can be located in `tests/`. To run them locally, you may use *
|
|
277
|
+
Tests are run with *pytest*, can be located in `tests/`. To run them locally, you may use *uv*:
|
|
276
278
|
|
|
277
279
|
``` shell
|
|
278
|
-
|
|
280
|
+
uv run pytest tests/ -vv
|
|
279
281
|
```
|
|
280
282
|
|
|
281
283
|
# License
|
|
282
284
|
|
|
283
285
|
This project is licensed under the MIT license. See [LICENSE](LICENSE).
|
|
284
|
-
|
|
@@ -54,17 +54,17 @@ git clone https://github.com/tomasfarias/airflow-dbt-python.git
|
|
|
54
54
|
cd airflow-dbt-python
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
And installing with *
|
|
57
|
+
And installing with *uv*:
|
|
58
58
|
|
|
59
59
|
``` shell
|
|
60
|
-
|
|
60
|
+
uv pip install .
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
## In AWS MWAA
|
|
64
64
|
|
|
65
65
|
Add *airflow-dbt-python* to your `requirements.txt` file and edit your Airflow environment to use this new `requirements.txt` file, or upload it as a plugin.
|
|
66
66
|
|
|
67
|
-
Read the [documentation](https://airflow-dbt-python.readthedocs.io/en/latest/getting_started
|
|
67
|
+
Read the [documentation](https://airflow-dbt-python.readthedocs.io/en/latest/getting_started/#installing-in-mwaa) for more a more detailed AWS MWAA installation breakdown.
|
|
68
68
|
|
|
69
69
|
## In GCP Cloud Composer
|
|
70
70
|
|
|
@@ -198,7 +198,7 @@ with DAG(
|
|
|
198
198
|
) as dag:
|
|
199
199
|
dbt_test = DbtTestOperator(
|
|
200
200
|
task_id="dbt_test",
|
|
201
|
-
|
|
201
|
+
selector="pre-run-tests",
|
|
202
202
|
)
|
|
203
203
|
|
|
204
204
|
dbt_seed = DbtSeedOperator(
|
|
@@ -221,14 +221,14 @@ More examples can be found in the [`examples/`](examples/) directory and the [do
|
|
|
221
221
|
|
|
222
222
|
# Development
|
|
223
223
|
|
|
224
|
-
See the [development documentation](https://airflow-dbt-python.readthedocs.io/en/latest/development
|
|
224
|
+
See the [development documentation](https://airflow-dbt-python.readthedocs.io/en/latest/development/) for a more in-depth dive into setting up a development environment, running the test-suite, and general commentary on working on *airflow-dbt-python*.
|
|
225
225
|
|
|
226
226
|
## Testing
|
|
227
227
|
|
|
228
|
-
Tests are run with *pytest*, can be located in `tests/`. To run them locally, you may use *
|
|
228
|
+
Tests are run with *pytest*, can be located in `tests/`. To run them locally, you may use *uv*:
|
|
229
229
|
|
|
230
230
|
``` shell
|
|
231
|
-
|
|
231
|
+
uv run pytest tests/ -vv
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
# License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The hooks module provides Airflow hooks used to interact with dbt."""
|
{airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/hooks/fs/__init__.py
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""A ``DbtFSHook`` can download and upload dbt project files to a filesystem.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Currently, only AWS S3 and the local filesystem are supported as remotes.
|
|
3
|
+
By filesystem we understand any storage that can hold dbt project files, like an
|
|
4
|
+
S3 bucket or a git repository.
|
|
6
5
|
"""
|
|
7
6
|
|
|
8
7
|
from abc import ABC, abstractmethod
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
"""Provides
|
|
1
|
+
"""Provides hooks to establish a dbt connection with supported targets.
|
|
2
|
+
|
|
3
|
+
The connection details will be extracted from an Airflow connection, and each
|
|
4
|
+
target can interpret connection information differently according to its
|
|
5
|
+
specific connection requirements.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
from __future__ import annotations
|
|
4
9
|
|
|
10
|
+
import base64
|
|
11
|
+
import binascii
|
|
5
12
|
import json
|
|
6
13
|
import operator
|
|
7
14
|
import re
|
|
@@ -21,6 +28,24 @@ from airflow.hooks.base import BaseHook
|
|
|
21
28
|
from airflow.models.connection import Connection
|
|
22
29
|
|
|
23
30
|
|
|
31
|
+
def try_decode_base64(s: str) -> str:
|
|
32
|
+
"""Attempt to decode a string from base64.
|
|
33
|
+
|
|
34
|
+
If the string is not valid base64, returns the original value.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
s: The string to decode.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The decoded string, or the original value if decoding fails.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
s = base64.b64decode(s, validate=True).decode("utf-8")
|
|
44
|
+
except binascii.Error:
|
|
45
|
+
pass
|
|
46
|
+
return s
|
|
47
|
+
|
|
48
|
+
|
|
24
49
|
class DbtConnectionParam(NamedTuple):
|
|
25
50
|
"""A tuple indicating connection parameters relevant to dbt.
|
|
26
51
|
|
|
@@ -35,6 +60,7 @@ class DbtConnectionParam(NamedTuple):
|
|
|
35
60
|
name: str
|
|
36
61
|
store_override_name: Optional[str] = None
|
|
37
62
|
default: Optional[Any] = None
|
|
63
|
+
converter: Callable[[Any], Any] | None = None
|
|
38
64
|
|
|
39
65
|
@property
|
|
40
66
|
def override_name(self):
|
|
@@ -225,6 +251,8 @@ class DbtConnectionHook(BaseHook, ABC, metaclass=DbtConnectionHookMeta):
|
|
|
225
251
|
if isinstance(param, DbtConnectionParam):
|
|
226
252
|
key = param.override_name
|
|
227
253
|
value = getattr(conn, param.name, param.default)
|
|
254
|
+
if param.converter:
|
|
255
|
+
value = param.converter(value)
|
|
228
256
|
elif isinstance(param, DbtConnectionConditionParam):
|
|
229
257
|
key, default = param.resolve(conn)
|
|
230
258
|
value = getattr(conn, param.name, default)
|
|
@@ -394,7 +422,9 @@ class DbtSnowflakeHook(DbtConnectionHook):
|
|
|
394
422
|
"database",
|
|
395
423
|
DbtConnectionParam("refresh_token", "token"),
|
|
396
424
|
DbtConnectionParam("private_key_file", "private_key_path"),
|
|
397
|
-
DbtConnectionParam(
|
|
425
|
+
DbtConnectionParam(
|
|
426
|
+
"private_key_content", "private_key", converter=try_decode_base64
|
|
427
|
+
),
|
|
398
428
|
]
|
|
399
429
|
|
|
400
430
|
|
|
@@ -400,14 +400,15 @@ class DbtRunOperator(DbtBaseOperator):
|
|
|
400
400
|
full_refresh: Optional[bool] = None,
|
|
401
401
|
models: Optional[list[str]] = None,
|
|
402
402
|
select: Optional[list[str]] = None,
|
|
403
|
-
|
|
403
|
+
selector: Optional[str] = None,
|
|
404
|
+
selector_name: Optional[str] = None,
|
|
404
405
|
exclude: Optional[list[str]] = None,
|
|
405
406
|
**kwargs,
|
|
406
407
|
) -> None:
|
|
407
408
|
super().__init__(**kwargs)
|
|
408
409
|
self.full_refresh = full_refresh
|
|
409
410
|
self.exclude = exclude
|
|
410
|
-
self.
|
|
411
|
+
self.selector = selector or selector_name
|
|
411
412
|
self.select = select or models
|
|
412
413
|
|
|
413
414
|
@property
|
|
@@ -432,6 +433,7 @@ class DbtSeedOperator(DbtBaseOperator):
|
|
|
432
433
|
select: Optional[list[str]] = None,
|
|
433
434
|
show: Optional[bool] = None,
|
|
434
435
|
exclude: Optional[list[str]] = None,
|
|
436
|
+
selector: Optional[str] = None,
|
|
435
437
|
selector_name: Optional[str] = None,
|
|
436
438
|
**kwargs,
|
|
437
439
|
) -> None:
|
|
@@ -440,7 +442,7 @@ class DbtSeedOperator(DbtBaseOperator):
|
|
|
440
442
|
self.select = select
|
|
441
443
|
self.show = show
|
|
442
444
|
self.exclude = exclude
|
|
443
|
-
self.
|
|
445
|
+
self.selector = selector or selector_name
|
|
444
446
|
|
|
445
447
|
@property
|
|
446
448
|
def command(self) -> str:
|
|
@@ -465,6 +467,7 @@ class DbtTestOperator(DbtBaseOperator):
|
|
|
465
467
|
models: Optional[list[str]] = None,
|
|
466
468
|
select: Optional[list[str]] = None,
|
|
467
469
|
exclude: Optional[list[str]] = None,
|
|
470
|
+
selector: Optional[str] = None,
|
|
468
471
|
selector_name: Optional[str] = None,
|
|
469
472
|
indirect_selection: Optional[str] = None,
|
|
470
473
|
**kwargs,
|
|
@@ -473,7 +476,7 @@ class DbtTestOperator(DbtBaseOperator):
|
|
|
473
476
|
self.singular = singular
|
|
474
477
|
self.generic = generic
|
|
475
478
|
self.exclude = exclude
|
|
476
|
-
self.
|
|
479
|
+
self.selector = selector or selector_name
|
|
477
480
|
self.select = select or models
|
|
478
481
|
self.indirect_selection = indirect_selection
|
|
479
482
|
|
|
@@ -500,6 +503,7 @@ class DbtCompileOperator(_GraphRunnableOperator):
|
|
|
500
503
|
models: Optional[list[str]] = None,
|
|
501
504
|
select: Optional[list[str]] = None,
|
|
502
505
|
exclude: Optional[list[str]] = None,
|
|
506
|
+
selector: Optional[str] = None,
|
|
503
507
|
selector_name: Optional[str] = None,
|
|
504
508
|
upload_dbt_project: bool = True,
|
|
505
509
|
**kwargs,
|
|
@@ -508,7 +512,7 @@ class DbtCompileOperator(_GraphRunnableOperator):
|
|
|
508
512
|
self.parse_only = parse_only
|
|
509
513
|
self.full_refresh = full_refresh
|
|
510
514
|
self.exclude = exclude
|
|
511
|
-
self.
|
|
515
|
+
self.selector = selector or selector_name
|
|
512
516
|
self.select = select or models
|
|
513
517
|
self.upload_dbt_project = upload_dbt_project
|
|
514
518
|
|
|
@@ -614,13 +618,14 @@ class DbtSnapshotOperator(DbtBaseOperator):
|
|
|
614
618
|
self,
|
|
615
619
|
select: Optional[list[str]] = None,
|
|
616
620
|
exclude: Optional[list[str]] = None,
|
|
621
|
+
selector: Optional[str] = None,
|
|
617
622
|
selector_name: Optional[str] = None,
|
|
618
623
|
**kwargs,
|
|
619
624
|
) -> None:
|
|
620
625
|
super().__init__(**kwargs)
|
|
621
626
|
self.select = select
|
|
622
627
|
self.exclude = exclude
|
|
623
|
-
self.
|
|
628
|
+
self.selector = selector or selector_name
|
|
624
629
|
|
|
625
630
|
@property
|
|
626
631
|
def command(self) -> str:
|
|
@@ -644,6 +649,7 @@ class DbtLsOperator(_GraphRunnableOperator):
|
|
|
644
649
|
resource_types: Optional[list[str]] = None,
|
|
645
650
|
select: Optional[list[str]] = None,
|
|
646
651
|
exclude: Optional[list[str]] = None,
|
|
652
|
+
selector: Optional[str] = None,
|
|
647
653
|
selector_name: Optional[str] = None,
|
|
648
654
|
dbt_output: Optional[Output] = None,
|
|
649
655
|
output_keys: Optional[list[str]] = None,
|
|
@@ -654,7 +660,7 @@ class DbtLsOperator(_GraphRunnableOperator):
|
|
|
654
660
|
self.resource_types = resource_types
|
|
655
661
|
self.select = select
|
|
656
662
|
self.exclude = exclude
|
|
657
|
-
self.
|
|
663
|
+
self.selector = selector or selector_name
|
|
658
664
|
self.dbt_output = dbt_output
|
|
659
665
|
self.output_keys = output_keys
|
|
660
666
|
self.indirect_selection = indirect_selection
|
|
@@ -727,6 +733,7 @@ class DbtSourceFreshnessOperator(DbtBaseOperator):
|
|
|
727
733
|
select: Optional[list[str]] = None,
|
|
728
734
|
dbt_output: Optional[Union[str, Path]] = None,
|
|
729
735
|
exclude: Optional[list[str]] = None,
|
|
736
|
+
selector: Optional[str] = None,
|
|
730
737
|
selector_name: Optional[str] = None,
|
|
731
738
|
upload_dbt_project: bool = True,
|
|
732
739
|
**kwargs,
|
|
@@ -734,7 +741,7 @@ class DbtSourceFreshnessOperator(DbtBaseOperator):
|
|
|
734
741
|
super().__init__(**kwargs)
|
|
735
742
|
self.select = select
|
|
736
743
|
self.exclude = exclude
|
|
737
|
-
self.
|
|
744
|
+
self.selector = selector or selector_name
|
|
738
745
|
self.dbt_output = dbt_output
|
|
739
746
|
self.upload_dbt_project = upload_dbt_project
|
|
740
747
|
|
|
@@ -759,6 +766,7 @@ class DbtBuildOperator(DbtBaseOperator):
|
|
|
759
766
|
full_refresh: Optional[bool] = None,
|
|
760
767
|
select: Optional[list[str]] = None,
|
|
761
768
|
exclude: Optional[list[str]] = None,
|
|
769
|
+
selector: Optional[str] = None,
|
|
762
770
|
selector_name: Optional[str] = None,
|
|
763
771
|
singular: Optional[bool] = None,
|
|
764
772
|
generic: Optional[bool] = None,
|
|
@@ -770,7 +778,7 @@ class DbtBuildOperator(DbtBaseOperator):
|
|
|
770
778
|
self.full_refresh = full_refresh
|
|
771
779
|
self.select = select
|
|
772
780
|
self.exclude = exclude
|
|
773
|
-
self.
|
|
781
|
+
self.selector = selector or selector_name
|
|
774
782
|
self.singular = singular
|
|
775
783
|
self.generic = generic
|
|
776
784
|
self.show = show
|
|
@@ -181,14 +181,18 @@ class BaseConfig:
|
|
|
181
181
|
|
|
182
182
|
# legacy behaviors - https://github.com/dbt-labs/dbt-core/blob/main/docs/guides/behavior-change-flags.md
|
|
183
183
|
require_batched_execution_for_custom_microbatch_strategy: bool = False
|
|
184
|
+
require_event_names_in_deprecations: bool = False
|
|
184
185
|
require_explicit_package_overrides_for_builtin_materializations: bool = True
|
|
185
|
-
require_resource_names_without_spaces: bool =
|
|
186
|
-
source_freshness_run_project_hooks: bool =
|
|
186
|
+
require_resource_names_without_spaces: bool = True
|
|
187
|
+
source_freshness_run_project_hooks: bool = True
|
|
187
188
|
skip_nodes_if_on_run_start_fails: bool = False
|
|
188
189
|
state_modified_compare_more_unrendered_values: bool = False
|
|
189
190
|
state_modified_compare_vars: bool = False
|
|
190
191
|
require_yaml_configuration_for_mf_time_spines: bool = False
|
|
191
192
|
require_nested_cumulative_type_params: bool = False
|
|
193
|
+
validate_macro_args: bool = False
|
|
194
|
+
require_all_warnings_handled_by_warn_error: bool = False
|
|
195
|
+
require_generic_test_arguments_property: bool = False
|
|
192
196
|
|
|
193
197
|
def __post_init__(self):
|
|
194
198
|
"""Post initialization actions for a dbt configuration."""
|
|
@@ -518,11 +522,13 @@ class SelectionConfig(BaseConfig):
|
|
|
518
522
|
|
|
519
523
|
exclude: Optional[list[str]] = None
|
|
520
524
|
select: Optional[list[str]] = None
|
|
521
|
-
selector_name: Optional[list[str]] = None
|
|
522
525
|
selector: Optional[str] = None
|
|
523
526
|
state: Optional[Union[Path, str]] = None
|
|
524
527
|
defer_state: Optional[Union[Path, str]] = None
|
|
525
528
|
|
|
529
|
+
# Kept for compatibility with dbt versions < 1.5
|
|
530
|
+
selector_name: Optional[str] = None
|
|
531
|
+
|
|
526
532
|
# Kept for compatibility with dbt versions < 0.21
|
|
527
533
|
models: Optional[list[str]] = None
|
|
528
534
|
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: airflow-dbt-python
|
|
3
|
+
Version: 3.0.3
|
|
4
|
+
Summary: A collection of Airflow operators, hooks, and utilities to execute dbt commands
|
|
5
|
+
Author-email: Tomás Farías Santana <tomas@tomasfarias.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/tomasfarias/airflow-dbt-python
|
|
8
|
+
Project-URL: documentation, https://airflow-dbt-python.readthedocs.io
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: <3.13,>=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: apache-airflow>=2.8
|
|
19
|
+
Requires-Dist: contextlib-chdir==1.0.2; python_version < "3.11"
|
|
20
|
+
Requires-Dist: dbt-core<2.0.0,>=1.8.0
|
|
21
|
+
Provides-Extra: airflow-providers
|
|
22
|
+
Requires-Dist: apache-airflow-providers-amazon>=3.0.0; extra == "airflow-providers"
|
|
23
|
+
Requires-Dist: apache-airflow-providers-google>=11.0.0; extra == "airflow-providers"
|
|
24
|
+
Requires-Dist: apache-airflow-providers-ssh>=3.0.0; extra == "airflow-providers"
|
|
25
|
+
Provides-Extra: adapters
|
|
26
|
+
Requires-Dist: dbt-bigquery<2.0.0,>=1.8.0; extra == "adapters"
|
|
27
|
+
Requires-Dist: dbt-redshift<2.0.0,>=1.8.0; extra == "adapters"
|
|
28
|
+
Requires-Dist: dbt-postgres<2.0.0,>=1.8.0; extra == "adapters"
|
|
29
|
+
Requires-Dist: dbt-snowflake<2.0.0,>=1.8.0; extra == "adapters"
|
|
30
|
+
Requires-Dist: dbt-spark[all]<2.0.0,>=1.8.0; extra == "adapters"
|
|
31
|
+
Provides-Extra: bigquery
|
|
32
|
+
Requires-Dist: dbt-bigquery<2.0.0,>=1.8.0; extra == "bigquery"
|
|
33
|
+
Provides-Extra: gcs
|
|
34
|
+
Requires-Dist: apache-airflow-providers-google>=11.0.0; extra == "gcs"
|
|
35
|
+
Provides-Extra: git
|
|
36
|
+
Requires-Dist: apache-airflow-providers-ssh>=3.0.0; extra == "git"
|
|
37
|
+
Requires-Dist: dulwich>=0.21; extra == "git"
|
|
38
|
+
Provides-Extra: postgres
|
|
39
|
+
Requires-Dist: dbt-postgres<2.0.0,>=1.8.0; extra == "postgres"
|
|
40
|
+
Provides-Extra: redshift
|
|
41
|
+
Requires-Dist: dbt-redshift<2.0.0,>=1.8.0; extra == "redshift"
|
|
42
|
+
Provides-Extra: s3
|
|
43
|
+
Requires-Dist: apache-airflow-providers-amazon>=3.0.0; extra == "s3"
|
|
44
|
+
Provides-Extra: snowflake
|
|
45
|
+
Requires-Dist: dbt-snowflake<2.0.0,>=1.8.0; extra == "snowflake"
|
|
46
|
+
Provides-Extra: spark
|
|
47
|
+
Requires-Dist: dbt-spark<2.0.0,>=1.8.0; extra == "spark"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# airflow-dbt-python
|
|
51
|
+
|
|
52
|
+
[](https://pypi.org/project/airflow-dbt-python/)
|
|
53
|
+
[](https://github.com/tomasfarias/airflow-dbt-python/actions)
|
|
54
|
+
[](https://github.com/psf/black)
|
|
55
|
+
[](https://github.com/tomasfarias/airflow-dbt-python/actions)
|
|
56
|
+
[](https://airflow-dbt-python.readthedocs.io/en/latest/?badge=latest)
|
|
57
|
+
|
|
58
|
+
A collection of [Airflow](https://airflow.apache.org/) operators, hooks, and utilities to execute [`dbt`](https://pypi.org/project/dbt-core/) commands.
|
|
59
|
+
|
|
60
|
+
Read the [documentation](https://airflow-dbt-python.readthedocs.io) for examples, installation instructions, and more details.
|
|
61
|
+
|
|
62
|
+
# Installation
|
|
63
|
+
|
|
64
|
+
## Requirements
|
|
65
|
+
|
|
66
|
+
Before using *airflow-dbt-python*, ensure you meet the following requirements:
|
|
67
|
+
* A *dbt* project using [dbt-core](https://pypi.org/project/dbt-core/) version 1.8 or later.
|
|
68
|
+
* An Airflow environment using version 2.8 or later.
|
|
69
|
+
|
|
70
|
+
* If using any managed service, like AWS MWAA or GCP Cloud Composer 2/3, ensure your environment is created with a supported version of Airflow.
|
|
71
|
+
* If self-hosting, Airflow installation instructions can be found in their [official documentation](https://airflow.apache.org/docs/apache-airflow/stable/installation/index.html).
|
|
72
|
+
|
|
73
|
+
* Running Python 3.9 or later in your Airflow environment.
|
|
74
|
+
|
|
75
|
+
> **Warning**
|
|
76
|
+
>
|
|
77
|
+
> Even though we don't impose any upper limits on versions of Airflow and *dbt*, it's possible that new versions are not supported immediately after release, particularly for *dbt*. We recommend testing the latest versions before upgrading and [reporting any issues](https://github.com/tomasfarias/airflow-dbt-python/issues/new/choose).
|
|
78
|
+
|
|
79
|
+
> **Note**
|
|
80
|
+
>
|
|
81
|
+
> Older versions of Airflow and *dbt* may work with *airflow-dbt-python*, although we cannot guarantee this. Our testing pipeline runs the latest *dbt-core* with the latest Airflow release, and the latest version supported by [AWS MWAA](https://aws.amazon.com/managed-workflows-for-apache-airflow/) and [GCP Cloud Composer 2/3](https://aws.amazon.com/managed-workflows-for-apache-airflow/).
|
|
82
|
+
|
|
83
|
+
## From PyPI
|
|
84
|
+
|
|
85
|
+
*airflow-dbt-python* is available in [PyPI](https://pypi.org/project/airflow-dbt-python/) and can be installed with *pip*:
|
|
86
|
+
|
|
87
|
+
``` shell
|
|
88
|
+
pip install airflow-dbt-python
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
As a convenience, some *dbt* adapters can be installed by specifying extras. For example, if requiring the *dbt-redshift* adapter:
|
|
92
|
+
|
|
93
|
+
``` shell
|
|
94
|
+
pip install airflow-dbt-python[redshift]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## From this repo
|
|
98
|
+
|
|
99
|
+
*airflow-dbt-python* can also be built from source by cloning this GitHub repository:
|
|
100
|
+
|
|
101
|
+
``` shell
|
|
102
|
+
git clone https://github.com/tomasfarias/airflow-dbt-python.git
|
|
103
|
+
cd airflow-dbt-python
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
And installing with *uv*:
|
|
107
|
+
|
|
108
|
+
``` shell
|
|
109
|
+
uv pip install .
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## In AWS MWAA
|
|
113
|
+
|
|
114
|
+
Add *airflow-dbt-python* to your `requirements.txt` file and edit your Airflow environment to use this new `requirements.txt` file, or upload it as a plugin.
|
|
115
|
+
|
|
116
|
+
Read the [documentation](https://airflow-dbt-python.readthedocs.io/en/latest/getting_started/#installing-in-mwaa) for more a more detailed AWS MWAA installation breakdown.
|
|
117
|
+
|
|
118
|
+
## In GCP Cloud Composer
|
|
119
|
+
|
|
120
|
+
Add *airflow-dbt-python* to your PyPI packages list.
|
|
121
|
+
|
|
122
|
+
Read the [documentation](https://cloud.google.com/composer/docs/composer-2/install-python-dependencies#install-pypi) for more a more detailed GCP Cloud Composer 2 installation breakdown.
|
|
123
|
+
|
|
124
|
+
## In other managed services
|
|
125
|
+
|
|
126
|
+
*airflow-dbt-python* should be compatible with most or all Airflow managed services. Consult the documentation specific to your provider.
|
|
127
|
+
|
|
128
|
+
If you notice an issue when installing *airflow-dbt-python* in a specific managed service, please open an [issue](https://github.com/tomasfarias/airflow-dbt-python/issues/new/choose).
|
|
129
|
+
|
|
130
|
+
# Features
|
|
131
|
+
|
|
132
|
+
*airflow-dbt-python* aims to make dbt a **first-class citizen** of Airflow by supporting additional features that integrate both tools. As you would expect, *airflow-dbt-python* can run all your dbt workflows in Airflow with the same interface you are used to from the CLI, but without being a mere wrapper: *airflow-dbt-python* directly communicates with internal *dbt-core* classes, bridging the gap between them and Airflow's operator interface. Essentially, we are attempting to use *dbt* **as a library**.
|
|
133
|
+
|
|
134
|
+
As this integration was completed, several features were developed to **extend the capabilities of dbt** to leverage Airflow as much as possible. Can you think of a way *dbt* could leverage Airflow that is not currently supported? Let us know in a [GitHub issue](https://github.com/tomasfarias/airflow-dbt-python/issues/new/choose)!
|
|
135
|
+
|
|
136
|
+
## Independent task execution
|
|
137
|
+
|
|
138
|
+
Airflow executes [Tasks](https://airflow.apache.org/docs/apache-airflow/stable/concepts/tasks.html) independent of one another: even though downstream and upstream dependencies between tasks exist, the execution of an individual task happens entirely independently of any other task execution (see: [Tasks Relationships](https://airflow.apache.org/docs/apache-airflow/stable/concepts/tasks.html#relationships)).
|
|
139
|
+
|
|
140
|
+
In order to work with this constraint, *airflow-dbt-python* runs each dbt command in a **temporary and isolated directory**. Before execution, all the relevant dbt files are copied from supported backends, and after executing the command any artifacts are exported. This ensures dbt can work with any Airflow deployment, including most production deployments as they are usually running [Remote Executors](https://airflow.apache.org/docs/apache-airflow/stable/executor/index.html#executor-types) and do not guarantee any files will be shared by default between tasks, since each task may run in a completely different environment.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## Download dbt files from a remote storage
|
|
144
|
+
|
|
145
|
+
The dbt parameters `profiles_dir` and `project_dir` would normally point to a directory containing a `profiles.yml` file and a dbt project in the local environment respectively (defined by the presence of a *dbt_project.yml* file). *airflow-dbt-python* extends these parameters to also accept an URL pointing to a remote storage.
|
|
146
|
+
|
|
147
|
+
Currently, we support the following remote storages:
|
|
148
|
+
|
|
149
|
+
* [AWS S3](https://aws.amazon.com/s3/) (identified by a *s3* scheme).
|
|
150
|
+
* Remote git repositories, like those stored in GitHub (both *https* and *ssh* schemes are supported).
|
|
151
|
+
|
|
152
|
+
* If a remote URL is used for `project_dir`, then this URL must point to a location in your remote storage containing a *dbt* project to run. A *dbt* project is identified by the prescence of a *dbt_project.yml*, and contains all your [resources](https://docs.getdbt.com/docs/build/projects). All of the contents of this remote location will be downloaded and made available for the operator. The URL may also point to an archived file containing all the files of a dbt project, which will be downloaded, uncompressed, and made available for the operator.
|
|
153
|
+
* If a remote URL is used for `profiles_dir`, then this URL must point to a location in your remote storage that contains a *profiles.yml* file. The *profiles.yml* file will be downloaded and made available for the operator to use when running. The *profiles.yml* may be part of your *dbt* project, in which case this argument may be ommitted.
|
|
154
|
+
|
|
155
|
+
This feature is intended to work in line with Airflow's [description of the task concept](https://airflow.apache.org/docs/apache-airflow/stable/concepts/tasks.html#relationships):
|
|
156
|
+
|
|
157
|
+
> Tasks don’t pass information to each other by default, and run entirely independently.
|
|
158
|
+
|
|
159
|
+
We interpret this as meaning a task should be responsible of fetching all the *dbt* related files it needs in order to run independently, as already described in [Independent Task Execution](#independent-task-execution).
|
|
160
|
+
|
|
161
|
+
## Push dbt artifacts to XCom
|
|
162
|
+
|
|
163
|
+
Each dbt execution produces one or more [JSON artifacts](https://docs.getdbt.com/reference/artifacts/dbt-artifacts/) that are valuable to produce meta-metrics, build conditional workflows, for reporting purposes, and other uses. *airflow-dbt-python* can push these artifacts to [XCom](https://airflow.apache.org/docs/apache-airflow/stable/concepts/xcoms.html) as requested via the `do_xcom_push_artifacts` parameter, which takes a list of artifacts to push.
|
|
164
|
+
|
|
165
|
+
## Use Airflow connections as dbt targets (without a profiles.yml)
|
|
166
|
+
|
|
167
|
+
[Airflow connections](https://airflow.apache.org/docs/apache-airflow/stable/howto/connection.html) allow users to manage and store connection information, such as hostname, port, username, and password, for operators to use when accessing certain applications, like databases. Similarly, a *dbt* `profiles.yml` file stores connection information under each target key. *airflow-dbt-python* bridges the gap between the two and allows you to use connection information stored as an Airflow connection by specifying the connection id as the `target` parameter of any of the *dbt* operators it provides. What's more, if using an Airflow connection, the `profiles.yml` file may be entirely omitted (although keep in mind a `profiles.yml` file contains a configuration block besides target connection information).
|
|
168
|
+
|
|
169
|
+
See an example DAG [here](examples/airflow_connection_target_dag.py).
|
|
170
|
+
|
|
171
|
+
# Motivation
|
|
172
|
+
|
|
173
|
+
## Airflow running in a managed environment
|
|
174
|
+
|
|
175
|
+
Although [`dbt`](https://docs.getdbt.com/) is meant to be installed and used as a CLI, we may not have control of the environment where Airflow is running, disallowing us the option of using *dbt* as a CLI.
|
|
176
|
+
|
|
177
|
+
This is exactly what happens when using [Amazon's Managed Workflows for Apache Airflow](https://aws.amazon.com/managed-workflows-for-apache-airflow/) (aka MWAA): although a list of Python requirements can be passed, the CLI cannot be found in the worker's PATH.
|
|
178
|
+
|
|
179
|
+
There is a workaround which involves using Airflow's `BashOperator` and running Python from the command line:
|
|
180
|
+
|
|
181
|
+
``` python
|
|
182
|
+
from airflow.operators.bash import BashOperator
|
|
183
|
+
|
|
184
|
+
BASH_COMMAND = "python -c 'from dbt.main import main; main()' run"
|
|
185
|
+
operator = BashOperator(
|
|
186
|
+
task_id="dbt_run",
|
|
187
|
+
bash_command=BASH_COMMAND,
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
But it can get cumbersome when appending all potential arguments a `dbt run` command (or other subcommand) can take.
|
|
192
|
+
|
|
193
|
+
That's where *airflow-dbt-python* comes in: it abstracts the complexity of interfacing with *dbt-core* and exposes one operator for each *dbt* subcommand that can be instantiated with all the corresponding arguments that the *dbt* CLI would take.
|
|
194
|
+
|
|
195
|
+
## An alternative to *airflow-dbt* that works without the *dbt* CLI
|
|
196
|
+
|
|
197
|
+
The alternative [`airflow-dbt`](https://pypi.org/project/airflow-dbt/) package, by default, would not work if the *dbt* CLI is not in PATH, which means it would not be usable in MWAA. There is a workaround via the `dbt_bin` argument, which can be set to `"python -c 'from dbt.main import main; main()' run"`, in similar fashion as the `BashOperator` example. Yet this approach is not without its limitations:
|
|
198
|
+
* *airflow-dbt* works by wrapping the *dbt* CLI, which makes our code dependent on the environment in which it runs.
|
|
199
|
+
* *airflow-dbt* does not support the full range of arguments a command can take. For example, `DbtRunOperator` does not have an attribute for `fail_fast`.
|
|
200
|
+
* *airflow-dbt* does not offer access to *dbt* artifacts created during execution. *airflow-dbt-python* does so by pushing any artifacts to [XCom](https://airflow.apache.org/docs/apache-airflow/stable/concepts/xcoms.html).
|
|
201
|
+
|
|
202
|
+
# Usage
|
|
203
|
+
|
|
204
|
+
Currently, the following *dbt* commands are supported:
|
|
205
|
+
|
|
206
|
+
* `clean`
|
|
207
|
+
* `compile`
|
|
208
|
+
* `debug`
|
|
209
|
+
* `deps`
|
|
210
|
+
* `docs generate`
|
|
211
|
+
* `ls`
|
|
212
|
+
* `parse`
|
|
213
|
+
* `run`
|
|
214
|
+
* `run-operation`
|
|
215
|
+
* `seed`
|
|
216
|
+
* `snapshot`
|
|
217
|
+
* `source`
|
|
218
|
+
* `test`
|
|
219
|
+
|
|
220
|
+
## Examples
|
|
221
|
+
|
|
222
|
+
All example DAGs are tested against the latest Airflow version. Some changes, like modifying `import` statements or changing types, may be required for them to work in other versions.
|
|
223
|
+
|
|
224
|
+
``` python
|
|
225
|
+
import datetime as dt
|
|
226
|
+
|
|
227
|
+
import pendulum
|
|
228
|
+
from airflow import DAG
|
|
229
|
+
|
|
230
|
+
from airflow_dbt_python.operators.dbt import (
|
|
231
|
+
DbtRunOperator,
|
|
232
|
+
DbtSeedOperator,
|
|
233
|
+
DbtTestOperator,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
args = {
|
|
237
|
+
"owner": "airflow",
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
with DAG(
|
|
241
|
+
dag_id="example_dbt_operator",
|
|
242
|
+
default_args=args,
|
|
243
|
+
schedule="0 0 * * *",
|
|
244
|
+
start_date=pendulum.today("UTC").add(days=-1),
|
|
245
|
+
dagrun_timeout=dt.timedelta(minutes=60),
|
|
246
|
+
tags=["example", "example2"],
|
|
247
|
+
) as dag:
|
|
248
|
+
dbt_test = DbtTestOperator(
|
|
249
|
+
task_id="dbt_test",
|
|
250
|
+
selector="pre-run-tests",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
dbt_seed = DbtSeedOperator(
|
|
254
|
+
task_id="dbt_seed",
|
|
255
|
+
select=["/path/to/first.csv", "/path/to/second.csv"],
|
|
256
|
+
full_refresh=True,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
dbt_run = DbtRunOperator(
|
|
260
|
+
task_id="dbt_run",
|
|
261
|
+
select=["/path/to/models"],
|
|
262
|
+
full_refresh=True,
|
|
263
|
+
fail_fast=True,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
dbt_test >> dbt_seed >> dbt_run
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
More examples can be found in the [`examples/`](examples/) directory and the [documentation](https://airflow-dbt-python.readthedocs.io).
|
|
270
|
+
|
|
271
|
+
# Development
|
|
272
|
+
|
|
273
|
+
See the [development documentation](https://airflow-dbt-python.readthedocs.io/en/latest/development/) for a more in-depth dive into setting up a development environment, running the test-suite, and general commentary on working on *airflow-dbt-python*.
|
|
274
|
+
|
|
275
|
+
## Testing
|
|
276
|
+
|
|
277
|
+
Tests are run with *pytest*, can be located in `tests/`. To run them locally, you may use *uv*:
|
|
278
|
+
|
|
279
|
+
``` shell
|
|
280
|
+
uv run pytest tests/ -vv
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
# License
|
|
284
|
+
|
|
285
|
+
This project is licensed under the MIT license. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
airflow_dbt_python/__init__.py
|
|
5
|
+
airflow_dbt_python/__version__.py
|
|
6
|
+
airflow_dbt_python.egg-info/PKG-INFO
|
|
7
|
+
airflow_dbt_python.egg-info/SOURCES.txt
|
|
8
|
+
airflow_dbt_python.egg-info/dependency_links.txt
|
|
9
|
+
airflow_dbt_python.egg-info/requires.txt
|
|
10
|
+
airflow_dbt_python.egg-info/top_level.txt
|
|
11
|
+
airflow_dbt_python/hooks/__init__.py
|
|
12
|
+
airflow_dbt_python/hooks/dbt.py
|
|
13
|
+
airflow_dbt_python/hooks/target.py
|
|
14
|
+
airflow_dbt_python/hooks/fs/__init__.py
|
|
15
|
+
airflow_dbt_python/hooks/fs/gcs.py
|
|
16
|
+
airflow_dbt_python/hooks/fs/git.py
|
|
17
|
+
airflow_dbt_python/hooks/fs/local.py
|
|
18
|
+
airflow_dbt_python/hooks/fs/s3.py
|
|
19
|
+
airflow_dbt_python/operators/__init__.py
|
|
20
|
+
airflow_dbt_python/operators/dbt.py
|
|
21
|
+
airflow_dbt_python/utils/__init__.py
|
|
22
|
+
airflow_dbt_python/utils/configs.py
|
|
23
|
+
airflow_dbt_python/utils/enums.py
|
|
24
|
+
airflow_dbt_python/utils/env.py
|
|
25
|
+
airflow_dbt_python/utils/url.py
|
|
26
|
+
airflow_dbt_python/utils/version.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
apache-airflow>=2.8
|
|
2
|
+
dbt-core<2.0.0,>=1.8.0
|
|
3
|
+
|
|
4
|
+
[:python_version < "3.11"]
|
|
5
|
+
contextlib-chdir==1.0.2
|
|
6
|
+
|
|
7
|
+
[adapters]
|
|
8
|
+
dbt-bigquery<2.0.0,>=1.8.0
|
|
9
|
+
dbt-redshift<2.0.0,>=1.8.0
|
|
10
|
+
dbt-postgres<2.0.0,>=1.8.0
|
|
11
|
+
dbt-snowflake<2.0.0,>=1.8.0
|
|
12
|
+
dbt-spark[all]<2.0.0,>=1.8.0
|
|
13
|
+
|
|
14
|
+
[airflow-providers]
|
|
15
|
+
apache-airflow-providers-amazon>=3.0.0
|
|
16
|
+
apache-airflow-providers-google>=11.0.0
|
|
17
|
+
apache-airflow-providers-ssh>=3.0.0
|
|
18
|
+
|
|
19
|
+
[bigquery]
|
|
20
|
+
dbt-bigquery<2.0.0,>=1.8.0
|
|
21
|
+
|
|
22
|
+
[gcs]
|
|
23
|
+
apache-airflow-providers-google>=11.0.0
|
|
24
|
+
|
|
25
|
+
[git]
|
|
26
|
+
apache-airflow-providers-ssh>=3.0.0
|
|
27
|
+
dulwich>=0.21
|
|
28
|
+
|
|
29
|
+
[postgres]
|
|
30
|
+
dbt-postgres<2.0.0,>=1.8.0
|
|
31
|
+
|
|
32
|
+
[redshift]
|
|
33
|
+
dbt-redshift<2.0.0,>=1.8.0
|
|
34
|
+
|
|
35
|
+
[s3]
|
|
36
|
+
apache-airflow-providers-amazon>=3.0.0
|
|
37
|
+
|
|
38
|
+
[snowflake]
|
|
39
|
+
dbt-snowflake<2.0.0,>=1.8.0
|
|
40
|
+
|
|
41
|
+
[spark]
|
|
42
|
+
dbt-spark<2.0.0,>=1.8.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
airflow_dbt_python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "airflow-dbt-python"
|
|
3
|
-
version = "3.0.
|
|
3
|
+
version = "3.0.3"
|
|
4
4
|
description = "A collection of Airflow operators, hooks, and utilities to execute dbt commands"
|
|
5
5
|
authors = [{ name = "Tomás Farías Santana", email = "tomas@tomasfarias.dev" }]
|
|
6
6
|
license = "MIT"
|
|
@@ -11,8 +11,6 @@ classifiers = [
|
|
|
11
11
|
|
|
12
12
|
"Intended Audience :: Developers",
|
|
13
13
|
|
|
14
|
-
"License :: OSI Approved :: MIT License",
|
|
15
|
-
|
|
16
14
|
"Programming Language :: Python :: 3.9",
|
|
17
15
|
"Programming Language :: Python :: 3.10",
|
|
18
16
|
"Programming Language :: Python :: 3.11",
|
|
@@ -25,12 +23,9 @@ dependencies = [
|
|
|
25
23
|
"dbt-core>=1.8.0,<2.0.0",
|
|
26
24
|
]
|
|
27
25
|
|
|
28
|
-
[project.
|
|
26
|
+
[project.urls]
|
|
29
27
|
repository = "https://github.com/tomasfarias/airflow-dbt-python"
|
|
30
|
-
|
|
31
|
-
[build-system]
|
|
32
|
-
requires = ["poetry-core>=1.2.0"]
|
|
33
|
-
build-backend = "poetry.core.masonry.api"
|
|
28
|
+
documentation = "https://airflow-dbt-python.readthedocs.io"
|
|
34
29
|
|
|
35
30
|
[project.optional-dependencies]
|
|
36
31
|
airflow-providers = [
|
|
@@ -71,62 +66,35 @@ spark = [
|
|
|
71
66
|
"dbt-spark>=1.8.0,<2.0.0",
|
|
72
67
|
]
|
|
73
68
|
|
|
74
|
-
[
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
[
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
optional = true
|
|
104
|
-
|
|
105
|
-
[tool.poetry.group.docs.dependencies]
|
|
106
|
-
Sphinx = "7.4.7"
|
|
107
|
-
sphinx-rtd-theme = "3.0.2"
|
|
108
|
-
sphinx-copybutton = "0.5.2"
|
|
109
|
-
attrs = ">=22"
|
|
110
|
-
zipp = ">=3.19.1"
|
|
111
|
-
|
|
112
|
-
[tool.black]
|
|
113
|
-
line-length = 88
|
|
114
|
-
target-version = ["py39", "py310", "py311", "py312"]
|
|
115
|
-
include = '\.pyi?$'
|
|
116
|
-
exclude = '''
|
|
117
|
-
/(
|
|
118
|
-
\.eggs
|
|
119
|
-
| \.git
|
|
120
|
-
| \.hg
|
|
121
|
-
| \.mypy_cache
|
|
122
|
-
| \.tox
|
|
123
|
-
| \.venv
|
|
124
|
-
| _build
|
|
125
|
-
| buck-out
|
|
126
|
-
| build
|
|
127
|
-
| dist
|
|
128
|
-
)/
|
|
129
|
-
'''
|
|
69
|
+
[dependency-groups]
|
|
70
|
+
dev = [
|
|
71
|
+
"apache-airflow-providers-amazon>=3.0.0",
|
|
72
|
+
"apache-airflow-providers-google>=11.0.0",
|
|
73
|
+
"apache-airflow-providers-ssh>=3.0.0",
|
|
74
|
+
"boto3-stubs[s3]>=1.26.8",
|
|
75
|
+
"coverage[toml]>=7.2",
|
|
76
|
+
# docutils 0.21 causes an error with poetry (https://github.com/python-poetry/poetry/issues/9293)
|
|
77
|
+
"docutils!=0.21",
|
|
78
|
+
"dulwich>=0.21,!=0.21.6",
|
|
79
|
+
"freezegun>=1.1.0",
|
|
80
|
+
"mock-gcp==0.2.0",
|
|
81
|
+
"moto>=4.0.3",
|
|
82
|
+
"mypy>=1",
|
|
83
|
+
"pre-commit>=2.18",
|
|
84
|
+
"pytest>=7.2",
|
|
85
|
+
"pytest-mock>=3.14.0",
|
|
86
|
+
"pytest-postgresql>=5",
|
|
87
|
+
"ruff>=0.0.254",
|
|
88
|
+
"types-PyYAML>=6.0.7",
|
|
89
|
+
"types-freezegun>=1.1.6",
|
|
90
|
+
]
|
|
91
|
+
docs = [
|
|
92
|
+
"attrs>=22",
|
|
93
|
+
"Sphinx==7.4.7",
|
|
94
|
+
"sphinx-copybutton==0.5.2",
|
|
95
|
+
"sphinx-rtd-theme==3.0.2",
|
|
96
|
+
"zipp>=3.19.1",
|
|
97
|
+
]
|
|
130
98
|
|
|
131
99
|
[tool.coverage.run]
|
|
132
100
|
source = ["airflow_dbt_python"]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Hooks module provides DbtHooks and DbtFSHooks."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airflow_dbt_python-3.0.1 → airflow_dbt_python-3.0.3}/airflow_dbt_python/operators/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|